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:
Fernando Valverde 2022-07-22 11:35:30 -06:00 committed by GitHub
parent ffa5e023bb
commit f77b5f83a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 9 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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