Include audit log for API admin endpoints (#18167)
* First commit for audit log - specs need fixing * Fix notification subscription issues + specs * Use single line in notification subscribe initializer * Remove conditional in unpublish endpoint
This commit is contained in:
parent
ffa5e023bb
commit
f77b5f83a9
6 changed files with 74 additions and 9 deletions
|
|
@ -109,6 +109,9 @@ module Api
|
|||
authorize @article, :revoke_publication?
|
||||
|
||||
if Articles::Unpublish.call(@user, @article)
|
||||
payload = { action: "api_article_unpublish", article_id: @article.id }
|
||||
Audit::Logger.log(:admin_api, @user, payload)
|
||||
|
||||
render head: :ok
|
||||
else
|
||||
render json: { message: @article.errors.full_messages }, status: :unprocessable_entity
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ module Api
|
|||
Moderator::ManageActivityAndRoles.handle_user_roles(admin: @user,
|
||||
user: target_user,
|
||||
user_params: suspend_params)
|
||||
|
||||
payload = { action: "api_user_suspend", target_user_id: target_user.id }
|
||||
Audit::Logger.log(:admin_api, @user, payload)
|
||||
|
||||
render head: :ok
|
||||
rescue StandardError
|
||||
render json: {
|
||||
|
|
@ -43,7 +47,17 @@ module Api
|
|||
|
||||
def unpublish
|
||||
authorize(@user, :unpublish_all_articles?)
|
||||
target_articles = Article.published.where(user_id: params[:id].to_i)
|
||||
|
||||
Moderator::UnpublishAllArticlesWorker.perform_async(params[:id].to_i)
|
||||
|
||||
payload = {
|
||||
action: "api_user_unpublish",
|
||||
target_user_id: params[:id].to_i,
|
||||
target_article_ids: target_articles.ids
|
||||
}
|
||||
Audit::Logger.log(:admin_api, @user, payload)
|
||||
|
||||
render head: :ok
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ class AuditLog < ApplicationRecord
|
|||
validates :data, presence: true
|
||||
|
||||
MODERATOR_AUDIT_LOG_CATEGORY = "moderator.audit.log".freeze
|
||||
ADMIN_API_AUDIT_LOG_CATEGORY = "admin_api.audit.log".freeze
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,5 +7,5 @@
|
|||
# Audit::Subscribe.listen :internal, :quest_user
|
||||
|
||||
Rails.application.reloader.to_prepare do
|
||||
Audit::Subscribe.listen(:moderator, :internal) unless Rails.env.test?
|
||||
Audit::Subscribe.listen(:moderator, :internal, :admin_api) unless Rails.env.test?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ RSpec.describe "Api::V1::Articles", type: :request do
|
|||
let(:article) { create(:article, featured: true, tags: "discuss") }
|
||||
let(:new_article) { create(:article) }
|
||||
let(:api_secret) { create(:api_secret) }
|
||||
let(:listener) { :admin_api }
|
||||
let(:v1_headers) do
|
||||
{
|
||||
"content-type" => "application/json",
|
||||
|
|
@ -1184,11 +1185,13 @@ RSpec.describe "Api::V1::Articles", type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe "PUT /api/articles/:id/unpublish" do
|
||||
describe "PUT /api/articles/:id/unpublish", :aggregate_failures do
|
||||
let(:user) { api_secret.user }
|
||||
let!(:published_article) { create(:article, published: true) }
|
||||
let(:path) { api_article_unpublish_path(published_article.id) }
|
||||
|
||||
before { Audit::Subscribe.listen listener }
|
||||
|
||||
context "when unauthorized" do
|
||||
it "fails with no api key" do
|
||||
put path, headers: { "content-type" => "application/json", "Accept" => "application/vnd.forem.api-v1+json" }
|
||||
|
|
@ -1228,6 +1231,16 @@ RSpec.describe "Api::V1::Articles", type: :request do
|
|||
expect(response).to have_http_status(:ok)
|
||||
expect(published_article.reload.published).to be false
|
||||
end
|
||||
|
||||
it "creates an audit log of the action taken" do
|
||||
put path, headers: v1_headers
|
||||
|
||||
log = AuditLog.last
|
||||
expect(log.category).to eq(AuditLog::ADMIN_API_AUDIT_LOG_CATEGORY)
|
||||
expect(log.data["action"]).to eq("api_article_unpublish")
|
||||
expect(log.data["article_id"]).to eq(published_article.id)
|
||||
expect(log.user_id).to eq(user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ require "rails_helper"
|
|||
RSpec.describe "Api::V0::Users", type: :request do
|
||||
let(:api_secret) { create(:api_secret) }
|
||||
let(:v1_headers) { { "api-key" => api_secret.secret, "Accept" => "application/vnd.forem.api-v1+json" } }
|
||||
let(:listener) { :admin_api }
|
||||
|
||||
describe "GET /api/users/:id" do
|
||||
before { allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true) }
|
||||
|
|
@ -143,11 +144,14 @@ RSpec.describe "Api::V0::Users", type: :request do
|
|||
end
|
||||
end
|
||||
|
||||
describe "PUT /api/users/:id/suspend" do
|
||||
describe "PUT /api/users/:id/suspend", :aggregate_failures do
|
||||
let(:target_user) { create(:user) }
|
||||
let(:payload) { { note: "Violated CoC despite multiple warnings" } }
|
||||
|
||||
before { allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true) }
|
||||
before do
|
||||
allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true)
|
||||
Audit::Subscribe.listen listener
|
||||
end
|
||||
|
||||
context "when unauthenticated" do
|
||||
it "returns unauthorized" do
|
||||
|
|
@ -178,9 +182,9 @@ RSpec.describe "Api::V0::Users", type: :request do
|
|||
end
|
||||
|
||||
context "when request is authenticated" do
|
||||
it "is successful in suspending a user", :aggregate_failures do
|
||||
api_secret.user.add_role(:super_admin)
|
||||
before { api_secret.user.add_role(:super_admin) }
|
||||
|
||||
it "is successful in suspending a user", :aggregate_failures do
|
||||
expect do
|
||||
put api_user_suspend_path(id: target_user.id),
|
||||
params: payload,
|
||||
|
|
@ -191,13 +195,29 @@ RSpec.describe "Api::V0::Users", type: :request do
|
|||
expect(Note.last.content).to eq(payload[:note])
|
||||
end.to change(Note, :count).by(1)
|
||||
end
|
||||
|
||||
it "creates an audit log of the action taken" do
|
||||
put api_user_suspend_path(id: target_user.id),
|
||||
params: payload,
|
||||
headers: v1_headers
|
||||
|
||||
log = AuditLog.last
|
||||
expect(log.category).to eq(AuditLog::ADMIN_API_AUDIT_LOG_CATEGORY)
|
||||
expect(log.data["action"]).to eq("api_user_suspend")
|
||||
expect(log.data["target_user_id"]).to eq(target_user.id)
|
||||
expect(log.user_id).to eq(api_secret.user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "PUT /api/users/:id/unpublish" do
|
||||
describe "PUT /api/users/:id/unpublish", :aggregate_failures do
|
||||
let(:target_user) { create(:user) }
|
||||
let!(:target_articles) { create_list(:article, 3, user_id: target_user.id) }
|
||||
|
||||
before { allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true) }
|
||||
before do
|
||||
allow(FeatureFlag).to receive(:enabled?).with(:api_v1).and_return(true)
|
||||
Audit::Subscribe.listen listener
|
||||
end
|
||||
|
||||
context "when unauthenticated" do
|
||||
it "returns unauthorized" do
|
||||
|
|
@ -225,15 +245,29 @@ RSpec.describe "Api::V0::Users", type: :request do
|
|||
end
|
||||
|
||||
context "when request is authenticated" do
|
||||
it "is successful in unpublishing a user's comments and articles", :aggregate_failures do
|
||||
before do
|
||||
allow(Moderator::UnpublishAllArticlesWorker).to receive(:perform_async)
|
||||
api_secret.user.add_role(:super_admin)
|
||||
end
|
||||
|
||||
it "is successful in unpublishing a user's comments and articles", :aggregate_failures do
|
||||
put api_user_unpublish_path(id: target_user.id),
|
||||
headers: v1_headers
|
||||
|
||||
expect(response).to have_http_status(:ok)
|
||||
expect(Moderator::UnpublishAllArticlesWorker).to have_received(:perform_async).with(target_user.id).once
|
||||
end
|
||||
|
||||
it "creates an audit log of the action taken" do
|
||||
put api_user_unpublish_path(id: target_user.id),
|
||||
headers: v1_headers
|
||||
|
||||
log = AuditLog.last
|
||||
expect(log.category).to eq(AuditLog::ADMIN_API_AUDIT_LOG_CATEGORY)
|
||||
expect(log.data["action"]).to eq("api_user_unpublish")
|
||||
expect(log.data["target_article_ids"]).to match_array(target_articles.map(&:id))
|
||||
expect(log.user_id).to eq(api_secret.user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue