camaleon_cms 2.9.1 → 2.9.3
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/README.md +29 -9
- data/Rakefile +3 -16
- data/app/apps/plugins/attack/admin_controller.rb +9 -6
- data/app/apps/plugins/attack/attack_helper.rb +35 -25
- data/app/apps/plugins/authoring_post/authoring_post_helper.rb +23 -16
- data/app/apps/plugins/front_cache/admin_controller.rb +16 -10
- data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
- data/app/apps/plugins/front_cache/front_cache_helper.rb +57 -24
- data/app/apps/plugins/visibility_post/config/config.json +1 -0
- data/app/apps/plugins/visibility_post/config/routes_front.txt +1 -0
- data/app/apps/plugins/visibility_post/front_controller.rb +23 -0
- data/app/apps/plugins/visibility_post/visibility_post_helper.rb +141 -70
- data/app/apps/themes/camaleon_first/main_helper.rb +18 -12
- data/app/apps/themes/default/assets/css/style.css.scss +24 -0
- data/app/apps/themes/default/assets/genericons/genericons.css +3 -8
- data/app/apps/themes/default/default_helper.rb +2 -1
- data/app/apps/themes/default/views/category.html.erb +1 -1
- data/app/apps/themes/default/views/layouts/index.html.erb +3 -1
- data/app/apps/themes/default/views/post_tag.html.erb +1 -1
- data/app/apps/themes/default/views/post_type.html.erb +1 -1
- data/app/apps/themes/default/views/search.html.erb +1 -1
- data/app/apps/themes/new/assets/css/styles.css.scss +24 -0
- data/app/apps/themes/new/custom_helper.rb +5 -9
- data/app/apps/themes/new/views/category.html.erb +1 -1
- data/app/apps/themes/new/views/layouts/_header.html.erb +3 -1
- data/app/apps/themes/new/views/post_tag.html.erb +1 -1
- data/app/apps/themes/new/views/post_type.html.erb +1 -1
- data/app/apps/themes/new/views/search.html.erb +1 -1
- data/app/assets/javascripts/camaleon_cms/admin/_data.js +16 -0
- data/app/assets/javascripts/camaleon_cms/admin/_post.js +5 -2
- data/app/assets/javascripts/camaleon_cms/admin/nav_menu.js +14 -3
- data/app/assets/javascripts/camaleon_cms/admin/uploader/_media_manager.js +5 -0
- data/app/controllers/camaleon_cms/admin/appearances/nav_menus_controller.rb +29 -10
- data/app/controllers/camaleon_cms/admin/appearances/themes_controller.rb +8 -5
- data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +9 -2
- data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +4 -3
- data/app/controllers/camaleon_cms/admin/appearances/widgets/sidebar_controller.rb +2 -2
- data/app/controllers/camaleon_cms/admin/categories_controller.rb +27 -8
- data/app/controllers/camaleon_cms/admin/comments_controller.rb +0 -1
- data/app/controllers/camaleon_cms/admin/installers_controller.rb +32 -1
- data/app/controllers/camaleon_cms/admin/media_controller.rb +84 -19
- data/app/controllers/camaleon_cms/admin/plugins_controller.rb +1 -3
- data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +15 -6
- data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +40 -11
- data/app/controllers/camaleon_cms/admin/posts_controller.rb +58 -22
- data/app/controllers/camaleon_cms/admin/sessions_controller.rb +238 -45
- data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +103 -18
- data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +17 -5
- data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +33 -7
- data/app/controllers/camaleon_cms/admin/settings_controller.rb +11 -6
- data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +9 -7
- data/app/controllers/camaleon_cms/admin/users_controller.rb +89 -18
- data/app/controllers/camaleon_cms/admin_controller.rb +144 -26
- data/app/controllers/camaleon_cms/apps/plugins_admin_controller.rb +5 -0
- data/app/controllers/camaleon_cms/camaleon_controller.rb +37 -92
- data/app/controllers/camaleon_cms/frontend_controller.rb +140 -70
- data/app/controllers/concerns/camaleon_cms/admin/custom_fields_concern.rb +44 -0
- data/app/controllers/concerns/camaleon_cms/frontend_concern.rb +46 -17
- data/app/controllers/concerns/camaleon_cms/frontend_visited_state_concern.rb +72 -0
- data/app/controllers/concerns/camaleon_cms/hook_lifecycle_concern.rb +104 -0
- data/app/controllers/concerns/camaleon_cms/request_context_concern.rb +113 -0
- data/app/controllers/concerns/camaleon_cms/runtime_admin_menu_concern.rb +237 -0
- data/app/controllers/concerns/camaleon_cms/runtime_captcha_image_concern.rb +13 -0
- data/app/controllers/concerns/camaleon_cms/runtime_html_content_concern.rb +143 -0
- data/app/controllers/concerns/camaleon_cms/runtime_shortcode_theme_concern.rb +246 -0
- data/app/controllers/concerns/camaleon_cms/runtime_state_concern.rb +13 -0
- data/app/controllers/concerns/camaleon_cms/runtime_uploader_concern.rb +54 -0
- data/app/controllers/concerns/camaleon_cms/session_captcha_runtime_concern.rb +19 -0
- data/app/controllers/concerns/camaleon_cms/session_runtime_concern.rb +40 -0
- data/app/decorators/camaleon_cms/application_decorator.rb +11 -8
- data/app/decorators/camaleon_cms/category_decorator.rb +3 -2
- data/app/decorators/camaleon_cms/post_comment_decorator.rb +13 -5
- data/app/decorators/camaleon_cms/post_decorator.rb +67 -51
- data/app/decorators/camaleon_cms/post_type_decorator.rb +1 -1
- data/app/decorators/camaleon_cms/site_decorator.rb +29 -23
- data/app/decorators/camaleon_cms/term_taxonomy_decorator.rb +21 -10
- data/app/decorators/camaleon_cms/theme_decorator.rb +3 -3
- data/app/decorators/camaleon_cms/user_decorator.rb +26 -5
- data/app/helpers/camaleon_cms/admin/application_helper.rb +21 -24
- data/app/helpers/camaleon_cms/admin/breadcrumb_helper.rb +1 -1
- data/app/helpers/camaleon_cms/admin/custom_fields_helper.rb +24 -23
- data/app/helpers/camaleon_cms/admin/menus_helper.rb +249 -139
- data/app/helpers/camaleon_cms/admin/post_type_helper.rb +51 -31
- data/app/helpers/camaleon_cms/camaleon_helper.rb +31 -39
- data/app/helpers/camaleon_cms/captcha_helper.rb +73 -53
- data/app/helpers/camaleon_cms/comment_helper.rb +87 -41
- data/app/helpers/camaleon_cms/content_helper.rb +13 -6
- data/app/helpers/camaleon_cms/email_helper.rb +7 -3
- data/app/helpers/camaleon_cms/frontend/content_select_helper.rb +34 -20
- data/app/helpers/camaleon_cms/frontend/nav_menu_helper.rb +144 -67
- data/app/helpers/camaleon_cms/frontend/seo_helper.rb +39 -28
- data/app/helpers/camaleon_cms/frontend/site_helper.rb +32 -12
- data/app/helpers/camaleon_cms/hooks_helper.rb +14 -4
- data/app/helpers/camaleon_cms/html_helper.rb +73 -38
- data/app/helpers/camaleon_cms/plugins_helper.rb +23 -24
- data/app/helpers/camaleon_cms/session_helper.rb +195 -39
- data/app/helpers/camaleon_cms/short_code_helper.rb +181 -85
- data/app/helpers/camaleon_cms/site_helper.rb +43 -9
- data/app/helpers/camaleon_cms/theme_helper.rb +35 -19
- data/app/helpers/camaleon_cms/uploader_helper.rb +19 -422
- data/app/helpers/camaleon_cms/user_roles_helper.rb +5 -4
- data/app/mailers/camaleon_cms/html_mailer.rb +4 -4
- data/app/models/camaleon_cms/ability.rb +65 -105
- data/app/models/camaleon_cms/category.rb +7 -5
- data/app/models/camaleon_cms/custom_field.rb +29 -6
- data/app/models/camaleon_cms/custom_field_group.rb +75 -21
- data/app/models/camaleon_cms/custom_fields_relationship.rb +133 -2
- data/app/models/camaleon_cms/media.rb +8 -2
- data/app/models/camaleon_cms/meta.rb +2 -2
- data/app/models/camaleon_cms/nav_menu.rb +6 -2
- data/app/models/camaleon_cms/nav_menu_item.rb +18 -8
- data/app/models/camaleon_cms/plugin.rb +4 -3
- data/app/models/camaleon_cms/post.rb +113 -27
- data/app/models/camaleon_cms/post_comment.rb +10 -6
- data/app/models/camaleon_cms/post_default.rb +42 -12
- data/app/models/camaleon_cms/post_relationship.rb +6 -4
- data/app/models/camaleon_cms/post_tag.rb +4 -3
- data/app/models/camaleon_cms/post_type.rb +22 -12
- data/app/models/camaleon_cms/site.rb +40 -28
- data/app/models/camaleon_cms/term_relationship.rb +10 -7
- data/app/models/camaleon_cms/term_taxonomy.rb +51 -5
- data/app/models/camaleon_cms/theme.rb +3 -3
- data/app/models/camaleon_cms/user.rb +17 -5
- data/app/models/camaleon_cms/user_role.rb +77 -4
- data/app/models/camaleon_cms/widget/assigned.rb +11 -6
- data/app/models/camaleon_cms/widget/main.rb +13 -7
- data/app/models/camaleon_cms/widget/sidebar.rb +3 -6
- data/app/models/camaleon_record.rb +123 -5
- data/app/models/concerns/camaleon_cms/categories_tags_for_posts.rb +20 -38
- data/app/models/concerns/camaleon_cms/common_relationships.rb +18 -5
- data/app/models/concerns/camaleon_cms/custom_fields_read.rb +115 -58
- data/app/models/concerns/camaleon_cms/metas.rb +32 -7
- data/app/models/concerns/camaleon_cms/normalize_attrs.rb +2 -8
- data/app/models/concerns/camaleon_cms/site_default_settings.rb +120 -16
- data/app/models/concerns/camaleon_cms/user_methods.rb +44 -19
- data/app/models/current_request.rb +23 -0
- data/app/uploaders/camaleon_cms_aws_uploader.rb +39 -11
- data/app/uploaders/camaleon_cms_local_uploader.rb +76 -19
- data/app/uploaders/camaleon_cms_uploader.rb +23 -10
- data/app/validators/camaleon_cms/post_uniq_validator.rb +21 -8
- data/app/validators/camaleon_cms/uniq_validator.rb +1 -1
- data/app/validators/camaleon_cms/user_url_validator.rb +215 -36
- data/app/views/camaleon_cms/admin/appearances/nav_menus/_left_menu_items.html.erb +2 -2
- data/app/views/camaleon_cms/admin/appearances/widgets/main/form.html.erb +1 -1
- data/app/views/camaleon_cms/admin/categories/index.html.erb +3 -3
- data/app/views/camaleon_cms/admin/comments/index.html.erb +2 -2
- data/app/views/camaleon_cms/admin/comments/list.html.erb +1 -1
- data/app/views/camaleon_cms/admin/installers/form.html.erb +5 -0
- data/app/views/camaleon_cms/admin/installers/welcome.html.erb +30 -6
- data/app/views/camaleon_cms/admin/plugins/_plugins_list.html.erb +2 -2
- data/app/views/camaleon_cms/admin/post_tags/index.html.erb +3 -3
- data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +2 -2
- data/app/views/camaleon_cms/admin/posts/form.html.erb +3 -2
- data/app/views/camaleon_cms/admin/posts/index.html.erb +6 -6
- data/app/views/camaleon_cms/admin/search.html.erb +1 -1
- data/app/views/camaleon_cms/admin/sessions/back_to_parent.html.erb +39 -0
- data/app/views/camaleon_cms/admin/sessions/logout_confirm.html.erb +27 -0
- data/app/views/camaleon_cms/admin/settings/_email_settings.html.erb +4 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/_get_items.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/_render.html.erb +42 -6
- data/app/views/camaleon_cms/admin/settings/custom_fields/_render_category_simple.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/custom_fields/form.html.erb +5 -4
- data/app/views/camaleon_cms/admin/settings/custom_fields/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/post_types/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/shortcodes.html.erb +4 -4
- data/app/views/camaleon_cms/admin/settings/site.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/sites/form.html.erb +1 -1
- data/app/views/camaleon_cms/admin/settings/sites/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/user_roles/form.html.erb +102 -7
- data/app/views/camaleon_cms/admin/user_roles/index.html.erb +1 -1
- data/app/views/camaleon_cms/admin/users/form.html.erb +8 -4
- data/app/views/camaleon_cms/admin/users/index.html.erb +2 -2
- data/app/views/camaleon_cms/default_theme/partials/_render_custom_field.html.erb +8 -2
- data/app/views/camaleon_cms/default_theme/sitemap.html.erb +1 -1
- data/app/views/camaleon_cms/default_theme/sitemap.xml.builder +1 -1
- data/app/views/layouts/camaleon_cms/admin/_flash_messages.html.erb +2 -2
- data/app/views/layouts/camaleon_cms/admin/_header.html.erb +1 -1
- data/config/initializers/action_view.rb +42 -5
- data/config/initializers/active_record_extension.rb +13 -27
- data/config/initializers/assets.rb +52 -23
- data/config/initializers/custom_initializers.rb +3 -3
- data/config/locales/camaleon_cms/admin/ar.yml +7 -2
- data/config/locales/camaleon_cms/admin/de.yml +7 -2
- data/config/locales/camaleon_cms/admin/en.yml +39 -2
- data/config/locales/camaleon_cms/admin/es.yml +7 -2
- data/config/locales/camaleon_cms/admin/fr.yml +7 -2
- data/config/locales/camaleon_cms/admin/it.yml +7 -2
- data/config/locales/camaleon_cms/admin/nl.yml +8 -2
- data/config/locales/camaleon_cms/admin/pt-BR.yml +7 -2
- data/config/locales/camaleon_cms/admin/pt.yml +7 -2
- data/config/locales/camaleon_cms/admin/ru.yml +7 -2
- data/config/locales/camaleon_cms/admin/uk.yml +7 -2
- data/config/locales/camaleon_cms/admin/zh-CH.yml +7 -2
- data/config/routes/admin.rb +33 -15
- data/config/routes/frontend.rb +21 -15
- data/db/migrate/20150611161134_post_table_into_utf8.rb +14 -14
- data/db/migrate/20150926095310_rename_column_posts.rb +3 -3
- data/db/migrate/20151212095328_add_confirm_token_to_users.rb +3 -3
- data/db/migrate/20160504155652_add_feature_to_posts.rb +1 -1
- data/db/migrate/20160504155653_move_first_name_of_users.rb +2 -2
- data/db/migrate/20160609121449_add_group_to_custom_field_values.rb +1 -1
- data/db/migrate/20161215202255_drop_user_relationship_table.rb +1 -1
- data/db/migrate/20180124132318_create_media.rb +1 -1
- data/db/migrate/20180704211100_adjust_field_length.rb +1 -1
- data/lib/camaleon_cms/assets_precompile.rb +86 -0
- data/lib/camaleon_cms/captcha_image_generation.rb +65 -0
- data/lib/camaleon_cms/content_security.rb +158 -0
- data/lib/camaleon_cms/engine.rb +47 -9
- data/lib/camaleon_cms/media_security_headers.rb +27 -0
- data/lib/camaleon_cms/setup_token.rb +69 -0
- data/lib/camaleon_cms/svg_content_checker.rb +123 -0
- data/lib/camaleon_cms/task_reporter.rb +14 -0
- data/lib/camaleon_cms/unsafe_markup.rb +207 -0
- data/lib/camaleon_cms/uploader_content_security.rb +248 -0
- data/lib/camaleon_cms/uploader_image_processing.rb +166 -0
- data/lib/camaleon_cms/uploader_path_security.rb +146 -0
- data/lib/camaleon_cms/uploader_pipeline.rb +350 -0
- data/lib/camaleon_cms/uploader_support.rb +86 -0
- data/lib/camaleon_cms/version.rb +1 -1
- data/lib/camaleon_cms.rb +11 -0
- data/lib/ext/hash.rb +28 -2
- data/lib/ext/string.rb +13 -8
- data/lib/ext/translator.rb +10 -2
- data/lib/generators/camaleon_cms/gem_plugin_template/app/controllers/plugins/my_plugin/admin_controller.rb +5 -1
- data/lib/generators/camaleon_cms/install_generator.rb +7 -4
- data/lib/generators/camaleon_cms/install_template/plugin_routes.rb +5 -12
- data/lib/generators/camaleon_cms/theme_generator.rb +5 -10
- data/lib/generators/camaleon_cms/theme_template/views/layouts/index.html.erb +3 -1
- data/lib/plugin_routes.rb +412 -368
- data/lib/tasks/camaleon_cms/camaleon_tasks.rake +2 -1
- data/lib/tasks/cross_site_field_groups.rake +74 -0
- data/lib/tasks/custom_fields_roles.rake +58 -0
- data/lib/tasks/orphaned_comments.rake +42 -0
- data/lib/tasks/private_upload_acls.rake +31 -0
- data/lib/tasks/site_custom_field_groups.rake +36 -0
- data/lib/tasks/unsafe_stored_content.rake +42 -0
- data/lib/tasks/unsafe_stored_uploads.rake +70 -0
- data/lib/tasks/user_field_groups.rake +27 -0
- metadata +185 -7
|
@@ -3,6 +3,8 @@ module CamaleonCms
|
|
|
3
3
|
module Settings
|
|
4
4
|
class CustomFieldsController < CamaleonCms::Admin::SettingsController
|
|
5
5
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.custom_fields'), :cama_admin_settings_custom_fields_path
|
|
6
|
+
|
|
7
|
+
before_action :validate_role, only: %i[create update destroy]
|
|
6
8
|
before_action :set_custom_field_group, only: %i[show edit update destroy]
|
|
7
9
|
before_action :set_post_data, only: %i[create update]
|
|
8
10
|
|
|
@@ -59,22 +61,38 @@ module CamaleonCms
|
|
|
59
61
|
# reorder custom fields group
|
|
60
62
|
def reorder
|
|
61
63
|
params[:values].to_a.each_with_index do |value, index|
|
|
62
|
-
current_site.custom_field_groups.find(value)
|
|
64
|
+
current_site.custom_field_groups.find(value)
|
|
65
|
+
.update_column(:field_order, index) # rubocop:disable Rails/SkipsModelValidations
|
|
63
66
|
end
|
|
64
67
|
json = { size: params[:values].size }
|
|
65
68
|
render json: json
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
def list
|
|
69
|
-
p = params.permit(:post_type, :post_id
|
|
70
|
-
|
|
71
|
-
if p[:post_id].present?
|
|
72
|
-
post
|
|
73
|
-
|
|
72
|
+
p = params.permit(:post_type, :post_id)
|
|
73
|
+
cat_ids = current_site.full_categories.where(id: params[:categories]).pluck(:id)
|
|
74
|
+
if p[:post_id].present? && (post = current_site.the_post(p[:post_id].to_i)).present?
|
|
75
|
+
# The action carries no before_action, so authorize against the resolved post: a caller who
|
|
76
|
+
# cannot update it may neither read its custom-field values (GET) nor rewrite its categories
|
|
77
|
+
# (POST). This scopes an otherwise any-signed-in-user endpoint to the post's own editors.
|
|
78
|
+
authorize! :update, post
|
|
79
|
+
# The category write is state-changing, so it runs only on a POST — the sole verb on this
|
|
80
|
+
# route that protect_from_forgery verifies, because Rails exempts HEAD from CSRF checks
|
|
81
|
+
# just like GET (an `unless request.get?` guard would let a CSRF-exempt HEAD through). A GET
|
|
82
|
+
# or HEAD renders the current fields without mutating the post. Otherwise a bare
|
|
83
|
+
# GET .../custom_fields/list?post_id=N (CSRF through a top-level navigation, which carries
|
|
84
|
+
# the SameSite=Lax auth cookie) wipes the post's categories, because an omitted `categories`
|
|
85
|
+
# param resolves to [] and update_categories then deletes them all (audit finding M6).
|
|
86
|
+
post.update_categories(cat_ids) if request.post?
|
|
87
|
+
args = {}
|
|
74
88
|
else
|
|
89
|
+
# The render-only branch builds a new post of the requested type; gate it on the ability to
|
|
90
|
+
# create posts of that type so the field-group structure is not disclosed to other roles.
|
|
91
|
+
post_type = current_site.the_post_type(p[:post_type].to_i)
|
|
92
|
+
authorize! :create_post, post_type if post_type
|
|
75
93
|
post = CamaleonCms::Post.new
|
|
76
|
-
post.taxonomy_id =
|
|
77
|
-
args
|
|
94
|
+
post.taxonomy_id = post_type&.id
|
|
95
|
+
args = { cat_ids: cat_ids }
|
|
78
96
|
end
|
|
79
97
|
render partial: 'camaleon_cms/admin/settings/custom_fields/render',
|
|
80
98
|
locals: { record: post, field_groups: post.get_field_groups(args),
|
|
@@ -84,9 +102,58 @@ module CamaleonCms
|
|
|
84
102
|
private
|
|
85
103
|
|
|
86
104
|
def set_post_data
|
|
87
|
-
@post_data = params.require(:custom_field_group).permit
|
|
88
|
-
@post_data[:object_class], @post_data[:objectid] = @post_data.delete(:assign_group).split(',')
|
|
105
|
+
@post_data = params.require(:custom_field_group).permit(:name, :description, :assign_group, :caption)
|
|
106
|
+
@post_data[:object_class], @post_data[:objectid] = @post_data.delete(:assign_group).to_s.split(',')
|
|
107
|
+
# A Site placement has exactly one legal target, and the form only ever offers
|
|
108
|
+
# "Site,<current_site.id>". Pinning it keeps a crafted objectid from producing a group that
|
|
109
|
+
# Site#get_field_groups can never return, which would leave it stranded and unreachable.
|
|
110
|
+
@post_data[:objectid] = current_site.id if @post_data[:object_class] == 'Site'
|
|
89
111
|
@caption = @post_data.delete(:caption)
|
|
112
|
+
reject_foreign_placement unless placement_owned_by_current_site?
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# A group's placement (object_class + objectid) decides which record's admin page renders it,
|
|
116
|
+
# and those reads are not scoped by the owning site. Without this check a user who can manage
|
|
117
|
+
# custom fields on one site could stamp a group with another site's theme, menu or post type
|
|
118
|
+
# id and have it render there -- invisible in that site's own field group list, which is
|
|
119
|
+
# scoped by parent_id, so its administrators could neither find nor delete it.
|
|
120
|
+
def placement_owned_by_current_site?
|
|
121
|
+
object_class = @post_data[:object_class].to_s
|
|
122
|
+
objectid = @post_data[:objectid].to_s
|
|
123
|
+
return false if object_class.blank? || objectid !~ /\A\d+\z/
|
|
124
|
+
|
|
125
|
+
owned_placement_ids(object_class).include?(objectid.to_i)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Every "other" option the form emits is "<Class>,<current_site.id>" -- Site, the configured
|
|
129
|
+
# user model, and any model contributed through the custom_field_custom_models hook. Keying
|
|
130
|
+
# the default arm on the id rather than on an allow-list of class names admits those hook
|
|
131
|
+
# models without this controller having to know their names.
|
|
132
|
+
def owned_placement_ids(object_class)
|
|
133
|
+
case object_class
|
|
134
|
+
when 'PostType', 'PostType_Post', 'PostType_Category', 'PostType_PostTag'
|
|
135
|
+
current_site.post_types.pluck(:id)
|
|
136
|
+
when 'Theme' then current_site.themes.pluck(:id)
|
|
137
|
+
when 'NavMenu' then current_site.nav_menus.pluck(:id)
|
|
138
|
+
when 'Plugin' then current_site.plugins.pluck(:id)
|
|
139
|
+
when 'Post' then current_site.posts.pluck(:id)
|
|
140
|
+
when 'Category', 'Category_Post' then current_site.full_categories.pluck(:id)
|
|
141
|
+
else [current_site.id]
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def reject_foreign_placement
|
|
146
|
+
@field_group ||= current_site.custom_field_groups.new(@post_data.except(:object_class, :objectid))
|
|
147
|
+
@field_group.errors.add(
|
|
148
|
+
:base,
|
|
149
|
+
t('camaleon_cms.admin.custom_field.message.invalid_placement',
|
|
150
|
+
default: 'Select where to display this group. The selected target does not belong to this site.')
|
|
151
|
+
)
|
|
152
|
+
render 'form'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def validate_role
|
|
156
|
+
authorize! :manage, :custom_fields
|
|
90
157
|
end
|
|
91
158
|
|
|
92
159
|
def set_custom_field_group
|
|
@@ -96,21 +163,39 @@ module CamaleonCms
|
|
|
96
163
|
redirect_to cama_admin_path
|
|
97
164
|
end
|
|
98
165
|
|
|
166
|
+
def permitted_fields
|
|
167
|
+
return {} if params[:fields].blank?
|
|
168
|
+
|
|
169
|
+
params.require(:fields).permit(params[:fields].keys.index_with do
|
|
170
|
+
%i[id name slug description field_order]
|
|
171
|
+
end).to_h
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def permitted_field_options
|
|
175
|
+
return {} if params[:field_options].blank?
|
|
176
|
+
|
|
177
|
+
params.require(:field_options).permit(params[:field_options].keys.index_with do
|
|
178
|
+
[:field_key, :multiple, :required, :translate, :default_value, :dimension, :width, :height, :class,
|
|
179
|
+
:placeholder, { default_values: [], multiple_options: %i[title value default] }]
|
|
180
|
+
end).to_h
|
|
181
|
+
end
|
|
182
|
+
|
|
99
183
|
# return boolean: true if all fields were saved successfully
|
|
100
184
|
def _save_fields(group)
|
|
101
|
-
errors_saved, _all_fields = group.add_fields(
|
|
102
|
-
params[:field_options] ? params.require(:field_options).permit! : {})
|
|
185
|
+
errors_saved, _all_fields = group.add_fields(permitted_fields, permitted_field_options)
|
|
103
186
|
group.set_option('caption', @caption)
|
|
104
187
|
if errors_saved.present?
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
188
|
+
errors_found_msg = t('camaleon_cms.errors_found_msg', default: 'Several errors were found, please check.')
|
|
189
|
+
errors_saved_all_text = errors_saved.map do |field|
|
|
190
|
+
"#{field.name}: " + field.errors.messages.map do |k, v|
|
|
191
|
+
"#{k.to_s.titleize}: #{v.join('|')}"
|
|
192
|
+
end.join(', ').to_s
|
|
193
|
+
end.join('<br>')
|
|
194
|
+
flash[:error] = "<b>#{errors_found_msg}</b><br>#{errors_saved_all_text}"
|
|
195
|
+
false
|
|
110
196
|
else
|
|
111
197
|
flash[:notice] = t('camaleon_cms.admin.custom_field.message.custom_updated')
|
|
112
198
|
end
|
|
113
|
-
true
|
|
114
199
|
end
|
|
115
200
|
end
|
|
116
201
|
end
|
|
@@ -4,6 +4,7 @@ module CamaleonCms
|
|
|
4
4
|
class PostTypesController < CamaleonCms::Admin::SettingsController
|
|
5
5
|
before_action :set_post_type, only: %i[show edit update destroy]
|
|
6
6
|
before_action :set_data_term, only: %i[create update]
|
|
7
|
+
|
|
7
8
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.content_groups'), :cama_admin_settings_post_types_path
|
|
8
9
|
|
|
9
10
|
def index
|
|
@@ -20,7 +21,7 @@ module CamaleonCms
|
|
|
20
21
|
|
|
21
22
|
def update
|
|
22
23
|
if @post_type.update(@data_term)
|
|
23
|
-
@post_type.set_field_values(
|
|
24
|
+
@post_type.set_field_values(cama_permitted_field_options('PostType')) if params[:field_options].present?
|
|
24
25
|
hooks_run('updated_post_type', { post_type: @post_type })
|
|
25
26
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.updated')
|
|
26
27
|
redirect_to action: :index
|
|
@@ -32,7 +33,7 @@ module CamaleonCms
|
|
|
32
33
|
def create
|
|
33
34
|
@post_type = current_site.post_types.new(@data_term)
|
|
34
35
|
if @post_type.save
|
|
35
|
-
@post_type.set_field_values(
|
|
36
|
+
@post_type.set_field_values(cama_permitted_field_options('PostType')) if params[:field_options].present?
|
|
36
37
|
hooks_run('created_post_type', { post_type: @post_type })
|
|
37
38
|
flash[:notice] = t('camaleon_cms.admin.post_type.message.created')
|
|
38
39
|
redirect_to action: :index
|
|
@@ -49,13 +50,24 @@ module CamaleonCms
|
|
|
49
50
|
private
|
|
50
51
|
|
|
51
52
|
def set_data_term
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
# parent_id is the post type's site_id (alias_attribute). It is set from the site association
|
|
54
|
+
# on create and must never be reassigned, so it is not accepted from the request; otherwise a
|
|
55
|
+
# settings manager could re-home the post type onto another site (audit finding H8).
|
|
56
|
+
data_term = params.require(:post_type).permit(:name, :slug, :description)
|
|
57
|
+
data_term[:data_options] = params[:meta].present? ? post_type_meta_params : {}
|
|
54
58
|
@data_term = data_term
|
|
55
59
|
end
|
|
56
60
|
|
|
61
|
+
def post_type_meta_params
|
|
62
|
+
params.require(:meta).permit(:icon, :has_layout, :default_layout, :has_template, :default_template,
|
|
63
|
+
:has_category, :has_single_category, :has_tags, :has_content, :has_summary,
|
|
64
|
+
:has_comments, :has_featured, :has_seo, :has_parent_structure, :has_picture,
|
|
65
|
+
:posts_image_dimension, :posts_thumb_versions, :posts_thumb_size,
|
|
66
|
+
:is_required_picture, :contents_route_format, :default_thumb)
|
|
67
|
+
end
|
|
68
|
+
|
|
57
69
|
def set_post_type
|
|
58
|
-
@post_type = current_site.post_types.
|
|
70
|
+
@post_type = current_site.post_types.find_by(id: params[:id])
|
|
59
71
|
rescue StandardError
|
|
60
72
|
flash[:error] = t('camaleon_cms.admin.post_type.message.error')
|
|
61
73
|
redirect_to cama_admin_path
|
|
@@ -23,11 +23,11 @@ module CamaleonCms
|
|
|
23
23
|
|
|
24
24
|
def update
|
|
25
25
|
tmp = @site.slug
|
|
26
|
-
if @site.update(
|
|
26
|
+
if @site.update(site_params)
|
|
27
27
|
save_metas(@site)
|
|
28
28
|
flash[:notice] = t('camaleon_cms.admin.sites.message.updated')
|
|
29
29
|
if @site.id == Cama::Site.main_site.id && tmp != @site.slug
|
|
30
|
-
redirect_to
|
|
30
|
+
redirect_to(cama_admin_path)
|
|
31
31
|
else
|
|
32
32
|
redirect_to action: :index
|
|
33
33
|
end
|
|
@@ -43,12 +43,11 @@ module CamaleonCms
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
def create
|
|
46
|
-
|
|
47
|
-
@site = CamaleonCms::Site.new(site_data)
|
|
46
|
+
@site = CamaleonCms::Site.new(site_params)
|
|
48
47
|
if @site.save
|
|
49
48
|
save_metas(@site)
|
|
50
49
|
site_after_install(@site, @site.get_theme_slug)
|
|
51
|
-
flash[:notice] =
|
|
50
|
+
flash[:notice] = site_created_notice(@site)
|
|
52
51
|
redirect_to action: :index
|
|
53
52
|
else
|
|
54
53
|
new
|
|
@@ -63,7 +62,7 @@ module CamaleonCms
|
|
|
63
62
|
private
|
|
64
63
|
|
|
65
64
|
def save_metas(site)
|
|
66
|
-
return
|
|
65
|
+
return if params[:metas].blank?
|
|
67
66
|
|
|
68
67
|
params[:metas].each do |meta, val|
|
|
69
68
|
site.set_meta(meta, val)
|
|
@@ -71,7 +70,7 @@ module CamaleonCms
|
|
|
71
70
|
end
|
|
72
71
|
|
|
73
72
|
def set_site
|
|
74
|
-
@site = CamaleonCms::Site.
|
|
73
|
+
@site = CamaleonCms::Site.find_by(id: params[:id]).decorate
|
|
75
74
|
rescue StandardError
|
|
76
75
|
flash[:error] = t('camaleon_cms.admin.sites.message.error')
|
|
77
76
|
redirect_to cama_admin_path
|
|
@@ -84,6 +83,33 @@ module CamaleonCms
|
|
|
84
83
|
flash[:error] = t('camaleon_cms.admin.sites.message.unauthorized')
|
|
85
84
|
redirect_to cama_admin_path
|
|
86
85
|
end
|
|
86
|
+
|
|
87
|
+
def site_params
|
|
88
|
+
params.require(:site).permit(:name, :slug, :description)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# When creating an additional site provisions a fresh administrator (only when users are not
|
|
92
|
+
# shared across sites), surface its generated password once to the authenticated creator, with
|
|
93
|
+
# a copy control. Otherwise a plain confirmation. See harden-installer-default-admin.
|
|
94
|
+
def site_created_notice(site)
|
|
95
|
+
password = site.generated_admin_password
|
|
96
|
+
return t('camaleon_cms.admin.sites.message.created') if password.blank?
|
|
97
|
+
|
|
98
|
+
# rubocop:disable Style/FormatStringToken -- Rails i18n interpolation requires %{...} syntax
|
|
99
|
+
message = t(
|
|
100
|
+
'camaleon_cms.admin.sites.message.created_with_admin',
|
|
101
|
+
password: password,
|
|
102
|
+
default: 'Site created. New administrator password: %{password}. Change it after signing in.'
|
|
103
|
+
)
|
|
104
|
+
# rubocop:enable Style/FormatStringToken
|
|
105
|
+
copy = helpers.tag.button(
|
|
106
|
+
t('camaleon_cms.admin.installer.copy', default: 'Copy'),
|
|
107
|
+
type: 'button',
|
|
108
|
+
class: 'btn btn-xs btn-default',
|
|
109
|
+
onclick: "navigator.clipboard&&navigator.clipboard.writeText('#{password}')"
|
|
110
|
+
)
|
|
111
|
+
helpers.safe_join([message, ' ', copy])
|
|
112
|
+
end
|
|
87
113
|
end
|
|
88
114
|
end
|
|
89
115
|
end
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Admin
|
|
3
3
|
class SettingsController < CamaleonCms::AdminController
|
|
4
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
5
|
+
|
|
4
6
|
before_action :validate_role, except: %i[theme save_theme]
|
|
5
7
|
before_action :validate_role_theme, only: %i[theme save_theme]
|
|
8
|
+
|
|
6
9
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.settings')
|
|
7
10
|
|
|
8
11
|
def index
|
|
@@ -19,10 +22,10 @@ module CamaleonCms
|
|
|
19
22
|
def site_saved
|
|
20
23
|
@site = current_site
|
|
21
24
|
cache_slug = @site.slug
|
|
22
|
-
if @site.update(params.require(:site).permit
|
|
25
|
+
if @site.update(params.require(:site).permit(:name, :slug, :description))
|
|
23
26
|
@site.set_options(params[:options]) if params[:options].present?
|
|
24
27
|
@site.set_metas(params[:metas]) if params[:metas].present?
|
|
25
|
-
@site.set_field_values(
|
|
28
|
+
@site.set_field_values(cama_permitted_field_options('Site'))
|
|
26
29
|
flash[:notice] = t('camaleon_cms.admin.settings.message.site_updated')
|
|
27
30
|
args = { action: :site }
|
|
28
31
|
args[:host], args[:port] = @site.get_domain.to_s.split(':') if cache_slug != @site.slug
|
|
@@ -59,11 +62,13 @@ module CamaleonCms
|
|
|
59
62
|
end
|
|
60
63
|
|
|
61
64
|
def save_theme
|
|
62
|
-
current_theme.set_field_values(
|
|
65
|
+
current_theme.set_field_values(cama_permitted_field_options('Theme', param_key: :theme_fields))
|
|
63
66
|
current_theme.set_options(params[:theme_option]) if params[:theme_option].present?
|
|
64
67
|
current_theme.set_metas(params[:theme_meta]) if params[:theme_meta].present?
|
|
65
|
-
current_theme.set_field_values(
|
|
66
|
-
|
|
68
|
+
current_theme.set_field_values(cama_permitted_field_options('Theme'))
|
|
69
|
+
|
|
70
|
+
# permit saving extra/custom values by this hook
|
|
71
|
+
hook_run(current_theme.settings, 'on_theme_settings', current_theme)
|
|
67
72
|
flash[:notice] = t('camaleon_cms.admin.message.updated_success', default: 'Theme updated successfully')
|
|
68
73
|
redirect_to action: :theme
|
|
69
74
|
end
|
|
@@ -74,7 +79,7 @@ module CamaleonCms
|
|
|
74
79
|
CamaleonCms::HtmlMailer.sender(params[:email], 'Test', data).deliver_now
|
|
75
80
|
head :ok
|
|
76
81
|
rescue StandardError => e
|
|
77
|
-
render
|
|
82
|
+
render plain: e.message, status: :bad_gateway
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
private
|
|
@@ -20,13 +20,15 @@ module CamaleonCms
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def create
|
|
23
|
-
user_role_data = params.require(:user_role).permit
|
|
23
|
+
user_role_data = params.require(:user_role).permit(:name, :slug, :description)
|
|
24
24
|
@user_role = current_site.user_roles.new(user_role_data)
|
|
25
25
|
if @user_role.save
|
|
26
|
-
@user_role.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
if @user_role.permissions_editable?
|
|
27
|
+
@user_role.set_meta("_post_type_#{current_site.id}",
|
|
28
|
+
defined?(params[:rol_values][:post_type]) ? params[:rol_values][:post_type] : {})
|
|
29
|
+
@user_role.set_meta("_manager_#{current_site.id}",
|
|
30
|
+
defined?(params[:rol_values][:post_type]) ? params[:rol_values][:manager] : {})
|
|
31
|
+
end
|
|
30
32
|
flash[:notice] = t('camaleon_cms.admin.users.message.rol_created')
|
|
31
33
|
redirect_to action: :edit, id: @user_role.id
|
|
32
34
|
else
|
|
@@ -40,8 +42,8 @@ module CamaleonCms
|
|
|
40
42
|
end
|
|
41
43
|
|
|
42
44
|
def update
|
|
43
|
-
if @user_role.update(params.require(:user_role).permit
|
|
44
|
-
if @user_role.
|
|
45
|
+
if @user_role.update(params.require(:user_role).permit(:name, :slug, :description))
|
|
46
|
+
if @user_role.permissions_editable?
|
|
45
47
|
@user_role.set_meta("_post_type_#{current_site.id}",
|
|
46
48
|
defined?(params[:rol_values][:post_type]) ? params[:rol_values][:post_type] : {})
|
|
47
49
|
@user_role.set_meta("_manager_#{current_site.id}",
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
module CamaleonCms
|
|
2
2
|
module Admin
|
|
3
3
|
class UsersController < CamaleonCms::AdminController
|
|
4
|
+
include CamaleonCms::Admin::CustomFieldsConcern
|
|
5
|
+
|
|
6
|
+
# Member actions resolve a single target user, so a caller acting on their own record is
|
|
7
|
+
# legitimately exempt from :manage, :users. The collection actions (index/new/create) resolve
|
|
8
|
+
# no such target, so a self-referential ?user_id= must not exempt them from the capability check.
|
|
9
|
+
SELF_TARGET_ACTIONS = %w[show edit update destroy impersonate updated_ajax].freeze
|
|
10
|
+
|
|
4
11
|
before_action :validate_role, except: %i[profile profile_edit]
|
|
12
|
+
|
|
5
13
|
add_breadcrumb I18n.t('camaleon_cms.admin.sidebar.users'), :cama_admin_users_url
|
|
14
|
+
|
|
6
15
|
before_action :set_user, only: %i[show edit update destroy impersonate]
|
|
7
16
|
|
|
8
17
|
def index
|
|
@@ -12,7 +21,16 @@ module CamaleonCms
|
|
|
12
21
|
|
|
13
22
|
def profile
|
|
14
23
|
add_breadcrumb I18n.t('camaleon_cms.admin.users.profile')
|
|
15
|
-
|
|
24
|
+
user_id = params[:user_id]
|
|
25
|
+
# Authorize from the parameter before loading, so a denied caller cannot tell
|
|
26
|
+
# whether the requested user exists from the shape of the response.
|
|
27
|
+
authorize! :manage, :users if user_id.present? && user_id.to_i != cama_current_user.id
|
|
28
|
+
@user = user_id.present? ? current_site.the_user(user_id.to_i)&.object : cama_current_user.object
|
|
29
|
+
if @user.blank?
|
|
30
|
+
flash[:error] = t('camaleon_cms.admin.users.message.error')
|
|
31
|
+
return redirect_to(cama_admin_path)
|
|
32
|
+
end
|
|
33
|
+
|
|
16
34
|
edit
|
|
17
35
|
end
|
|
18
36
|
|
|
@@ -31,8 +49,8 @@ module CamaleonCms
|
|
|
31
49
|
r = { user: @user }
|
|
32
50
|
hooks_run('user_update', r)
|
|
33
51
|
if @user.update(user_params)
|
|
34
|
-
@user.set_metas(
|
|
35
|
-
@user.set_field_values(params[:field_options]
|
|
52
|
+
@user.set_metas(user_meta_params) if params[:meta].present?
|
|
53
|
+
@user.set_field_values(cama_permitted_field_options(user_field_scope)) if params[:field_options].present?
|
|
36
54
|
r = { user: @user, message: t('camaleon_cms.admin.users.message.updated'), params: params }
|
|
37
55
|
hooks_run('user_after_edited', r)
|
|
38
56
|
flash[:notice] = r[:message]
|
|
@@ -50,17 +68,33 @@ module CamaleonCms
|
|
|
50
68
|
|
|
51
69
|
# update some ajax requests from profile or user form
|
|
52
70
|
def updated_ajax
|
|
53
|
-
@user = current_site.users.find(
|
|
71
|
+
@user = current_site.users.find(user_id_param)
|
|
72
|
+
# Only an admin may reset an admin's password; a `:manage, :users` holder who could would sign in
|
|
73
|
+
# as that admin (H10). Self-service and non-admin targets are unaffected.
|
|
74
|
+
unless cama_current_user.may_edit_credentials?(@user)
|
|
75
|
+
return render plain: t('camaleon_cms.admin.users.message.error'), status: :forbidden
|
|
76
|
+
end
|
|
77
|
+
|
|
54
78
|
update_session = current_user_is?(@user)
|
|
55
79
|
attrs = params.require(:password).permit(%i[password password_confirmation])
|
|
56
80
|
@user.update(password: attrs.require(:password), password_confirmation: attrs.require(:password_confirmation))
|
|
57
81
|
|
|
58
|
-
return render
|
|
82
|
+
return render plain: @user.errors.full_messages.join(', '), status: :unprocessable_entity if @user.errors.any?
|
|
83
|
+
|
|
84
|
+
# A newly provisioned admin (see harden-installer-default-admin) owes a password change; clearing
|
|
85
|
+
# the marker on a real change lets them past the enforce_password_change gate.
|
|
86
|
+
@user.delete_meta('must_change_password') if @user.saved_change_to_password_digest?
|
|
59
87
|
|
|
60
88
|
# keep user logged in when changing their own password
|
|
61
89
|
update_auth_token_in_cookie @user.auth_token if update_session && @user.saved_change_to_password_digest?
|
|
90
|
+
rescue ActiveRecord::RecordNotFound
|
|
91
|
+
# The other failure paths of this action answer with a status and a short text body, so an
|
|
92
|
+
# unresolvable target does too rather than falling through to the framework's HTML error
|
|
93
|
+
# page. Catch this class only, never StandardError, so a genuine lookup failure still
|
|
94
|
+
# surfaces instead of being reported as a missing user.
|
|
95
|
+
render plain: t('camaleon_cms.admin.users.message.error'), status: :not_found
|
|
62
96
|
rescue ActionController::ParameterMissing => e
|
|
63
|
-
render
|
|
97
|
+
render plain: "ERROR: #{e.class.name}, #{e.message}", status: :bad_request
|
|
64
98
|
end
|
|
65
99
|
|
|
66
100
|
def update_auth_token_in_cookie(token)
|
|
@@ -68,7 +102,10 @@ module CamaleonCms
|
|
|
68
102
|
|
|
69
103
|
current_token = cookie_split_auth_token
|
|
70
104
|
updated_token = [token, *current_token[1..]]
|
|
71
|
-
|
|
105
|
+
# Route through the shared hardened-cookie options (audit M3/M4): a bare assignment here
|
|
106
|
+
# re-issued the auth cookie without HttpOnly/Secure/domain/expiry when a user changed their
|
|
107
|
+
# own password, undoing the M3 hardening for that session.
|
|
108
|
+
cookies[:auth_token] = cama_auth_cookie_options(updated_token.join('&'))
|
|
72
109
|
end
|
|
73
110
|
|
|
74
111
|
def current_user_is?(user)
|
|
@@ -93,13 +130,12 @@ module CamaleonCms
|
|
|
93
130
|
end
|
|
94
131
|
|
|
95
132
|
def create
|
|
96
|
-
|
|
97
|
-
@user = current_site.users.new(user_data)
|
|
133
|
+
@user = current_site.users.new(user_params)
|
|
98
134
|
r = { user: @user }
|
|
99
135
|
hooks_run('user_create', r)
|
|
100
136
|
if @user.save
|
|
101
|
-
@user.set_metas(
|
|
102
|
-
@user.set_field_values(params[:field_options]
|
|
137
|
+
@user.set_metas(user_meta_params) if params[:meta].present?
|
|
138
|
+
@user.set_field_values(cama_permitted_field_options(user_field_scope)) if params[:field_options].present?
|
|
103
139
|
r = { user: @user }
|
|
104
140
|
hooks_run('user_created', r)
|
|
105
141
|
flash[:notice] = t('camaleon_cms.admin.users.message.created')
|
|
@@ -130,20 +166,55 @@ module CamaleonCms
|
|
|
130
166
|
private
|
|
131
167
|
|
|
132
168
|
def validate_role
|
|
133
|
-
|
|
169
|
+
return if self_target_own_record?
|
|
170
|
+
|
|
171
|
+
authorize! :manage, :users
|
|
134
172
|
end
|
|
135
173
|
|
|
174
|
+
# The self-exemption applies only to member actions that resolve a single target user; a
|
|
175
|
+
# collection action (index/new/create) has no such target, so a self-referential ?user_id=
|
|
176
|
+
# never exempts it (audit finding H7).
|
|
177
|
+
def self_target_own_record?
|
|
178
|
+
return false unless SELF_TARGET_ACTIONS.include?(action_name)
|
|
179
|
+
|
|
180
|
+
user_id = user_id_param
|
|
181
|
+
user_id.present? && cama_current_user.id.to_s == user_id.to_s
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Only a scalar user_id participates in target resolution (?user_id[]= would
|
|
185
|
+
# crash the record lookup and flip authorization); otherwise the route id wins
|
|
136
186
|
def user_id_param
|
|
137
|
-
|
|
187
|
+
user_id = params[:user_id]
|
|
188
|
+
return params[:id] unless user_id.is_a?(String)
|
|
189
|
+
|
|
190
|
+
user_id
|
|
138
191
|
end
|
|
139
192
|
|
|
140
193
|
def user_params
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
194
|
+
fields = %i[username email first_name last_name password password_confirmation]
|
|
195
|
+
# Only an admin may change an admin's password or recovery identifiers (email/username); drop them
|
|
196
|
+
# for anyone else editing an admin, the same fail-safe the role permit below uses (H10).
|
|
197
|
+
unless cama_current_user.may_edit_credentials?(@user)
|
|
198
|
+
fields -= %i[username email password password_confirmation]
|
|
146
199
|
end
|
|
200
|
+
p = params.require(:user).permit(*fields)
|
|
201
|
+
# role is a scalar slug; coerce anything else (e.g. a nested user[role][x] param) to nil so it is
|
|
202
|
+
# never mass-assigned — assigning an unpermitted nested Parameters would raise and 500 the request.
|
|
203
|
+
requested_role = params[:user][:role]
|
|
204
|
+
requested_role = nil unless requested_role.is_a?(String)
|
|
205
|
+
p[:role] = requested_role if requested_role.present? && cama_current_user.role_grantor?(@user, requested_role)
|
|
206
|
+
p
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def user_meta_params
|
|
210
|
+
params.require(:meta).permit(:avatar, :slogan)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Allowed field slugs must be keyed on the demodulized placement name the settings form
|
|
214
|
+
# emits and get_user_field_groups queries; keyed on a hardcoded 'User' the lookup finds no
|
|
215
|
+
# groups for a host user model that demodulizes to another name, discarding every value.
|
|
216
|
+
def user_field_scope
|
|
217
|
+
PluginRoutes.get_user_class_name.demodulize
|
|
147
218
|
end
|
|
148
219
|
|
|
149
220
|
def set_user
|