FEATURE: Extend topic update API scope to allow status updates (#19654)

Allow an API key created with topic:update API scope to make updates to
topic status. This change also introduces an optional category_id scope
param.
This commit is contained in:
Selase Krakani 2023-01-13 01:21:04 +00:00 committed by GitHub
parent 8a1b50f62d
commit 73ec80893d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 103 additions and 5 deletions

View File

@ -479,7 +479,12 @@ class TopicsController < ApplicationController
enabled = params[:enabled] == "true"
check_for_status_presence(:status, status)
@topic = Topic.find_by(id: topic_id)
@topic =
if params[:category_id]
Topic.find_by(id: topic_id, category_id: params[:category_id].to_i)
else
Topic.find_by(id: topic_id)
end
case status
when "closed"

View File

@ -28,8 +28,8 @@ class ApiKeyScope < ActiveRecord::Base
params: %i[topic_id],
},
update: {
actions: %w[topics#update],
params: %i[topic_id],
actions: %w[topics#update topics#status],
params: %i[topic_id category_id],
},
read: {
actions: %w[topics#show topics#feed topics#posts],

View File

@ -4670,7 +4670,7 @@ en:
topics:
read: Read a topic or a specific post in it. RSS is also supported.
write: Create a new topic or post to an existing one.
update: Update a topic. Change the title, category, tags, etc.
update: Update a topic. Change the title, category, tags, status, archetype, featured_link etc.
read_lists: Read topic lists like top, new, latest, etc. RSS is also supported.
posts:
edit: Edit any post or a specific one.

View File

@ -59,7 +59,7 @@ class RouteMatcher
params.all? do |param|
param_alias = aliases&.[](param)
allowed_values = [allowed_param_values[param.to_s]].flatten
allowed_values = [allowed_param_values.fetch(param.to_s, [])].flatten
value = requested_params[param.to_s]
alias_value = requested_params[param_alias.to_s]

View File

@ -1037,6 +1037,99 @@ RSpec.describe TopicsController do
expect(topic.posts.last.action_code).to eq("visible.enabled")
end
end
context "with API key" do
let(:api_key) { Fabricate(:api_key, user: moderator, created_by: moderator) }
context "when key scope has restricted params" do
before do
ApiKeyScope.create(
resource: "topics",
action: "update",
api_key_id: api_key.id,
allowed_parameters: {
"category_id" => ["#{topic.category_id}"],
},
)
end
it "fails to update topic status in an unpermitted category" do
put "/t/#{topic.id}/status.json",
params: {
status: "closed",
enabled: "true",
category_id: tracked_category.id,
},
headers: {
"HTTP_API_KEY" => api_key.key,
"HTTP_API_USERNAME" => api_key.user.username,
}
expect(response.status).to eq(403)
expect(response.body).to include(I18n.t("invalid_access"))
expect(topic.reload.closed).to eq(false)
end
it "fails without a category_id" do
put "/t/#{topic.id}/status.json",
params: {
status: "closed",
enabled: "true",
},
headers: {
"HTTP_API_KEY" => api_key.key,
"HTTP_API_USERNAME" => api_key.user.username,
}
expect(response.status).to eq(403)
expect(response.body).to include(I18n.t("invalid_access"))
expect(topic.reload.closed).to eq(false)
end
it "updates topic status in a permitted category" do
put "/t/#{topic.id}/status.json",
params: {
status: "closed",
enabled: "true",
category_id: topic.category_id,
},
headers: {
"HTTP_API_KEY" => api_key.key,
"HTTP_API_USERNAME" => api_key.user.username,
}
expect(response.status).to eq(200)
expect(topic.reload.closed).to eq(true)
end
end
context "when key scope has no param restrictions" do
before do
ApiKeyScope.create(
resource: "topics",
action: "update",
api_key_id: api_key.id,
allowed_parameters: {
},
)
end
it "updates topic status" do
put "/t/#{topic.id}/status.json",
params: {
status: "closed",
enabled: "true",
},
headers: {
"HTTP_API_KEY" => api_key.key,
"HTTP_API_USERNAME" => api_key.user.username,
}
expect(response.status).to eq(200)
expect(topic.reload.closed).to eq(true)
end
end
end
end
describe "#destroy_timings" do