From 52cd70dfa3ecf6a3875083c45005f1fdb7916c35 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Mon, 9 Mar 2026 23:44:58 -0700 Subject: [PATCH] fix(super-admin): prefill confirmed_at in new user form (#13662) On self-hosted instances without email configured, users created from Super Admin can get stuck in an unconfirmed state. This PR implements the default at the Super Admin frontend form layer, not in backend creation logic. What changed: - Added a custom `ConfirmedAtField` for Super Admin user forms. - Prefills `confirmed_at` with current time on the **New User** form (`GET /super_admin/users/new`). - Kept backend create behavior unchanged (`resource_class.new(resource_params)`), so API/manual payloads still behave normally. Behavior: - In Super Admin UI, `confirmed_at` is prefilled by default. - If someone wants an unconfirmed user, they can clear the `confirmed_at` field before saving. - If `confirmed_at` is omitted from payload entirely, the created user remains unconfirmed. Scope note: external signup flows are intentionally unchanged in this PR (`/api/v1/accounts`, `/api/v2/accounts`, and social/omniauth signup behavior are not modified). ## Demo https://github.com/user-attachments/assets/436abbb0-d4cf-49a6-a1b8-4b6aa85aa09f --- app/dashboards/user_dashboard.rb | 2 +- app/fields/confirmed_at_field.rb | 4 ++ .../fields/confirmed_at_field/_form.html.erb | 8 ++++ .../super_admin/users_controller_spec.rb | 47 +++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 app/fields/confirmed_at_field.rb create mode 100644 app/views/fields/confirmed_at_field/_form.html.erb diff --git a/app/dashboards/user_dashboard.rb b/app/dashboards/user_dashboard.rb index 753b617ef..b499193cc 100644 --- a/app/dashboards/user_dashboard.rb +++ b/app/dashboards/user_dashboard.rb @@ -25,7 +25,7 @@ class UserDashboard < Administrate::BaseDashboard current_sign_in_ip: Field::String, last_sign_in_ip: Field::String, confirmation_token: Field::String, - confirmed_at: Field::DateTime, + confirmed_at: ConfirmedAtField, confirmation_sent_at: Field::DateTime, unconfirmed_email: Field::String, name: Field::String.with_options(searchable: true), diff --git a/app/fields/confirmed_at_field.rb b/app/fields/confirmed_at_field.rb new file mode 100644 index 000000000..67e04c4a0 --- /dev/null +++ b/app/fields/confirmed_at_field.rb @@ -0,0 +1,4 @@ +require 'administrate/field/base' + +class ConfirmedAtField < Administrate::Field::DateTime +end diff --git a/app/views/fields/confirmed_at_field/_form.html.erb b/app/views/fields/confirmed_at_field/_form.html.erb new file mode 100644 index 000000000..9d4c3029c --- /dev/null +++ b/app/views/fields/confirmed_at_field/_form.html.erb @@ -0,0 +1,8 @@ +
+ <%= f.label field.attribute %> +
+
+ <% value = field.data %> + <% value = Time.current if value.blank? && action_name == 'new' %> + <%= f.datetime_local_field field.attribute, step: 1, value: value %> +
diff --git a/spec/controllers/super_admin/users_controller_spec.rb b/spec/controllers/super_admin/users_controller_spec.rb index e1461f554..724e4fa91 100644 --- a/spec/controllers/super_admin/users_controller_spec.rb +++ b/spec/controllers/super_admin/users_controller_spec.rb @@ -23,6 +23,25 @@ RSpec.describe 'Super Admin Users API', type: :request do type: 'SuperAdmin' } } end + let!(:params_without_confirmed_at) do + { user: { + name: 'agent@example.com', + display_name: 'agent@example.com', + email: 'agent@example.com', + password: 'Password1!', + type: 'SuperAdmin' + } } + end + let!(:params_with_blank_confirmed_at) do + { user: { + name: 'agent-2@example.com', + display_name: 'agent-2@example.com', + email: 'agent-2@example.com', + password: 'Password1!', + confirmed_at: '', + type: 'SuperAdmin' + } } + end it 'shows the list of users' do sign_in(super_admin, scope: :super_admin) @@ -36,6 +55,16 @@ RSpec.describe 'Super Admin Users API', type: :request do expect(header_texts).not_to include('MFA') end + it 'prefills confirmed_at on new user form' do + sign_in(super_admin, scope: :super_admin) + get '/super_admin/users/new' + + expect(response).to have_http_status(:success) + expect(response.body).to include('name="user[confirmed_at]"') + confirmed_at_value = response.body[/name="user\[confirmed_at\]".*?value="([^"]+)"/m, 1] + expect(confirmed_at_value).to be_present + end + it 'creates the new super_admin record' do sign_in(super_admin, scope: :super_admin) @@ -47,6 +76,24 @@ RSpec.describe 'Super Admin Users API', type: :request do post '/super_admin/users', params: params expect(response).to redirect_to('http://www.example.com/super_admin/users/new') end + + it 'creates unconfirmed users when confirmed_at is not provided in payload' do + sign_in(super_admin, scope: :super_admin) + + post '/super_admin/users', params: params_without_confirmed_at + + expect(response).to redirect_to("http://www.example.com/super_admin/users/#{User.last.id}") + expect(User.last).not_to be_confirmed + end + + it 'creates unconfirmed users when confirmed_at is explicitly cleared' do + sign_in(super_admin, scope: :super_admin) + + post '/super_admin/users', params: params_with_blank_confirmed_at + + expect(response).to redirect_to("http://www.example.com/super_admin/users/#{User.last.id}") + expect(User.last).not_to be_confirmed + end end end