Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion admin/app/controllers/workarea/admin/bulk_actions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ module Workarea
module Admin
class BulkActionsController < Admin::ApplicationController
def create
klass = params[:type].constantize
klass = bulk_action_class_for(params[:type])
return head(:unprocessable_entity) if klass.nil?

raise unless klass < BulkAction
bulk_action = Mongoid::Factory.build(klass, bulk_action_params(klass))

Expand Down Expand Up @@ -36,6 +38,17 @@ def destroy

private

# Returns the BulkAction subclass whose name matches +type_name+, or nil
# if the name is not present in Workarea.config.bulk_action_types.
# Constantize is called on the allowlisted config strings, never on the
# raw parameter value.
def bulk_action_class_for(type_name)
target = type_name.to_s
Workarea.config.bulk_action_types
.map { |t| t.constantize }
.find { |klass| klass.name == target }
end

def bulk_action_params(klass)
base = params.select { |k, v| k.in?(klass.fields.keys) }
base.merge(params[:bulk_action] || {})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Workarea
module Admin
module SegmentRuleLookup
extend ActiveSupport::Concern

private

def segment_rule_class_for(rule_type)
slug = rule_type.to_s.underscore
Workarea.config.segment_rule_types
.map { |t| t.constantize }
.find { |klass| klass.slug.to_s == slug }
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module Workarea
module Admin
class CreateSegmentsController < Admin::ApplicationController
include SegmentRuleLookup

required_permissions :people

before_action :find_segment
Expand Down Expand Up @@ -62,8 +64,9 @@ def find_rule
@rule = if params[:rule_id].present?
@segment.rules.where(id: params[:rule_id]).first
else
klass = "Workarea::Segment::Rules::#{params[:rule_type].to_s.camelize}"
@segment.model.rules.build(params[:rule], klass.constantize)
klass = segment_rule_class_for(params[:rule_type])
return head(:unprocessable_entity) if klass.nil?
@segment.model.rules.build(params[:rule], klass)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
module Workarea
module Admin
class SegmentRulesController < Admin::ApplicationController
include SegmentRuleLookup

before_action :find_segment, except: :geolocation_options
before_action :find_rules, except: :geolocation_options
before_action :find_rule, except: [:index, :geolocation_options]
Expand Down Expand Up @@ -55,8 +57,9 @@ def find_rule
@rule = if params[:id].present?
@segment.rules.where(id: params[:id]).first
else
klass = "Workarea::Segment::Rules::#{params[:rule_type].to_s.camelize}"
@segment.model.rules.build(params[:rule], klass.constantize)
klass = segment_rule_class_for(params[:rule_type])
return head(:unprocessable_entity) if klass.nil?
@segment.model.rules.build(params[:rule], klass)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,32 @@ module Admin
class BulkActionsIntegrationTest < Workarea::IntegrationTest
include Admin::IntegrationTest

def test_rejects_unknown_bulk_action_type
post admin.bulk_actions_path,
headers: { 'Referer' => admin.catalog_products_path },
params: {
type: 'Kernel',
query_id: 'fake',
ids: %w(1)
}

assert_equal(422, response.status)
assert_equal(0, BulkAction.count)
end

def test_rejects_non_bulk_action_subclass
post admin.bulk_actions_path,
headers: { 'Referer' => admin.catalog_products_path },
params: {
type: 'Workarea::User',
query_id: 'fake',
ids: %w(1)
}

assert_equal(422, response.status)
assert_equal(0, BulkAction.count)
end

def test_create
6.times.each do |id|
create_product(id: id, name: 'foo', filters: { 'bar' => 'baz' })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ module Admin
class CreateSegmentsIntegrationTest < Workarea::IntegrationTest
include Admin::IntegrationTest

def test_new_rule_rejects_unknown_rule_type
segment = create_segment(rules: [])

get admin.new_rule_create_segment_path(segment),
params: { rule_type: 'kernel' }

assert_equal(422, response.status)
end

def test_creates_segments
post admin.create_segments_path, params: { segment: { name: 'foo bar' } }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ def test_destroy
assert_equal(0, @segment.reload.rules.size)
end

def test_rejects_unknown_rule_type
post admin.segment_rules_path(@segment),
params: { rule_type: 'kernel', rule: {} }

assert_equal(422, response.status)
assert_equal(0, @segment.reload.rules.size)
end

def test_geolocation_options
get admin.geolocation_options_segment_rules_path(q: 'penn', format: 'json')
results = JSON.parse(response.body)['results']
Expand Down
8 changes: 7 additions & 1 deletion core/app/queries/workarea/admin_search_query_wrapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ def id
end

def klass
params[:model_type].constantize
model_type = params[:model_type].to_s
unless model_type.in?(Workarea.config.admin_exportable_models)
raise ArgumentError,
"#{model_type.inspect} is not in Workarea.config.admin_exportable_models. " \
"Add it to the allowlist before using it with AdminSearchQueryWrapper."
end
model_type.constantize
end

def results
Expand Down
39 changes: 39 additions & 0 deletions core/lib/workarea/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -708,6 +708,16 @@ def self.setup_defaults
# The TTL of bulk action records
config.bulk_action_expiration = 6.months

# Allowlist of BulkAction subclass names accepted by BulkActionsController.
# Plugins may append additional classes here.
config.bulk_action_types = SwappableList.new(
%w(
Workarea::BulkAction::Deletion
Workarea::BulkAction::ProductEdit
Workarea::BulkAction::SequentialProductEdit
)
)

# Classes used to update checkout data and determine checkout status.
# Used in Workarea::Checkout
config.checkout_steps = SwappableList.new(
Expand Down Expand Up @@ -1234,6 +1244,35 @@ def self.setup_defaults
# life cycle segment.
config.loyal_customers_last_order_days_ago = 180

# Allowlist of model class names that may be used as the model_type for
# admin data-file exports/imports and AdminSearchQueryWrapper.
# Plugins may append additional classes here.
config.admin_exportable_models = SwappableList.new(
%w(
Workarea::Catalog::Category
Workarea::Catalog::Product
Workarea::Content::Asset
Workarea::Content::Page
Workarea::Email::Signup
Workarea::Fulfillment::Sku
Workarea::Inventory::Sku
Workarea::Navigation::Redirect
Workarea::Order
Workarea::Payment::Transaction
Workarea::Pricing::Discount
Workarea::Pricing::Discount::CodeList
Workarea::Pricing::Discount::GeneratedPromoCode
Workarea::Pricing::Sku
Workarea::Release
Workarea::Segment
Workarea::Shipping::Service
Workarea::Shipping::Sku
Workarea::Tax::Category
Workarea::Tax::Rate
Workarea::User
)
)

# The list of types of rules for setting up custom segments in the admin
config.segment_rule_types = %w(
Workarea::Segment::Rules::BrowserInfo
Expand Down
10 changes: 10 additions & 0 deletions core/test/queries/workarea/admin_search_query_wrapper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ def test_searching
assert_equal([a], query.results.to_a)
end

def test_rejects_model_type_not_in_allowlist
query = AdminSearchQueryWrapper.new(model_type: 'Kernel')
assert_raises(ArgumentError) { query.klass }
end

def test_rejects_blank_model_type
query = AdminSearchQueryWrapper.new({})
assert_raises(ArgumentError) { query.klass }
end

def test_scroll
50.times { |i| create_redirect(path: "/#{i}", destination: '/bar') }

Expand Down
Loading