Skip to content

Commit

Permalink
Merge pull request #13948 from abellotti/api_fine_grained_settings
Browse files Browse the repository at this point in the history
API Enhancement to support fine-grained settings whitelisting
  • Loading branch information
gtanzillo authored Mar 16, 2017
2 parents e3e4ac4 + 86cd8ff commit 7af6fcd
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 7 deletions.
28 changes: 21 additions & 7 deletions app/controllers/api/settings_controller.rb
Original file line number Diff line number Diff line change
@@ -1,22 +1,36 @@
module Api
class SettingsController < BaseController
def index
render_resource :settings, slice_settings(exposed_settings)
render_resource :settings, whitelist_settings(settings_hash)
end

def show
raise NotFoundError, "Settings category #{@req.c_id} not found" unless exposed_settings.include?(@req.c_id)
render_resource :settings, slice_settings(@req.c_id)
settings_value = entry_value(whitelist_settings(settings_hash), @req.c_suffix)

raise NotFoundError, "Settings entry #{@req.c_suffix} not found" if settings_value.nil?
render_resource :settings, settings_entry_to_hash(@req.c_suffix, settings_value)
end

private

def exposed_settings
ApiConfig.collections[:settings][:categories]
def whitelist_settings(settings)
result_hash = {}
ApiConfig.collections[:settings][:categories].each do |category_path|
result_hash.deep_merge!(settings_entry_to_hash(category_path, entry_value(settings, category_path)))
end
result_hash
end

def settings_hash
@settings_hash ||= Settings.to_hash.deep_stringify_keys
end

def entry_value(settings, path)
settings.fetch_path(path.split('/'))
end

def slice_settings(keys)
::Settings.to_hash.slice(*Array(keys).collect(&:to_sym))
def settings_entry_to_hash(path, value)
{}.tap { |h| h.store_path(path.split("/"), value) }
end
end
end
107 changes: 107 additions & 0 deletions spec/requests/api/settings_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,111 @@
expect(response).to have_http_status(:not_found)
end
end

context "Fine-Grained Settings Queries" do
let(:sample_settings) do
JSON.parse('{
"product": {
"maindb": "ExtManagementSystem",
"container_deployment_wizard": false,
"datawarehouse_manager": false
},
"server": {
"role": "database_operations,event,reporting,scheduler,smartstate,ems_operations,ems_inventory,user_interface,websocket,web_services,automate",
"worker_monitor": {
"kill_algorithm": {
"name": "used_swap_percent_gt_value",
"value": 80
},
"miq_server_time_threshold": "2.minutes",
"nice_delta": 1,
"poll": "2.seconds",
"sync_interval": "30.minutes",
"wait_for_started_timeout": "10.minutes"
}
},
"authentication": {
"bind_timeout": 30,
"follow_referrals": false,
"get_direct_groups": true,
"group_memberships_max_depth": 2,
"ldapport": "389",
"mode": "database",
"search_timeout": 30,
"user_type": "userprincipalname"
}
}')
end

# TODO: as the api.yml settings/categories expand in the future to include
# different types of categories, full, partial and single entries, we would no
# longer need this stubbing logic or the above sample_settings as we can
# test the different cases with the default api.yml categories provided.
#
def stub_api_settings_categories(value)
settings_config = Api::ApiConfig.collections["settings"].dup
settings_config["categories"] = value
allow(Api::ApiConfig.collections).to receive("settings") { settings_config }
end

before do
stub_settings_merge(sample_settings)
end

it "supports multiple categories" do
stub_api_settings_categories(%w(product authentication server))
api_basic_authorize action_identifier(:settings, :read, :resource_actions, :get)

run_get settings_url

expect(response.parsed_body).to match(
"product" => sample_settings["product"],
"authentication" => sample_settings["authentication"],
"server" => sample_settings["server"]
)
end

it "supports partial categories" do
stub_api_settings_categories(%w(product server/role))
api_basic_authorize action_identifier(:settings, :read, :resource_actions, :get)

run_get settings_url

expect(response.parsed_body).to match(
"product" => sample_settings["product"],
"server" => { "role" => sample_settings["server"]["role"] }
)
end

it "supports second level partial categories" do
stub_api_settings_categories(%w(product server/role server/worker_monitor/sync_interval))
api_basic_authorize action_identifier(:settings, :read, :resource_actions, :get)

run_get settings_url

expect(response.parsed_body).to match(
"product" => sample_settings["product"],
"server" => {
"role" => sample_settings["server"]["role"],
"worker_monitor" => { "sync_interval" => sample_settings["server"]["worker_monitor"]["sync_interval"] }
}
)
end

it "supports multiple and partial categories" do
stub_api_settings_categories(%w(product server/role server/worker_monitor authentication))
api_basic_authorize action_identifier(:settings, :read, :resource_actions, :get)

run_get settings_url

expect(response.parsed_body).to match(
"product" => sample_settings["product"],
"server" => {
"role" => sample_settings["server"]["role"],
"worker_monitor" => sample_settings["server"]["worker_monitor"],
},
"authentication" => sample_settings["authentication"]
)
end
end
end

0 comments on commit 7af6fcd

Please sign in to comment.