admin: invite links without a username
All checks were successful
Build and Deploy to Production / test (push) Successful in 25s
Build and Deploy to Production / build (push) Successful in 1m2s
Build and Deploy to Production / deploy (push) Successful in 38s

Leaving the username empty when creating a user makes the account
invite-<hex>; the invitee picks the real username on the invite page
(taken names and the invite- prefix are refused). Without an email this
gives a plain invite link to share.

Assisted-by: Claude:opus-5
This commit is contained in:
Sergei Poljanski 2026-10-10 09:12:21 +04:00
commit 78ea9926e7
Signed by: asxpi
GPG key ID: 4F8851660FA4121B
6 changed files with 57 additions and 4 deletions

View file

@ -47,6 +47,31 @@ class UsersAdminTest < Minitest::Test
refute_includes last_response.body, '/admin/invite/' # shown once
end
def test_invite_without_username_lets_invitee_choose
admin_login!
create_user(username: '', perm: { 'bingo' => { 'read' => '1' } })
u = User.where(choose_username: true).first
assert_match(/\Ainvite-[0-9a-f]{8}\z/, u.username)
get '/admin/users'
link = last_response.body[%r{https?://\S+?/admin/invite/[\w-]+}]
assert link, 'invite link shown'
path = URI(link).path
clear_cookies
get path
assert_includes last_response.body, 'Choose a username'
tok = last_response.body[/name="authenticity_token" value="([^"]+)"/, 1]
post path, { authenticity_token: tok, username: ENV['ADMIN_USER'], password: 'kate-password-12', confirm: 'kate-password-12' }
assert_equal 422, last_response.status # taken
post path, { authenticity_token: tok, username: 'invite-x', password: 'kate-password-12', confirm: 'kate-password-12' }
assert_equal 422, last_response.status # reserved prefix
post path, { authenticity_token: tok, username: 'Kate', password: 'kate-password-12', confirm: 'kate-password-12' }
assert_equal 302, last_response.status
u.refresh
assert_equal 'kate', u.username
refute u.choose_username
assert u.can?('bingo', 'read')
end
def test_delete_implies_read
admin_login!
create_user(username: 'dave', perm: { 'bingo' => { 'delete' => '1' } })