Give admins the ability to add spam role to users via the API (#20445)

This commit is contained in:
Anna Buianova 2023-12-12 17:35:55 +03:00 committed by GitHub
parent e0ba90d60a
commit 81b042ee25
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 382 additions and 1718 deletions

View file

@ -5,7 +5,7 @@ module Api
# 'suspended' is also known as 'suspend' for historical reasons
SUSPEND_MODE = %w[suspend suspended].freeze
ROLES = (SUSPEND_MODE + %w[limited]).freeze
ROLES = (SUSPEND_MODE + %w[limited spam]).freeze
before_action :check_role
before_action :set_target_user
@ -24,7 +24,7 @@ module Api
end
def destroy
# This mechanism for removing roles is pretty specific to limited & suspended,
# This mechanism for removing roles is pretty specific to limited, suspended and spam,
# where they revert to "Good standing" — we would need a different approach,
# possibly a whole different service object — to remove *any* roles
remove_role_from_target_user

View file

@ -73,11 +73,11 @@ Rails.application.routes.draw do
resources :organizations, only: %i[index create update destroy]
scope("/users/:id") do
constraints(role: /suspend|suspended|limited/) do
constraints(role: /suspend|suspended|limited|spam/) do
put "/:role", to: "user_roles#update", as: "user_add_role"
end
constraints(role: /limited/) do
constraints(role: /limited|spam/) do
delete "/:role", to: "user_roles#destroy", as: "user_remove_role"
end
end

View file

@ -126,6 +126,10 @@ FactoryBot.define do
after(:build) { |user| user.add_role(:limited) }
end
trait :spam do
after(:build) { |user| user.add_role(:spam) }
end
trait :invited do
after(:build) do |user|
user.registered = false

View file

@ -182,6 +182,117 @@ notify them."
end
end
end
describe "PUT /users/:id/spam" do
before do
user.add_role(:admin)
end
path "/api/users/{id}/spam" do
put "Add spam role for a User" do
tags "users"
description "This endpoint allows the client to add the spam role to a user.
The user associated with the API key must have any 'admin' or 'moderator' role.
This specified user will be assigned the 'spam' role. Addding the spam role to a user will stop the
user from posting new posts and comments. It doesn't delete any of the user's content, just
prevents them from creating new content while having the spam role. Users are not notified of their spaminess
in the UI, so if you want them to know about this, you must notify them"
operationId "spamUser"
produces "application/json"
parameter name: :id, in: :path, required: true,
description: "The ID of the user to assign the spam role.",
schema: {
type: :integer,
format: :int32,
minimum: 1
},
example: 1
response "204", "Spam role assigned to the user successfully" do
let(:"api-key") { api_secret.secret }
let(:id) { banned_user.id }
add_examples
run_test!
end
response "401", "Unauthorized" do
let(:regular_user) { create(:user) }
let(:low_security_api_secret) { create(:api_secret, user: regular_user) }
let(:"api-key") { low_security_api_secret.secret }
let(:id) { banned_user.id }
add_examples
run_test!
end
response "404", "Unknown User ID" do
let(:"api-key") { api_secret.secret }
let(:id) { 10_000 }
add_examples
run_test!
end
end
end
end
describe "DELETE /users/:id/spam" do
before do
user.add_role(:admin)
end
path "/api/users/{id}/spam" do
delete "Remove spam role from a User" do
tags "users"
description "This endpoint allows the client to remove the spam role for a user.
The user associated with the API key must have any 'admin' or 'moderator' role.
This specified user will be restored to 'general' status. Users are not notified
of removing their spam role in the UI, so if you want them to know about this, you must
notify them."
operationId "unSpamUser"
produces "application/json"
parameter name: :id, in: :path, required: true,
description: "The ID of the user to remove the spam role from.",
schema: {
type: :integer,
format: :int32,
minimum: 1
},
example: 1
response "204", "Successfully removed the spam role from a user" do
let(:"api-key") { api_secret.secret }
let(:id) { banned_user.id }
add_examples
run_test!
end
response "401", "Unauthorized" do
let(:regular_user) { create(:user) }
let(:low_security_api_secret) { create(:api_secret, user: regular_user) }
let(:"api-key") { low_security_api_secret.secret }
let(:id) { banned_user.id }
add_examples
run_test!
end
response "404", "Unknown User ID" do
let(:"api-key") { api_secret.secret }
let(:id) { 10_000 }
add_examples
run_test!
end
end
end
end
end
end
# rubocop:enable RSpec/VariableName

View file

@ -178,4 +178,113 @@ RSpec.describe "Api::V1::UserRoles" do
end
end
end
describe "PUT /api/users/:id/spam", :aggregate_failures do
let(:target_user) { create(:user) }
before { Audit::Subscribe.listen listener }
after { Audit::Subscribe.forget listener }
context "when unauthenticated" do
it "returns unauthorized" do
put api_user_add_role_path(target_user, "spam"), headers: headers
expect(response).to have_http_status(:unauthorized)
end
end
context "when unauthorized" do
it "returns unauthorized if api key is invalid" do
put api_user_add_role_path(target_user, "spam"),
headers: headers.merge({ "api-key" => "invalid api key" })
expect(response).to have_http_status(:unauthorized)
end
# Setup via let(:api_secret) creates a user with no admin privileges
it "returns unauthorized if api key belongs to non-admin user" do
put api_user_add_role_path(target_user, "spam"), headers: auth_headers
expect(response).to have_http_status(:unauthorized)
end
end
context "when request is authenticated" do
before { api_secret.user.add_role(:super_admin) }
it "is successful in limiting a user", :aggregate_failures do
expect do
put api_user_add_role_path(target_user, "spam"), headers: auth_headers
expect(response).to have_http_status(:no_content)
expect(target_user.reload.spam?).to be true
expect(Note.last.content).to match(/username\d+ updated username\d+/)
end.to change(Note, :count).by(1)
end
it "creates an audit log of the action taken" do
put api_user_add_role_path(target_user, "spam"), headers: auth_headers
log = AuditLog.last
expect(log.category).to eq(AuditLog::ADMIN_API_AUDIT_LOG_CATEGORY)
expect(log.data["action"]).to eq("api_user_spam")
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 "DELETE /api/users/:id/spam", :aggregate_failures do
let(:target_user) { create(:user, :spam) }
before { Audit::Subscribe.listen listener }
after { Audit::Subscribe.forget listener }
context "when unauthenticated" do
it "returns unauthorized" do
delete api_user_remove_role_path(target_user, "spam"), headers: headers
expect(response).to have_http_status(:unauthorized)
end
end
context "when unauthorized" do
it "returns unauthorized if api key is invalid" do
delete api_user_remove_role_path(target_user, "spam"),
headers: headers.merge({ "api-key" => "invalid api key" })
expect(response).to have_http_status(:unauthorized)
end
# Setup via let(:api_secret) creates a user with no admin privileges
it "returns unauthorized if api key belongs to non-admin user" do
delete api_user_remove_role_path(target_user, "spam"), headers: auth_headers
expect(response).to have_http_status(:unauthorized)
end
end
context "when request is authenticated" do
before { api_secret.user.add_role(:super_admin) }
it "is successful in adding the spam role to a user", :aggregate_failures do
expect do
delete api_user_remove_role_path(target_user, "spam"), headers: auth_headers
expect(response).to have_http_status(:no_content)
expect(target_user.reload.spam?).to be false
expect(target_user.roles).to eq([])
expect(Note.last.content).to match(/username\d+ updated username\d+/)
end.to change(Note, :count).by(1)
end
it "creates an audit log of the action taken" do
delete api_user_remove_role_path(target_user, "spam"), headers: auth_headers
log = AuditLog.last
expect(log.category).to eq(AuditLog::ADMIN_API_AUDIT_LOG_CATEGORY)
expect(log.data["action"]).to eq("api_user_remove_spam")
expect(log.data["target_user_id"]).to eq(target_user.id)
expect(log.user_id).to eq(api_secret.user.id)
end
end
end
end

File diff suppressed because it is too large Load diff