FIX: Improve protection against problematic usernames (#8097)

This commit is contained in:
Roman Rizzi 2019-09-13 15:11:27 -03:00 committed by GitHub
parent 98719bee10
commit 1576b07a10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 43 additions and 3 deletions

View File

@ -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

View File

@ -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

View File

@ -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