FIX: Improve protection against problematic usernames (#8097)
This commit is contained in:
parent
98719bee10
commit
1576b07a10
|
@ -141,6 +141,10 @@ class UsersController < ApplicationController
|
||||||
def username
|
def username
|
||||||
params.require(:new_username)
|
params.require(:new_username)
|
||||||
|
|
||||||
|
if clashing_with_existing_route?(params[:new_username]) || User.reserved_username?(params[:new_username])
|
||||||
|
return render_json_error(I18n.t("login.reserved_username"))
|
||||||
|
end
|
||||||
|
|
||||||
user = fetch_user_from_params
|
user = fetch_user_from_params
|
||||||
guardian.ensure_can_edit_username!(user)
|
guardian.ensure_can_edit_username!(user)
|
||||||
|
|
||||||
|
@ -359,7 +363,7 @@ class UsersController < ApplicationController
|
||||||
return fail_with("login.email_too_long")
|
return fail_with("login.email_too_long")
|
||||||
end
|
end
|
||||||
|
|
||||||
if User.reserved_username?(params[:username])
|
if clashing_with_existing_route?(params[:username]) || User.reserved_username?(params[:username])
|
||||||
return fail_with("login.reserved_username")
|
return fail_with("login.reserved_username")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1355,4 +1359,18 @@ class UsersController < ApplicationController
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def clashing_with_existing_route?(username)
|
||||||
|
normalized_username = User.normalize_username(username)
|
||||||
|
http_verbs = %w[GET POST PUT DELETE PATCH]
|
||||||
|
allowed_actions = %w[show update destroy]
|
||||||
|
|
||||||
|
http_verbs.any? do |verb|
|
||||||
|
begin
|
||||||
|
path = Rails.application.routes.recognize_path("/u/#{normalized_username}", method: verb)
|
||||||
|
allowed_actions.exclude?(path[:action])
|
||||||
|
rescue ActionController::RoutingError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -487,7 +487,7 @@ users:
|
||||||
reserved_usernames:
|
reserved_usernames:
|
||||||
type: list
|
type: list
|
||||||
list_type: compact
|
list_type: compact
|
||||||
default: "admin|moderator|administrator|mod|sys|system|community|info|you|name|username|user|nickname|discourse|discourseorg|discourseforum|support|hp|account-created|password-reset|admin-login|confirm-admin|account-created|activate-account|confirm-email-token|authorize-email"
|
default: "admin|moderator|administrator|mod|sys|system|community|info|you|name|username|user|nickname|discourse|discourseorg|discourseforum|support|hp"
|
||||||
min_password_length:
|
min_password_length:
|
||||||
client: true
|
client: true
|
||||||
default: 10
|
default: 10
|
||||||
|
|
|
@ -1005,11 +1005,16 @@ describe UsersController do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with a reserved username' do
|
context 'with a reserved username' do
|
||||||
let(:create_params) { { name: @user.name, username: 'Reserved', email: @user.email, password: "x" * 20 } }
|
let(:create_params) { { name: @user.name, username: 'Reserved', email: @user.email, password: 'strongpassword' } }
|
||||||
before { SiteSetting.reserved_usernames = 'a|reserved|b' }
|
before { SiteSetting.reserved_usernames = 'a|reserved|b' }
|
||||||
include_examples 'failed signup'
|
include_examples 'failed signup'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'with a username that matches a user route' do
|
||||||
|
let(:create_params) { { name: @user.name, username: 'account-created', email: @user.email, password: 'strongpassword' } }
|
||||||
|
include_examples 'failed signup'
|
||||||
|
end
|
||||||
|
|
||||||
context 'with a missing username' do
|
context 'with a missing username' do
|
||||||
let(:create_params) { { name: @user.name, email: @user.email, password: "x" * 20 } }
|
let(:create_params) { { name: @user.name, email: @user.email, password: "x" * 20 } }
|
||||||
|
|
||||||
|
@ -1197,6 +1202,23 @@ describe UsersController do
|
||||||
expect(user.reload.username).to eq(new_username)
|
expect(user.reload.username).to eq(new_username)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'raises an error when the username clashes with an existing user route' do
|
||||||
|
put "/u/#{user.username}/preferences/username.json", params: { new_username: 'account-created' }
|
||||||
|
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body['errors'].first).to include(I18n.t('login.reserved_username'))
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error when the username is in the reserved list' do
|
||||||
|
SiteSetting.reserved_usernames = 'reserved'
|
||||||
|
|
||||||
|
put "/u/#{user.username}/preferences/username.json", params: { new_username: 'reserved' }
|
||||||
|
body = JSON.parse(response.body)
|
||||||
|
|
||||||
|
expect(body['errors'].first).to include(I18n.t('login.reserved_username'))
|
||||||
|
end
|
||||||
|
|
||||||
it 'should fail if the user is old' do
|
it 'should fail if the user is old' do
|
||||||
# Older than the change period and >1 post
|
# Older than the change period and >1 post
|
||||||
user.created_at = Time.now - (SiteSetting.username_change_period + 1).days
|
user.created_at = Time.now - (SiteSetting.username_change_period + 1).days
|
||||||
|
|
Loading…
Reference in New Issue