mirror of
https://github.com/we-promise/sure.git
synced 2026-09-02 05:11:05 +00:00
* feat: add safe admin user removal * fix: address user removal review findings * fix: close remaining user removal review gaps * fix: handle deleted users during session creation * fix: fail closed when session creation fails * fix: reject token issuance for inactive users
197 lines
6.4 KiB
Ruby
197 lines
6.4 KiB
Ruby
require "test_helper"
|
|
|
|
class Admin::UsersControllerTest < ActionDispatch::IntegrationTest
|
|
include ActiveJob::TestHelper
|
|
|
|
setup do
|
|
sign_in users(:sure_support_staff)
|
|
end
|
|
|
|
test "index groups users by family sorted by transaction count" do
|
|
family_with_more = users(:family_admin).family
|
|
family_with_fewer = users(:empty).family
|
|
|
|
account = Account.create!(family: family_with_more, name: "Test", balance: 0, currency: "USD", accountable: Depository.new)
|
|
3.times { |i| account.entries.create!(name: "Txn #{i}", date: Date.current, amount: 10, currency: "USD", entryable: Transaction.new) }
|
|
|
|
get admin_users_url
|
|
assert_response :success
|
|
|
|
body = response.body
|
|
more_idx = body.index(family_with_more.name)
|
|
fewer_idx = body.index(family_with_fewer.name)
|
|
|
|
assert_not_nil more_idx
|
|
assert_not_nil fewer_idx
|
|
assert_operator more_idx, :<, fewer_idx,
|
|
"Family with more transactions should appear before family with fewer"
|
|
end
|
|
|
|
test "index shows subscription status for families" do
|
|
family = users(:family_admin).family
|
|
family.subscription&.destroy
|
|
Subscription.create!(
|
|
family_id: family.id,
|
|
status: :active,
|
|
stripe_id: "cus_test_#{family.id}"
|
|
)
|
|
|
|
get admin_users_url
|
|
assert_response :success
|
|
assert_match(/Active/, response.body, "Page should show subscription status for families with active subscriptions")
|
|
end
|
|
|
|
test "index shows no subscription label for families without subscription" do
|
|
users(:family_admin).family.subscription&.destroy
|
|
|
|
get admin_users_url
|
|
assert_response :success
|
|
assert_match(/No subscription/, response.body, "Page should show 'No subscription' for families without one")
|
|
end
|
|
|
|
test "index shows removed SSO identities with a recovery action" do
|
|
block = SsoIdentityBlock.create!(
|
|
provider: "openid_connect",
|
|
uid_digest: SsoIdentityBlock.digest("blocked-subject"),
|
|
identity_label: "removed-user@example.com"
|
|
)
|
|
|
|
get admin_users_url
|
|
|
|
assert_response :success
|
|
assert_select "form[action=?]", admin_sso_identity_block_path(block)
|
|
assert_match block.identity_label, response.body
|
|
end
|
|
|
|
test "super admin permanently removes a user and revokes their credentials" do
|
|
target = users(:family_member)
|
|
target_email = target.email
|
|
removed_identity = target.oidc_identities.first!
|
|
removed_provider = removed_identity.provider
|
|
removed_uid = removed_identity.uid
|
|
target.sessions.create!
|
|
oauth_app = Doorkeeper::Application.create!(
|
|
name: "Removal test",
|
|
redirect_uri: "https://app.example/callback",
|
|
confidential: false
|
|
)
|
|
oauth_token = Doorkeeper::AccessToken.create!(
|
|
application: oauth_app,
|
|
resource_owner_id: target.id,
|
|
scopes: "read_write",
|
|
use_refresh_token: true
|
|
)
|
|
assert target.oidc_identities.exists?
|
|
assert target.api_keys.exists?
|
|
|
|
assert_difference -> { SsoAuditLog.by_event("user_removed").count }, 1 do
|
|
assert_enqueued_with(job: UserPurgeJob, args: [ target ]) do
|
|
delete admin_user_url(target), params: { confirmation_email: target_email }
|
|
end
|
|
end
|
|
|
|
assert_redirected_to admin_users_path
|
|
target.reload
|
|
assert_not target.active?
|
|
assert_empty target.sessions
|
|
assert_empty target.oidc_identities
|
|
assert_empty target.api_keys
|
|
assert oauth_token.reload.revoked?
|
|
assert SsoIdentityBlock.blocked?(provider: removed_provider, uid: removed_uid)
|
|
identity_block = SsoIdentityBlock.find_by!(provider: removed_provider)
|
|
if SsoIdentityBlock.encryption_ready?
|
|
assert_equal target_email, identity_block.identity_label
|
|
else
|
|
assert_not_equal target_email, identity_block.identity_label
|
|
end
|
|
audit_log = SsoAuditLog.by_event("user_removed").order(:created_at).last
|
|
assert_equal target.id, audit_log.metadata.fetch("target_user_id")
|
|
assert_not audit_log.metadata.key?("target_email")
|
|
assert_equal users(:sure_support_staff).id, audit_log.metadata.fetch("actor_user_id")
|
|
end
|
|
|
|
test "super admin cannot remove themselves" do
|
|
me = users(:sure_support_staff)
|
|
|
|
assert_no_enqueued_jobs only: UserPurgeJob do
|
|
delete admin_user_url(me)
|
|
end
|
|
|
|
assert_redirected_to admin_users_path
|
|
assert User.exists?(me.id)
|
|
assert me.reload.active?
|
|
end
|
|
|
|
test "audit failure rolls back user removal" do
|
|
target = users(:family_member)
|
|
target_email = target.email
|
|
SsoAuditLog.expects(:log_user_removed!).raises("audit failure")
|
|
|
|
assert_no_enqueued_jobs only: UserPurgeJob do
|
|
assert_raises(RuntimeError, match: /audit failure/) do
|
|
delete admin_user_url(target), params: { confirmation_email: target_email }
|
|
end
|
|
end
|
|
|
|
assert target.reload.active?
|
|
assert target.oidc_identities.exists?
|
|
end
|
|
|
|
test "inactive super admin cannot use an existing session to remove the last active super admin" do
|
|
target = users(:family_admin)
|
|
target.update!(role: :super_admin)
|
|
User.where(role: :super_admin).where.not(id: target.id).update_all(active: false)
|
|
|
|
assert_no_enqueued_jobs only: UserPurgeJob do
|
|
delete admin_user_url(target), params: { confirmation_email: target.email }
|
|
end
|
|
|
|
assert_redirected_to new_session_path
|
|
assert User.exists?(target.id)
|
|
assert target.reload.active?
|
|
end
|
|
|
|
test "deletion page redirects instead of erroring when targeting yourself" do
|
|
me = users(:sure_support_staff)
|
|
|
|
get deletion_admin_user_url(me)
|
|
|
|
assert_redirected_to admin_users_path
|
|
assert_match(/cannot remove your own account/i, flash[:alert].to_s)
|
|
end
|
|
|
|
test "deletion confirmation requires the target email" do
|
|
target = users(:family_member)
|
|
|
|
assert_no_enqueued_jobs only: UserPurgeJob do
|
|
delete admin_user_url(target), params: { confirmation_email: "wrong@example.com" }
|
|
end
|
|
|
|
assert_redirected_to admin_users_path
|
|
assert target.reload.active?
|
|
end
|
|
|
|
test "deletion page renders a typed email confirmation dialog" do
|
|
target = users(:family_member)
|
|
|
|
get deletion_admin_user_url(target)
|
|
|
|
assert_response :success
|
|
assert_select "dialog"
|
|
assert_select "input[name=confirmation_email][required]"
|
|
assert_includes response.body, target.email
|
|
end
|
|
|
|
test "non super admin cannot remove a user" do
|
|
sign_in users(:family_member)
|
|
target = users(:family_admin)
|
|
|
|
assert_no_enqueued_jobs only: UserPurgeJob do
|
|
delete admin_user_url(target)
|
|
end
|
|
|
|
assert_redirected_to root_path
|
|
assert User.exists?(target.id)
|
|
end
|
|
end
|