camaleon_cms 2.9.2 → 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 +17 -4
- 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 +15 -10
- data/app/apps/plugins/front_cache/config/initializer.rb +1 -1
- data/app/apps/plugins/front_cache/front_cache_helper.rb +33 -9
- 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/layouts/index.html.erb +3 -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/layouts/_header.html.erb +3 -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 +8 -6
- data/app/controllers/camaleon_cms/admin/appearances/themes_controller.rb +8 -5
- data/app/controllers/camaleon_cms/admin/appearances/widgets/assign_controller.rb +7 -2
- data/app/controllers/camaleon_cms/admin/appearances/widgets/main_controller.rb +2 -1
- data/app/controllers/camaleon_cms/admin/categories_controller.rb +19 -4
- 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 +66 -14
- data/app/controllers/camaleon_cms/admin/plugins_controller.rb +1 -3
- data/app/controllers/camaleon_cms/admin/post_tags_controller.rb +10 -4
- data/app/controllers/camaleon_cms/admin/posts/drafts_controller.rb +40 -11
- data/app/controllers/camaleon_cms/admin/posts_controller.rb +55 -22
- data/app/controllers/camaleon_cms/admin/sessions_controller.rb +237 -44
- data/app/controllers/camaleon_cms/admin/settings/custom_fields_controller.rb +76 -13
- data/app/controllers/camaleon_cms/admin/settings/post_types_controller.rb +5 -2
- data/app/controllers/camaleon_cms/admin/settings/sites_controller.rb +26 -3
- data/app/controllers/camaleon_cms/admin/settings_controller.rb +5 -3
- data/app/controllers/camaleon_cms/admin/user_roles_controller.rb +7 -5
- data/app/controllers/camaleon_cms/admin/users_controller.rb +78 -11
- data/app/controllers/camaleon_cms/admin_controller.rb +136 -26
- 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 +20 -5
- 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 +66 -50
- 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 +5 -8
- 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 +34 -17
- 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 +19 -7
- 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 +143 -66
- 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 +58 -37
- data/app/helpers/camaleon_cms/plugins_helper.rb +23 -24
- data/app/helpers/camaleon_cms/session_helper.rb +188 -44
- data/app/helpers/camaleon_cms/short_code_helper.rb +181 -85
- data/app/helpers/camaleon_cms/site_helper.rb +34 -13
- data/app/helpers/camaleon_cms/theme_helper.rb +35 -19
- data/app/helpers/camaleon_cms/uploader_helper.rb +19 -473
- 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 +15 -7
- data/app/models/camaleon_cms/category.rb +6 -6
- data/app/models/camaleon_cms/custom_field.rb +15 -6
- data/app/models/camaleon_cms/custom_field_group.rb +39 -22
- 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 +5 -3
- data/app/models/camaleon_cms/nav_menu_item.rb +16 -8
- data/app/models/camaleon_cms/plugin.rb +3 -4
- 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 -5
- data/app/models/camaleon_cms/post_type.rb +21 -13
- data/app/models/camaleon_cms/site.rb +39 -29
- data/app/models/camaleon_cms/term_relationship.rb +10 -7
- data/app/models/camaleon_cms/term_taxonomy.rb +52 -4
- data/app/models/camaleon_cms/theme.rb +2 -4
- data/app/models/camaleon_cms/user.rb +17 -5
- data/app/models/camaleon_cms/user_role.rb +66 -6
- data/app/models/camaleon_cms/widget/assigned.rb +11 -6
- data/app/models/camaleon_cms/widget/main.rb +12 -8
- data/app/models/camaleon_cms/widget/sidebar.rb +3 -8
- data/app/models/camaleon_record.rb +89 -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 +111 -54
- data/app/models/concerns/camaleon_cms/metas.rb +31 -6
- 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 +8 -1
- data/app/uploaders/camaleon_cms_aws_uploader.rb +31 -10
- 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 +5 -4
- 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/categories/index.html.erb +2 -2
- 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 +2 -2
- data/app/views/camaleon_cms/admin/posts/_sidebar.html.erb +1 -1
- data/app/views/camaleon_cms/admin/posts/form.html.erb +3 -2
- data/app/views/camaleon_cms/admin/posts/index.html.erb +5 -5
- 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 +19 -4
- 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/form.html.erb +4 -3
- 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/user_roles/form.html.erb +24 -3
- data/app/views/camaleon_cms/admin/users/form.html.erb +8 -4
- data/app/views/camaleon_cms/admin/users/index.html.erb +1 -1
- 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/_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 +1 -1
- data/config/locales/camaleon_cms/admin/ar.yml +1 -0
- data/config/locales/camaleon_cms/admin/de.yml +1 -0
- data/config/locales/camaleon_cms/admin/en.yml +33 -0
- data/config/locales/camaleon_cms/admin/es.yml +1 -0
- data/config/locales/camaleon_cms/admin/fr.yml +1 -0
- data/config/locales/camaleon_cms/admin/it.yml +1 -0
- data/config/locales/camaleon_cms/admin/nl.yml +1 -0
- data/config/locales/camaleon_cms/admin/pt-BR.yml +1 -0
- data/config/locales/camaleon_cms/admin/pt.yml +1 -0
- data/config/locales/camaleon_cms/admin/ru.yml +1 -0
- data/config/locales/camaleon_cms/admin/uk.yml +1 -0
- data/config/locales/camaleon_cms/admin/zh-CH.yml +1 -0
- data/config/routes/admin.rb +33 -15
- data/config/routes/frontend.rb +21 -15
- 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 +10 -5
- 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 +15 -13
- 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 +123 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0997f6bbf586f6f6c5618c97814c0a291fa98bcb54adbd7736a0449f60edc33
|
|
4
|
+
data.tar.gz: e73b0849f7b21ffc7bd7510d71c328706b022cfefaf738194836b9e332e4f062
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74c4843293dd8fbfa585cc784834866be63a7e330b7c9d21ecbbeb9a9e6753168d5c2502705db0fdecee9f89b2ff3b3d102e699c41384fb398f6332be98464cf
|
|
7
|
+
data.tar.gz: f152511e4e5022003ea2003a275cd677912cce73ac55bb9dbc8528094b6ddcf529b445627ba3750c4bbcebf5412b7107049c43daa0f3e3f5390539d2ba6c4eff
|
data/README.md
CHANGED
|
@@ -81,14 +81,13 @@ https://camaleon.website/store/plugins
|
|
|
81
81
|
* Image/iFrame Lazy Loader - https://github.com/brian-kephart/camaleon_lazy_loader
|
|
82
82
|
* Import / Export - https://github.com/owen2345/camaleon_export_import
|
|
83
83
|
* Lightbox - https://github.com/owen2345/CamaImageLightbox
|
|
84
|
-
* Autocomplete - https://github.com/gaelfokou/cama_autocomplete
|
|
85
84
|
* Permissions for External Menus - https://github.com/owen2345/cama_external_menu
|
|
86
85
|
* TinyMCE Template Integration - https://github.com/owen2345/Camaleon-Tinymce-Templates
|
|
87
86
|
* Download Manager - https://github.com/max2320/camaleon-download
|
|
88
87
|
* OAuth - https://github.com/owen2345/camaleon_oauth
|
|
89
|
-
* Visual Editor - Paid Plugin ($) - https://camaleon.website/store/plugins/camaleon_editor
|
|
90
|
-
* Spree Commerce Integration - Paid Plugin ($) - https://camaleon.website/store/plugins/camaleon-spree
|
|
91
|
-
* Admin AJAX - Paid Plugin ($) - https://camaleon.website/store/plugins/admin_ajax
|
|
88
|
+
* Visual Editor - Paid Plugin ($) - https://camaleon.website/store/plugins/camaleon_editor (Github: https://github.com/owen2345/camaleon_editor)
|
|
89
|
+
* Spree Commerce Integration - Paid Plugin ($) - https://camaleon.website/store/plugins/camaleon-spree (Github: https://github.com/owen2345/camaleon-spree)
|
|
90
|
+
* Admin AJAX - Paid Plugin ($) - https://camaleon.website/store/plugins/admin_ajax (Github: https://github.com/owen2345/admin-ajax)
|
|
92
91
|
* **See here for a complete Gemfile**: https://github.com/owen2345/camaleon-cms/blob/master/docs/example_gemfile.rb
|
|
93
92
|
|
|
94
93
|
## Camaleon CMS has many useful frontend Themes such as:
|
|
@@ -190,6 +189,13 @@ RAILS_ENV=test bundle exec rake app:db:test:prepare
|
|
|
190
189
|
|
|
191
190
|
See [docs/security/permissions.md](docs/security/permissions.md) for details on manager permissions, custom fields, and security considerations.
|
|
192
191
|
|
|
192
|
+
## Hooks
|
|
193
|
+
|
|
194
|
+
Plugins and themes extend Camaleon by handling named hooks. See [docs/hooks.md](docs/hooks.md) for the
|
|
195
|
+
mechanism (registration via `config.json`, the mutable `args` payload) and a code-derived inventory of the
|
|
196
|
+
hooks core fires, including the session/auth hooks (`user_before_login`, `user_before_register`,
|
|
197
|
+
`after_login`, …).
|
|
198
|
+
|
|
193
199
|
## Contributing
|
|
194
200
|
* Fork it.
|
|
195
201
|
* Create a branch (git checkout -b my_feature_branch)
|
|
@@ -197,6 +203,13 @@ See [docs/security/permissions.md](docs/security/permissions.md) for details on
|
|
|
197
203
|
* Push to the branch (git push origin my_feature_branch)
|
|
198
204
|
* Create a pull request from your branch into master (Please be sure to provide enough detail for us to understand what this change is doing)
|
|
199
205
|
|
|
206
|
+
## Releasing
|
|
207
|
+
|
|
208
|
+
Releases are cut by the manually-started **Release** GitHub Actions workflow, which builds the gem,
|
|
209
|
+
publishes it to RubyGems, tags the commit and creates the GitHub release. See
|
|
210
|
+
[docs/releasing.md](docs/releasing.md) for the full procedure, what the workflow checks before it
|
|
211
|
+
publishes, and the fully manual fallback.
|
|
212
|
+
|
|
200
213
|
## Version History
|
|
201
214
|
|
|
202
215
|
https://camaleon.website/version-history.html
|
data/Rakefile
CHANGED
|
@@ -4,26 +4,13 @@ rescue LoadError
|
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
require 'rdoc/task'
|
|
8
|
-
|
|
9
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
|
10
|
-
rdoc.rdoc_dir = 'rdoc'
|
|
11
|
-
rdoc.title = 'CamaleonCms'
|
|
12
|
-
rdoc.options << '--line-numbers'
|
|
13
|
-
rdoc.rdoc_files.include('README.rdoc')
|
|
14
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
15
|
-
end
|
|
16
|
-
|
|
17
7
|
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
|
18
8
|
load 'rails/tasks/engine.rake'
|
|
19
9
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Dir[File.join(File.dirname(__FILE__), 'tasks/**/*.rake')].each { |f| load f }
|
|
10
|
+
# Deliberately no Bundler::GemHelper.install_tasks: its `rake release` would tag `v<version>` and
|
|
11
|
+
# push a gem that none of the release checks had seen. Releases go through the Release workflow —
|
|
12
|
+
# see docs/releasing.md, which also covers building and publishing by hand.
|
|
25
13
|
|
|
26
|
-
require 'rspec/core'
|
|
27
14
|
require 'rspec/core/rake_task'
|
|
28
15
|
|
|
29
16
|
desc 'Run all specs in spec directory (excluding plugin specs)'
|
|
@@ -6,12 +6,15 @@ module Plugins
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def save_settings
|
|
9
|
-
current_site.set_meta(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
current_site.set_meta(
|
|
10
|
+
'attack_config', {
|
|
11
|
+
get: { sec: params[:attack][:get_sec], max: params[:attack][:get_max] },
|
|
12
|
+
post: { sec: params[:attack][:post_sec] || 20, max: params[:attack][:post_max] },
|
|
13
|
+
msg: params[:attack][:msg],
|
|
14
|
+
ban: params[:attack][:ban],
|
|
15
|
+
cleared: Time.zone.now.iso8601
|
|
16
|
+
}
|
|
17
|
+
)
|
|
15
18
|
flash[:notice] = t('plugin.attack.messages.settings_saved').to_s
|
|
16
19
|
redirect_to action: :settings
|
|
17
20
|
end
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
module Plugins
|
|
2
2
|
module Attack
|
|
3
3
|
module AttackHelper
|
|
4
|
-
#
|
|
4
|
+
# Here all actions on the plugin are destroyed
|
|
5
5
|
# plugin: plugin model
|
|
6
6
|
def attack_on_destroy(_plugin)
|
|
7
7
|
current_site.attack.destroy_all
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
#
|
|
11
|
-
# you can run
|
|
10
|
+
# Here all actions on going to active
|
|
11
|
+
# you can run SQL commands like this:
|
|
12
12
|
# results = ActiveRecord::Base.connection.execute(query);
|
|
13
13
|
# plugin: plugin model
|
|
14
14
|
def attack_on_active(_plugin)
|
|
@@ -16,15 +16,13 @@ module Plugins
|
|
|
16
16
|
post: { sec: 20, max: 5 },
|
|
17
17
|
msg: I18n.t('plugin.attack.form.request_limit_exceeded').to_s,
|
|
18
18
|
ban: 5,
|
|
19
|
-
cleared: Time.now })
|
|
19
|
+
cleared: Time.zone.now.iso8601 })
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
t.datetime 'created_at'
|
|
27
|
-
end
|
|
21
|
+
ActiveRecord::Base.connection.create_table :plugins_attacks, if_not_exists: true do |t|
|
|
22
|
+
t.string :path, index: true
|
|
23
|
+
t.string :browser_key, index: true
|
|
24
|
+
t.belongs_to :site, index: true
|
|
25
|
+
t.datetime 'created_at'
|
|
28
26
|
end
|
|
29
27
|
CamaleonCms::Site.class_eval do
|
|
30
28
|
has_many :attack, class_name: 'Plugins::Attack::Models::Attack'
|
|
@@ -38,9 +36,9 @@ module Plugins
|
|
|
38
36
|
end
|
|
39
37
|
|
|
40
38
|
def attack_app_before_load
|
|
41
|
-
cache_ban = Rails.cache.read(
|
|
39
|
+
cache_ban = Rails.cache.read(attack_ban_cache_key)
|
|
42
40
|
if cache_ban.present? # render banned message if it was banned
|
|
43
|
-
render html: cache_ban.
|
|
41
|
+
render html: cache_ban.to_s, layout: false
|
|
44
42
|
return
|
|
45
43
|
end
|
|
46
44
|
|
|
@@ -50,44 +48,56 @@ module Plugins
|
|
|
50
48
|
|
|
51
49
|
private
|
|
52
50
|
|
|
51
|
+
# Throttle and ban on the client IP, not the session id (audit finding H2). A cookieless /
|
|
52
|
+
# cookie-rotating request presented a fresh session id every time, so the per-key counter never
|
|
53
|
+
# accumulated: the throttle never tripped and its per-request DB insert ran unbounded. Keying on
|
|
54
|
+
# the IP makes the count accumulate, trip the limit, and stop inserting.
|
|
55
|
+
def attack_client_key
|
|
56
|
+
request.remote_ip.to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def attack_ban_cache_key
|
|
60
|
+
"plugins_attack_ban:#{attack_client_key}"
|
|
61
|
+
end
|
|
62
|
+
|
|
53
63
|
def attack_check_request
|
|
54
64
|
return unless current_site
|
|
55
65
|
|
|
56
66
|
config = current_site.get_meta('attack_config')
|
|
57
|
-
|
|
58
|
-
return
|
|
67
|
+
query = current_site.attack.where(browser_key: attack_client_key, path: attack_request_key)
|
|
68
|
+
return if config.blank?
|
|
59
69
|
|
|
60
70
|
# clear past requests
|
|
61
71
|
if begin
|
|
62
|
-
Time.parse(config[:cleared])
|
|
72
|
+
Time.zone.parse(config[:cleared])
|
|
63
73
|
rescue StandardError
|
|
64
74
|
2.hours.ago
|
|
65
75
|
end < 1.hour.ago
|
|
66
76
|
current_site.attack.where('plugins_attacks.created_at < ?', 1.hour.ago).delete_all
|
|
67
|
-
config[:cleared] = Time.now.
|
|
77
|
+
config[:cleared] = Time.zone.now.iso8601 # Use ISO8601 for reliable parsing
|
|
68
78
|
current_site.set_meta('attack_config', config)
|
|
69
79
|
end
|
|
70
80
|
|
|
71
81
|
# post request
|
|
72
82
|
if request.post? || request.patch?
|
|
73
|
-
r =
|
|
83
|
+
r = query.where(created_at: config[:post][:sec].to_i.seconds.ago..Time.zone.now)
|
|
74
84
|
if r.count > config[:post][:max].to_i
|
|
75
|
-
Rails.cache.write(
|
|
76
|
-
#
|
|
77
|
-
render html: config[:msg].
|
|
85
|
+
Rails.cache.write(attack_ban_cache_key, config[:msg], expires_in: config[:ban].to_i.minutes)
|
|
86
|
+
# Email administrator with request info (ip, browser, if logged, then send user info
|
|
87
|
+
render html: config[:msg].to_s
|
|
78
88
|
return
|
|
79
89
|
end
|
|
80
90
|
|
|
81
91
|
# get request
|
|
82
92
|
else
|
|
83
|
-
r =
|
|
93
|
+
r = query.where(created_at: config[:get][:sec].to_i.seconds.ago..Time.zone.now)
|
|
84
94
|
if r.count > config[:get][:max].to_i
|
|
85
|
-
Rails.cache.write(
|
|
86
|
-
render html: config[:msg].
|
|
95
|
+
Rails.cache.write(attack_ban_cache_key, config[:msg], expires_in: config[:ban].to_i.minutes)
|
|
96
|
+
render html: config[:msg].to_s
|
|
87
97
|
return
|
|
88
98
|
end
|
|
89
99
|
end
|
|
90
|
-
|
|
100
|
+
query.create
|
|
91
101
|
end
|
|
92
102
|
|
|
93
103
|
def attack_request_key(method = nil)
|
|
@@ -24,27 +24,34 @@ module Plugins
|
|
|
24
24
|
private
|
|
25
25
|
|
|
26
26
|
def plugin_authoring_form_html(post)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
disabled = !(can?(:edit_other, post.post_type) && (can?(:edit_publish, post.post_type) || !post.published?))
|
|
28
|
+
|
|
29
|
+
label = tag.label(t('camaleon_cms.admin.table.author'), class: 'control-label')
|
|
30
|
+
select = content_tag(
|
|
31
|
+
:select,
|
|
32
|
+
safe_join(plugin_authoring_authors_list(post)),
|
|
33
|
+
id: 'post_user_id', name: 'post[user_id]',
|
|
34
|
+
class: 'form-control select valid',
|
|
35
|
+
disabled: disabled,
|
|
36
|
+
'aria-invalid' => 'false'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
tag.div(safe_join([label, select]), class: 'form-group')
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def plugin_authoring_authors_list(post)
|
|
42
43
|
author_id = post.new_record? ? cama_current_user.id : post.author.id
|
|
43
|
-
|
|
44
|
-
current_site.users.where('role <> ?', 'client').order(:username).
|
|
45
|
-
|
|
44
|
+
|
|
45
|
+
current_site.users.where('role <> ?', 'client').order(:username).map do |user|
|
|
46
|
+
selected = user.id == author_id
|
|
47
|
+
titleized_username = user.username.titleize
|
|
48
|
+
display = if user.fullname == titleized_username
|
|
49
|
+
titleized_username
|
|
50
|
+
else
|
|
51
|
+
"#{titleized_username} (#{user.fullname})"
|
|
52
|
+
end
|
|
53
|
+
tag.option(display, value: user.id, selected: selected)
|
|
46
54
|
end
|
|
47
|
-
ret
|
|
48
55
|
end
|
|
49
56
|
end
|
|
50
57
|
end
|
|
@@ -5,19 +5,24 @@ module Plugins
|
|
|
5
5
|
|
|
6
6
|
def settings
|
|
7
7
|
@caches = current_site.get_meta('front_cache_elements', { paths: [] })
|
|
8
|
-
@caches[:paths] << ''
|
|
8
|
+
@caches[:paths] << '' if @caches[:paths].blank?
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def save_settings
|
|
12
|
-
current_site.set_meta(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
current_site.set_meta(
|
|
13
|
+
'front_cache_elements',
|
|
14
|
+
{
|
|
15
|
+
paths: (params[:cache][:paths] || []).compact_blank || [],
|
|
16
|
+
posts: params[:cache][:posts] || [],
|
|
17
|
+
post_types: params[:cache][:post_type] || [],
|
|
18
|
+
skip_posts: params[:cache][:skip_posts] || [],
|
|
19
|
+
cache_login: params[:cache][:cache_login],
|
|
20
|
+
home: params[:cache][:home],
|
|
21
|
+
preserve_cache_on_restart: params[:cache][:preserve_cache_on_restart],
|
|
22
|
+
invalidate_only: params[:cache][:invalidate_only],
|
|
23
|
+
cache_counter: current_site.get_meta('front_cache_elements')[:cache_counter] || 0
|
|
24
|
+
}
|
|
25
|
+
)
|
|
21
26
|
flash[:notice] = t('plugin.front_cache.message.settings_saved').to_s
|
|
22
27
|
redirect_to action: :settings
|
|
23
28
|
end
|
|
@@ -13,25 +13,35 @@ module Plugins
|
|
|
13
13
|
|
|
14
14
|
cache_key = front_cache_plugin_cache_key
|
|
15
15
|
@caches = current_site.get_meta('front_cache_elements')
|
|
16
|
-
if
|
|
16
|
+
if flash.keys.blank? && front_cache_exist?(cache_key) # recover cache item
|
|
17
17
|
Rails.logger.info "Camaleon CMS - readed cache: #{front_cache_plugin_get_path(cache_key)}"
|
|
18
18
|
response.headers['PLUGIN_FRONT_CACHE'] = 'TRUE'
|
|
19
19
|
args = { data: front_cache_get(cache_key).gsub('{{form_authenticity_token}}', form_authenticity_token) }
|
|
20
20
|
hooks_run('front_cache_reading_cache', args)
|
|
21
|
+
# rubocop:disable Rails/OutputSafety -- This replays a trusted cached page body that was already rendered by Rails.
|
|
21
22
|
render html: args[:data].html_safe
|
|
23
|
+
# rubocop:enable Rails/OutputSafety
|
|
22
24
|
return
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
@_plugin_do_cache = false
|
|
26
|
-
|
|
28
|
+
# cache paths and home page
|
|
29
|
+
if @caches[:paths].include?(request.original_url) || @caches[:paths].include?(request.path_info) ||
|
|
30
|
+
front_cache_plugin_match_path_patterns?(request.original_url, request.path_info) ||
|
|
31
|
+
(params[:action] == 'index' && params[:controller] == 'camaleon_cms/frontend' && @caches[:home].present?)
|
|
27
32
|
@_plugin_do_cache = true
|
|
28
|
-
elsif params[:action] == 'post' && params[:controller] == 'camaleon_cms/frontend' &&
|
|
29
|
-
if (post = current_site.the_posts.find_by_slug(params[:slug]))
|
|
33
|
+
elsif params[:action] == 'post' && params[:controller] == 'camaleon_cms/frontend' && params[:draft_id].blank?
|
|
34
|
+
if (post = current_site.the_posts.find_by_slug(params[:slug])) # rubocop:disable Rails/DynamicFindBy
|
|
30
35
|
post = post.decorate
|
|
31
|
-
|
|
36
|
+
# Never cache non-public posts. A password-protected post is unlocked per session (visibility_post
|
|
37
|
+
# audit M2), but the page cache is keyed on the URL alone, so caching an unlocked render would
|
|
38
|
+
# serve the protected body to visitors who never entered the password. Private posts are already
|
|
39
|
+
# excluded; password posts must be too.
|
|
40
|
+
if post.can_visit? && !%w[private password].include?(post.visibility)
|
|
32
41
|
if (@caches[:skip_posts] || []).include?(post.id.to_s)
|
|
33
42
|
@_plugin_do_cache = false
|
|
34
|
-
elsif (@caches[:post_types] || []).include?(post.post_type_id.to_s) ||
|
|
43
|
+
elsif (@caches[:post_types] || []).include?(post.post_type_id.to_s) ||
|
|
44
|
+
(@caches[:posts] || []).include?(post.id.to_s)
|
|
35
45
|
@_plugin_do_cache = true
|
|
36
46
|
end
|
|
37
47
|
end
|
|
@@ -43,7 +53,7 @@ module Plugins
|
|
|
43
53
|
|
|
44
54
|
def front_cache_front_after_load
|
|
45
55
|
cache_key = front_cache_plugin_cache_key
|
|
46
|
-
return unless @_plugin_do_cache &&
|
|
56
|
+
return unless @_plugin_do_cache && flash.keys.blank?
|
|
47
57
|
|
|
48
58
|
body =
|
|
49
59
|
response
|
|
@@ -135,12 +145,26 @@ module Plugins
|
|
|
135
145
|
end
|
|
136
146
|
|
|
137
147
|
def front_cache_plugin_match_path_patterns?(key, key2)
|
|
138
|
-
|
|
148
|
+
front_cache_compiled_path_patterns.any? { |pattern| key =~ pattern || key2 =~ pattern }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Security (audit Low): compile the admin-configured patterns once per request and skip any that
|
|
152
|
+
# are malformed -- Regexp.new was rebuilt on every request and raised (500) on an invalid pattern
|
|
153
|
+
# (RegexpError) or a non-String pattern reachable via crafted settings params (TypeError).
|
|
154
|
+
def front_cache_compiled_path_patterns
|
|
155
|
+
@front_cache_compiled_path_patterns ||= (@caches[:paths] || []).filter_map do |pattern|
|
|
156
|
+
Regexp.new(pattern)
|
|
157
|
+
rescue RegexpError, TypeError
|
|
158
|
+
nil
|
|
159
|
+
end
|
|
139
160
|
end
|
|
140
161
|
|
|
141
162
|
def front_cache_plugin_cache_key
|
|
142
163
|
uri = [request.protocol + request.host_with_port, request.fullpath].join('/')
|
|
143
|
-
|
|
164
|
+
# Security (audit Low): a lossless key. parameterize lowercased and collapsed every run of
|
|
165
|
+
# non-alphanumerics to '-', so distinct URLs shared one cache entry -- a visitor to one
|
|
166
|
+
# cacheable URL could be served the cached body of another.
|
|
167
|
+
Digest::SHA256.hexdigest(uri)
|
|
144
168
|
end
|
|
145
169
|
end
|
|
146
170
|
end
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"created_post":["plugin_visibility_create_post"],
|
|
18
18
|
"updated_post":["plugin_visibility_create_post"],
|
|
19
19
|
"post_the_content": ["plugin_visibility_post_the_content"],
|
|
20
|
+
"post_the_excerpt": ["plugin_visibility_post_the_excerpt"],
|
|
20
21
|
"filter_post": ["plugin_visibility_filter_post"],
|
|
21
22
|
"post_can_visit": ["plugin_visibility_can_visit"],
|
|
22
23
|
"list_post_extra_columns": ["plugin_visibility_extra_columns"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
post 'unlock/:post_id' => 'front#unlock', as: :unlock, constraints: { post_id: /[0-9]+/ }
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Plugins
|
|
2
|
+
module VisibilityPost
|
|
3
|
+
class FrontController < CamaleonCms::Apps::PluginsFrontController
|
|
4
|
+
# Security (audit M2): unlocks a password-protected post for the current session. Replaces
|
|
5
|
+
# the old GET `post_password` parameter flow, which put the password in URLs, logs and
|
|
6
|
+
# referrers and compared it with `==`. The password travels in a POST body, is compared in
|
|
7
|
+
# constant time, and the unlocked state is a session-side marker -- never the password.
|
|
8
|
+
def unlock
|
|
9
|
+
post = current_site.the_posts.where(id: params[:post_id]).first
|
|
10
|
+
return page_not_found if post.blank? || post.visibility != 'password'
|
|
11
|
+
|
|
12
|
+
submitted = params[:post_password].to_s
|
|
13
|
+
if submitted.present? && ActiveSupport::SecurityUtils.secure_compare(submitted, post.visibility_value.to_s)
|
|
14
|
+
session[VisibilityPostHelper::SESSION_UNLOCKED_KEY] =
|
|
15
|
+
Array(session[VisibilityPostHelper::SESSION_UNLOCKED_KEY]) | [post.id]
|
|
16
|
+
else
|
|
17
|
+
flash[:cama_visibility_post_error] = true
|
|
18
|
+
end
|
|
19
|
+
redirect_to post.decorate.the_url(as_path: true)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|