diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-08-02 18:59:08 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-08-02 18:59:08 +0200 |
| commit | d9a018a75002bba009e60eaa87a5fb8276233c4e (patch) | |
| tree | 33335d1c861b01039e13b6e94be214e1b284eb6a | |
| parent | 833c59abf109943e857f64e526f6434e095242d6 (diff) | |
Witness account creation
| -rw-r--r-- | app/controllers/users_controller.rb | 2 | ||||
| -rw-r--r-- | app/helpers/node_actions_helper.rb | 10 | ||||
| -rw-r--r-- | app/models/node_action.rb | 13 | ||||
| -rw-r--r-- | app/models/user.rb | 11 | ||||
| -rw-r--r-- | config/locales/de.yml | 1 | ||||
| -rw-r--r-- | config/locales/en.yml | 1 | ||||
| -rw-r--r-- | test/controllers/users_controller_test.rb | 17 |
7 files changed, 46 insertions, 9 deletions
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9b9d64e5..7932e28b 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb | |||
| @@ -31,7 +31,7 @@ class UsersController < ApplicationController | |||
| 31 | def create | 31 | def create |
| 32 | @user = User.new user_params | 32 | @user = User.new user_params |
| 33 | 33 | ||
| 34 | if @user.save | 34 | if @user.save_witnessed(:actor => current_user) |
| 35 | flash[:notice] = t("flash.users.created", :login => @user.login) | 35 | flash[:notice] = t("flash.users.created", :login => @user.login) |
| 36 | redirect_to user_path(@user) | 36 | redirect_to user_path(@user) |
| 37 | else | 37 | else |
diff --git a/app/helpers/node_actions_helper.rb b/app/helpers/node_actions_helper.rb index 53f6ecd0..019a9cf7 100644 --- a/app/helpers/node_actions_helper.rb +++ b/app/helpers/node_actions_helper.rb | |||
| @@ -19,10 +19,11 @@ module NodeActionsHelper | |||
| 19 | "otp_enroll" => "shield-lock", | 19 | "otp_enroll" => "shield-lock", |
| 20 | "otp_disable" => "shield-off", | 20 | "otp_disable" => "shield-off", |
| 21 | "otp_reset" => "shield-x", | 21 | "otp_reset" => "shield-x", |
| 22 | "user_create" => "user-plus", | ||
| 22 | "user_deactivate" => "user-off", | 23 | "user_deactivate" => "user-off", |
| 23 | "user_reactivate" => "user-check", | 24 | "user_reactivate" => "user-check", |
| 24 | "redaktion_grant" => "user-plus", | 25 | "redaktion_grant" => "users-plus", |
| 25 | "redaktion_revoke" => "user-minus" | 26 | "redaktion_revoke" => "users-minus" |
| 26 | }.freeze | 27 | }.freeze |
| 27 | 28 | ||
| 28 | def verb_icon action | 29 | def verb_icon action |
| @@ -295,4 +296,9 @@ module NodeActionsHelper | |||
| 295 | t("node_actions.redaktion_revoke", :actor => actor_ref(action), | 296 | t("node_actions.redaktion_revoke", :actor => actor_ref(action), |
| 296 | :target => user_participant_ref(action)).html_safe | 297 | :target => user_participant_ref(action)).html_safe |
| 297 | end | 298 | end |
| 299 | |||
| 300 | def summarize_user_create action | ||
| 301 | t("node_actions.user_create", :actor => actor_ref(action), | ||
| 302 | :target => user_participant_ref(action)).html_safe | ||
| 303 | end | ||
| 298 | end | 304 | end |
diff --git a/app/models/node_action.rb b/app/models/node_action.rb index d619aac5..82a3ef44 100644 --- a/app/models/node_action.rb +++ b/app/models/node_action.rb | |||
| @@ -98,12 +98,13 @@ class NodeAction < ApplicationRecord | |||
| 98 | # "detached_from" -- array of unique_names, only when any | 98 | # "detached_from" -- array of unique_names, only when any |
| 99 | # "headline_removed_from" -- array of unique_names, only when any | 99 | # "headline_removed_from" -- array of unique_names, only when any |
| 100 | # | 100 | # |
| 101 | # "otp_enroll" / "otp_disable" / "otp_reset" / "user_deactivate" / | 101 | # "otp_enroll" / "otp_disable" / "otp_reset" (second-factor |
| 102 | # "user_reactivate" (second-factor lifecycle; node column nil; | 102 | # lifecycle) and "user_create" / "user_deactivate" / |
| 103 | # participants: the affected User | 103 | # "user_reactivate" (account lifecycle). Node column nil; |
| 104 | # User-typed subject. otp_disable is self-service; otp_reset is an | 104 | # participants: the affected User, a User-typed subject. |
| 105 | # administrator clearing someone else's factor, where actor and | 105 | # otp_disable is self-service; otp_reset and all three account |
| 106 | # participant differ): | 106 | # verbs are an administrator acting on someone else, so actor and |
| 107 | # participant differ: | ||
| 107 | # "target_login" -- flat string, the affected account's login | 108 | # "target_login" -- flat string, the affected account's login |
| 108 | # | 109 | # |
| 109 | # Reserved: "demote" (via "trash" | "depublish") for an explicit | 110 | # Reserved: "demote" (via "trash" | "depublish") for an explicit |
diff --git a/app/models/user.rb b/app/models/user.rb index 4b70ebe5..49c22584 100644 --- a/app/models/user.rb +++ b/app/models/user.rb | |||
| @@ -117,6 +117,17 @@ class User < ApplicationRecord | |||
| 117 | redaktion? | 117 | redaktion? |
| 118 | end | 118 | end |
| 119 | 119 | ||
| 120 | def save_witnessed(actor:) | ||
| 121 | saved = false | ||
| 122 | transaction do | ||
| 123 | saved = save | ||
| 124 | raise ActiveRecord::Rollback unless saved | ||
| 125 | NodeAction.record!(:participants => [self], :user => actor, | ||
| 126 | :action => "user_create", :target_login => login) | ||
| 127 | end | ||
| 128 | saved | ||
| 129 | end | ||
| 130 | |||
| 120 | def deactivate!(actor:) | 131 | def deactivate!(actor:) |
| 121 | return false if alumni? | 132 | return false if alumni? |
| 122 | transaction do | 133 | transaction do |
diff --git a/config/locales/de.yml b/config/locales/de.yml index 63f133f1..9ea828ed 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml | |||
| @@ -226,6 +226,7 @@ de: | |||
| 226 | otp_enroll: "%{actor} hat einen zweiten Faktor eingerichtet" | 226 | otp_enroll: "%{actor} hat einen zweiten Faktor eingerichtet" |
| 227 | otp_disable: "%{actor} hat den zweiten Faktor entfernt" | 227 | otp_disable: "%{actor} hat den zweiten Faktor entfernt" |
| 228 | otp_reset: "%{actor} hat den zweiten Faktor von %{target} zurückgesetzt" | 228 | otp_reset: "%{actor} hat den zweiten Faktor von %{target} zurückgesetzt" |
| 229 | user_create: "%{actor} hat das Konto %{target} angelegt" | ||
| 229 | user_deactivate: "%{actor} hat %{target} deaktiviert" | 230 | user_deactivate: "%{actor} hat %{target} deaktiviert" |
| 230 | user_reactivate: "%{actor} hat %{target} reaktiviert" | 231 | user_reactivate: "%{actor} hat %{target} reaktiviert" |
| 231 | redaktion_grant: "%{actor} hat %{target} in die Redaktion aufgenommen" | 232 | redaktion_grant: "%{actor} hat %{target} in die Redaktion aufgenommen" |
diff --git a/config/locales/en.yml b/config/locales/en.yml index 27098e87..7b5b1b86 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml | |||
| @@ -173,6 +173,7 @@ en: | |||
| 173 | otp_enroll: "%{actor} set up a second factor" | 173 | otp_enroll: "%{actor} set up a second factor" |
| 174 | otp_disable: "%{actor} removed their second factor" | 174 | otp_disable: "%{actor} removed their second factor" |
| 175 | otp_reset: "%{actor} reset the second factor of %{target}" | 175 | otp_reset: "%{actor} reset the second factor of %{target}" |
| 176 | user_create: "%{actor} created the account %{target}" | ||
| 176 | user_deactivate: "%{actor} deactivated %{target}" | 177 | user_deactivate: "%{actor} deactivated %{target}" |
| 177 | user_reactivate: "%{actor} reactivated %{target}" | 178 | user_reactivate: "%{actor} reactivated %{target}" |
| 178 | redaktion_grant: "%{actor} added %{target} to Redaktion" | 179 | redaktion_grant: "%{actor} added %{target} to Redaktion" |
diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 0c517647..a1812cb7 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb | |||
| @@ -285,4 +285,21 @@ class UsersControllerTest < ActionController::TestCase | |||
| 285 | get :new, params: { :locale => "de" } | 285 | get :new, params: { :locale => "de" } |
| 286 | assert_redirected_to new_elevation_path | 286 | assert_redirected_to new_elevation_path |
| 287 | end | 287 | end |
| 288 | |||
| 289 | test "creating a user is witnessed" do | ||
| 290 | login_as :aaron | ||
| 291 | elevate_session! | ||
| 292 | |||
| 293 | assert_difference -> { NodeAction.where(:action => "user_create").count }, 1 do | ||
| 294 | post :create, params: { :locale => "de", :user => { | ||
| 295 | :login => "newcomer", :email => "n@example.org", | ||
| 296 | :password => "secret123", :password_confirmation => "secret123" } } | ||
| 297 | end | ||
| 298 | |||
| 299 | entry = NodeAction.where(:action => "user_create").last | ||
| 300 | assert_equal users(:aaron).id, entry.user_id | ||
| 301 | assert_equal "newcomer", entry.metadata["target_login"] | ||
| 302 | assert_equal User.find_by(:login => "newcomer").id, | ||
| 303 | entry.action_participants.first.subject_id | ||
| 304 | end | ||
| 288 | end | 305 | end |
