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
172 lines
6.1 KiB
Ruby
172 lines
6.1 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe 'Super Admin Users API', type: :request do
|
|
let(:super_admin) { create(:super_admin) }
|
|
|
|
describe 'GET /super_admin/users' do
|
|
context 'when it is an unauthenticated super admin' do
|
|
it 'returns unauthorized' do
|
|
get '/super_admin/users'
|
|
expect(response).to have_http_status(:redirect)
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated super admin' do
|
|
let!(:user) { create(:user, name: 'Disabled User') }
|
|
let!(:params) do
|
|
{ user: {
|
|
name: 'admin@example.com',
|
|
display_name: 'admin@example.com',
|
|
email: 'admin@example.com',
|
|
password: 'Password1!',
|
|
confirmed_at: '2023-03-20 22:32:41',
|
|
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)
|
|
get '/super_admin/users'
|
|
doc = Nokogiri::HTML(response.body)
|
|
header_texts = doc.css('table thead th').map { |header| header.text.squish }
|
|
|
|
expect(response).to have_http_status(:success)
|
|
expect(response.body).to include('New user')
|
|
expect(response.body).to include(CGI.escapeHTML(user.name))
|
|
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)
|
|
|
|
post '/super_admin/users', params: params
|
|
|
|
expect(response).to redirect_to("http://www.example.com/super_admin/users/#{User.last.id}")
|
|
expect(SuperAdmin.last.email).to eq('admin@example.com')
|
|
|
|
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
|
|
|
|
describe 'DELETE /super_admin/users/:id/avatar' do
|
|
let!(:user) { create(:user, :with_avatar) }
|
|
|
|
context 'when it is an unauthenticated super admin' do
|
|
it 'returns unauthorized' do
|
|
delete "/super_admin/users/#{user.id}/avatar", params: { attachment_id: user.avatar.id }
|
|
expect(response).to have_http_status(:redirect)
|
|
expect(user.reload.avatar).to be_attached
|
|
end
|
|
end
|
|
|
|
context 'when it is an authenticated super admin' do
|
|
it 'destroys the avatar' do
|
|
sign_in(super_admin, scope: :super_admin)
|
|
delete "/super_admin/users/#{user.id}/avatar", params: { attachment_id: user.avatar.id }
|
|
expect(response).to have_http_status(:redirect)
|
|
expect(user.reload.avatar).not_to be_attached
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'PATCH /super_admin/users/:id' do
|
|
let!(:user) { create(:user) }
|
|
let(:request_path) { "/super_admin/users/#{user.id}" }
|
|
|
|
before { sign_in(super_admin, scope: :super_admin) }
|
|
|
|
it 'skips reconfirmation when confirmed_at is provided' do
|
|
ActiveJob::Base.queue_adapter.enqueued_jobs.clear
|
|
patch request_path, params: { user: { email: 'updated@example.com', confirmed_at: Time.current } }
|
|
|
|
expect(response).to have_http_status(:redirect)
|
|
expect(user.reload.email).to eq('updated@example.com')
|
|
expect(user.reload.unconfirmed_email).to be_nil
|
|
|
|
mail_jobs = ActiveJob::Base.queue_adapter.enqueued_jobs.select do |job|
|
|
job[:job].to_s == 'ActionMailer::MailDeliveryJob'
|
|
end
|
|
expect(mail_jobs.count).to eq(0)
|
|
end
|
|
|
|
it 'does not skip reconfirmation when confirmed_at is blank' do
|
|
ActiveJob::Base.queue_adapter.enqueued_jobs.clear
|
|
patch request_path, params: { user: { email: 'updated-again@example.com' } }
|
|
|
|
expect(response).to have_http_status(:redirect)
|
|
expect(user.reload.unconfirmed_email).to eq('updated-again@example.com')
|
|
|
|
mail_jobs = ActiveJob::Base.queue_adapter.enqueued_jobs.select do |job|
|
|
job[:job].to_s == 'ActionMailer::MailDeliveryJob'
|
|
end
|
|
expect(mail_jobs.count).to be >= 1
|
|
end
|
|
end
|
|
|
|
describe 'GET /super_admin/users/:id' do
|
|
let!(:user) { create(:user, name: 'MFA Enabled User', otp_required_for_login: true) }
|
|
|
|
it 'shows the MFA status on the user detail page' do
|
|
sign_in(super_admin, scope: :super_admin)
|
|
|
|
get "/super_admin/users/#{user.id}"
|
|
doc = Nokogiri::HTML(response.body)
|
|
labels = doc.css('dt.attribute-label').map { |label| label.text.squish }
|
|
|
|
expect(response).to have_http_status(:success)
|
|
expect(labels).to include('MFA')
|
|
expect(response.body).to include('Enabled')
|
|
expect(response.body).to include(CGI.escapeHTML(user.name))
|
|
end
|
|
end
|
|
end
|