From 974062f16169f6d07f2289564be6d72610b8770e Mon Sep 17 00:00:00 2001 From: erdgeist Date: Tue, 4 Aug 2026 05:40:54 +0200 Subject: Witness every promotion and demotion made through the roles form --- test/controllers/users_controller_test.rb | 46 +++++++++++++++++++++ test/models/user_test.rb | 66 +++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) (limited to 'test') diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 69c535f5..aeff9bc7 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -357,4 +357,50 @@ class UsersControllerTest < ActionController::TestCase assert_redirected_to new_elevation_path assert_equal ["admin", "redaktion"], users(:aaron).reload.roles.sort end + + test "promoting through the roles form leaves a log entry" do + login_as :aaron + elevate_session! + target = users(:redella) + target.update_column(:otp_secret, ROTP::Base32.random) + + assert_difference -> { NodeAction.where(:action => "admin_grant").count }, 1 do + put :update, params: { :locale => "de", :id => target.id, + :user => { :roles => ["", "redaktion", "admin"] } } + end + + assert_redirected_to user_path(target) + assert_equal %w[admin redaktion], target.reload.roles.sort + end + + test "a refused promotion re-renders with an error and changes nothing" do + login_as :aaron + elevate_session! + target = users(:quentin) + + assert_no_difference -> { NodeAction.count } do + put :update, params: { :locale => "de", :id => target.id, + :user => { :roles => ["", "admin"] } } + end + + assert_response :success + assert_empty target.reload.roles + assert_not_nil flash[:error] + assert_nil flash[:notice] + end + + test "a validation failure leaves no promotion behind" do + login_as :aaron + elevate_session! + target = users(:redella) + target.update_column(:otp_secret, ROTP::Base32.random) + + assert_no_difference -> { NodeAction.count } do + put :update, params: { :locale => "de", :id => target.id, + :user => { :email => "", :roles => ["", "redaktion", "admin"] } } + end + + assert_response :success + assert_equal %w[redaktion], target.reload.roles + end end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 62552ee7..5ccc53a9 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -6,6 +6,11 @@ class UserTest < ActiveSupport::TestCase include AuthenticatedTestHelper fixtures :users + def role_entries + NodeAction.where(:action => %w[admin_grant admin_revoke redaktion_grant + redaktion_revoke user_deactivate user_reactivate]) + end + def test_should_create_user assert_difference 'User.count' do user = create_user @@ -187,6 +192,67 @@ class UserTest < ActiveSupport::TestCase redella.update_column(:last_login_at, nil) assert_equal :never, redella.staleness_tier(now) end + + test "granting a role through update_roles! is witnessed" do + target = users(:redella) + target.update_column(:otp_secret, ROTP::Base32.random) + + assert_difference -> { role_entries.count }, 1 do + assert_empty target.update_roles!(%w[redaktion admin], :actor => users(:aaron)) + end + + assert_equal %w[admin redaktion], target.reload.roles.sort + entry = role_entries.order(:id).last + assert_equal "admin_grant", entry.action + assert_equal users(:aaron).id, entry.user_id + assert_equal "redella", entry.metadata["target_login"] + end + + test "a refused grant applies nothing at all" do + target = users(:quentin) + assert_not target.otp_enrolled? + + assert_no_difference -> { role_entries.count } do + refusals = target.update_roles!(%w[redaktion admin], :actor => users(:aaron)) + assert_includes refusals, :admin_needs_otp + assert_includes refusals, :redaktion_needs_otp + end + + assert_empty target.reload.roles, "a refusal must leave the stored set untouched" + end + + test "nobody demotes themselves through update_roles!" do + actor = users(:aaron) + + refusals = actor.update_roles!([], :actor => actor) + + assert_includes refusals, :admin_not_self + assert_includes refusals, :redaktion_not_self + assert_equal %w[admin redaktion], actor.reload.roles.sort + end + + test "marking an account alumni through update_roles! is witnessed as a deactivation" do + target = users(:redella) + + assert_difference -> { role_entries.where(:action => "user_deactivate").count }, 1 do + assert_empty target.update_roles!(%w[redaktion alumni], :actor => users(:aaron)) + end + + assert target.reload.alumni? + assert target.redaktion?, "deactivate! preserves the other roles" + end + + test "revoking and granting in one submission apply in the right order" do + target = users(:redella) + target.update_column(:otp_secret, ROTP::Base32.random) + + assert_empty target.update_roles!(%w[admin], :actor => users(:aaron)) + + assert_equal %w[admin], target.reload.roles + actions = role_entries.order(:id).last(2).map(&:action) + assert_includes actions, "redaktion_revoke" + assert_includes actions, "admin_grant" + end protected def create_user(options = {}) -- cgit v1.3