SECURITY: when enabled_local_logins is false users could log in via API

thanks @Nicholas Blanco
This commit is contained in:
Sam 2014-03-26 15:39:44 +11:00
parent 1a60969347
commit be06156629
4 changed files with 60 additions and 50 deletions

View File

@ -44,7 +44,7 @@ class SessionController < ApplicationController
def create def create
if SiteSetting.enable_sso unless allow_local_auth?
render nothing: true, status: 500 render nothing: true, status: 500
return return
end end
@ -88,7 +88,7 @@ class SessionController < ApplicationController
def forgot_password def forgot_password
params.require(:login) params.require(:login)
if SiteSetting.enable_sso unless allow_local_auth?
render nothing: true, status: 500 render nothing: true, status: 500
return return
end end
@ -118,6 +118,10 @@ class SessionController < ApplicationController
private private
def allow_local_auth?
!SiteSetting.enable_sso && SiteSetting.enable_local_logins
end
def login_not_approved_for?(user) def login_not_approved_for?(user)
SiteSetting.must_approve_users? && !user.approved? && !user.admin? SiteSetting.must_approve_users? && !user.approved? && !user.admin?
end end

View File

@ -123,6 +123,12 @@ class UsersController < ApplicationController
user = User.new(user_params) user = User.new(user_params)
authentication = UserAuthenticator.new(user, session) authentication = UserAuthenticator.new(user, session)
if !authentication.has_authenticator? && !SiteSetting.enable_local_logins
render nothing: true, status: 500
return
end
authentication.start authentication.start
activation = UserActivator.new(user, request, session, cookies) activation = UserActivator.new(user, request, session, cookies)

View File

@ -90,78 +90,72 @@ describe SessionController do
get :sso_login, Rack::Utils.parse_query(sso.payload) get :sso_login, Rack::Utils.parse_query(sso.payload)
response.code.should == '500' response.code.should == '500'
end end
describe 'local attribute ovveride from SSO payload' do describe 'local attribute override from SSO payload' do
before do before do
SiteSetting.stubs("sso_overrides_email").returns(true) SiteSetting.stubs("sso_overrides_email").returns(true)
SiteSetting.stubs("sso_overrides_username").returns(true) SiteSetting.stubs("sso_overrides_username").returns(true)
SiteSetting.stubs("sso_overrides_name").returns(true) SiteSetting.stubs("sso_overrides_name").returns(true)
@user = Fabricate(:user) @user = Fabricate(:user)
@sso = get_sso('/hello/world') @sso = get_sso('/hello/world')
@sso.external_id = '997' @sso.external_id = '997'
@reversed_username = @user.username.reverse @reversed_username = @user.username.reverse
@sso.username = @reversed_username @sso.username = @reversed_username
@sso.email = "#{@reversed_username}@garbage.org" @sso.email = "#{@reversed_username}@garbage.org"
@reversed_name = @user.name.reverse @reversed_name = @user.name.reverse
@sso.name = @reversed_name @sso.name = @reversed_name
@suggested_username = UserNameSuggester.suggest(@sso.username || @sso.name || @sso.email) @suggested_username = UserNameSuggester.suggest(@sso.username || @sso.name || @sso.email)
@suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email) @suggested_name = User.suggest_name(@sso.name || @sso.username || @sso.email)
@user.create_single_sign_on_record(external_id: '997', last_payload: '') @user.create_single_sign_on_record(external_id: '997', last_payload: '')
end end
it 'stores the external attributes' do it 'stores the external attributes' do
get :sso_login, Rack::Utils.parse_query(@sso.payload) get :sso_login, Rack::Utils.parse_query(@sso.payload)
@user.single_sign_on_record.reload @user.single_sign_on_record.reload
@user.single_sign_on_record.external_username.should == @sso.username @user.single_sign_on_record.external_username.should == @sso.username
@user.single_sign_on_record.external_email.should == @sso.email @user.single_sign_on_record.external_email.should == @sso.email
@user.single_sign_on_record.external_name.should == @sso.name @user.single_sign_on_record.external_name.should == @sso.name
end end
it 'overrides attributes' do it 'overrides attributes' do
get :sso_login, Rack::Utils.parse_query(@sso.payload) get :sso_login, Rack::Utils.parse_query(@sso.payload)
logged_on_user = Discourse.current_user_provider.new(request.env).current_user logged_on_user = Discourse.current_user_provider.new(request.env).current_user
logged_on_user.username.should == @suggested_username logged_on_user.username.should == @suggested_username
logged_on_user.email.should == "#{@reversed_username}@garbage.org" logged_on_user.email.should == "#{@reversed_username}@garbage.org"
logged_on_user.name.should == @suggested_name logged_on_user.name.should == @suggested_name
end end
it 'does not change matching attributes for an existing account' do it 'does not change matching attributes for an existing account' do
@sso.username = @user.username @sso.username = @user.username
@sso.name = @user.name @sso.name = @user.name
@sso.email = @user.email @sso.email = @user.email
get :sso_login, Rack::Utils.parse_query(@sso.payload) get :sso_login, Rack::Utils.parse_query(@sso.payload)
logged_on_user = Discourse.current_user_provider.new(request.env).current_user logged_on_user = Discourse.current_user_provider.new(request.env).current_user
logged_on_user.username.should == @user.username logged_on_user.username.should == @user.username
logged_on_user.name.should == @user.name logged_on_user.name.should == @user.name
logged_on_user.email.should == @user.email logged_on_user.email.should == @user.email
end end
it 'does not change attributes for unchanged external attributes' do it 'does not change attributes for unchanged external attributes' do
@user.single_sign_on_record.external_username = @sso.username @user.single_sign_on_record.external_username = @sso.username
@user.single_sign_on_record.external_email = @sso.email @user.single_sign_on_record.external_email = @sso.email
@user.single_sign_on_record.external_name = @sso.name @user.single_sign_on_record.external_name = @sso.name
@user.single_sign_on_record.save @user.single_sign_on_record.save
get :sso_login, Rack::Utils.parse_query(@sso.payload) get :sso_login, Rack::Utils.parse_query(@sso.payload)
logged_on_user = Discourse.current_user_provider.new(request.env).current_user logged_on_user = Discourse.current_user_provider.new(request.env).current_user
logged_on_user.username.should == @user.username logged_on_user.username.should == @user.username
logged_on_user.email.should == @user.email logged_on_user.email.should == @user.email
logged_on_user.name.should == @user.name logged_on_user.name.should == @user.name
end end
end end
end end
describe '.create' do describe '.create' do
@ -195,24 +189,25 @@ describe SessionController do
end end
describe 'success by username' do describe 'success by username' do
before do it 'logs in correctly' do
xhr :post, :create, login: user.username, password: 'myawesomepassword' xhr :post, :create, login: user.username, password: 'myawesomepassword'
user.reload user.reload
end
it 'sets a session id' do
session[:current_user_id].should == user.id session[:current_user_id].should == user.id
end
it 'gives the user an auth token' do
user.auth_token.should be_present user.auth_token.should be_present
end
it 'sets a cookie with the auth token' do
cookies[:_t].should == user.auth_token cookies[:_t].should == user.auth_token
end end
end end
describe 'local logins disabled' do
it 'fails' do
SiteSetting.stubs(:enable_local_logins).returns(false)
xhr :post, :create, login: user.username, password: 'myawesomepassword'
response.status.to_i.should == 500
end
end
describe 'strips leading @ symbol' do describe 'strips leading @ symbol' do
before do before do
xhr :post, :create, login: "@" + user.username, password: 'myawesomepassword' xhr :post, :create, login: "@" + user.username, password: 'myawesomepassword'
@ -349,6 +344,12 @@ describe SessionController do
context 'for an existing username' do context 'for an existing username' do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
it "returns a 500 if local logins are disabled" do
SiteSetting.stubs(:enable_local_logins).returns(false)
xhr :post, :forgot_password, login: user.username
response.code.to_i.should == 500
end
it "generates a new token for a made up username" do it "generates a new token for a made up username" do
lambda { xhr :post, :forgot_password, login: user.username}.should change(EmailToken, :count) lambda { xhr :post, :forgot_password, login: user.username}.should change(EmailToken, :count)
end end

View File

@ -115,7 +115,7 @@ describe UsersController do
end end
context 'reponse' do context 'response' do
before do before do
Guardian.any_instance.expects(:can_access_forum?).returns(true) Guardian.any_instance.expects(:can_access_forum?).returns(true)
EmailToken.expects(:confirm).with('asdfasdf').returns(user) EmailToken.expects(:confirm).with('asdfasdf').returns(user)
@ -295,18 +295,20 @@ describe UsersController do
end end
context 'when creating a non active user (unconfirmed email)' do context 'when creating a non active user (unconfirmed email)' do
it 'enqueues a signup email' do
it 'returns a 500 when local logins are disabled' do
SiteSetting.expects(:enable_local_logins).returns(false)
post_user
expect(response.status).to eq(500)
end
it 'creates a user correctly' do
Jobs.expects(:enqueue).with(:user_email, has_entries(type: :signup)) Jobs.expects(:enqueue).with(:user_email, has_entries(type: :signup))
post_user
end
it 'does not enqueue a welcome email' do
User.any_instance.expects(:enqueue_welcome_message).with('welcome_user').never User.any_instance.expects(:enqueue_welcome_message).with('welcome_user').never
post_user
end
it 'indicates the user is not active in the response' do
post_user post_user
expect(JSON.parse(response.body)['active']).to be_false expect(JSON.parse(response.body)['active']).to be_false
end end
@ -1199,7 +1201,7 @@ describe UsersController do
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background"
response.status.should eq 422 response.status.should eq 422
end end
it 'rejects requests with unknown user_image_type' do it 'rejects requests with unknown user_image_type' do
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "asdf" xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "asdf"
response.status.should eq 422 response.status.should eq 422
@ -1224,22 +1226,20 @@ describe UsersController do
json['width'].should == 100 json['width'].should == 100
json['height'].should == 200 json['height'].should == 200
end end
it 'is successful for profile backgrounds' do it 'is successful for profile backgrounds' do
upload = Fabricate(:upload) upload = Fabricate(:upload)
Upload.expects(:create_for).returns(upload) Upload.expects(:create_for).returns(upload)
xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background" xhr :post, :upload_user_image, username: user.username, file: user_image_url, user_image_type: "profile_background"
user.reload user.reload
user.profile_background.should == "/uploads/default/1/1234567890123456.jpg" user.profile_background.should == "/uploads/default/1/1234567890123456.jpg"
# returns the url, width and height of the uploaded image # returns the url, width and height of the uploaded image
json = JSON.parse(response.body) json = JSON.parse(response.body)
json['url'].should == "/uploads/default/1/1234567890123456.jpg" json['url'].should == "/uploads/default/1/1234567890123456.jpg"
json['width'].should == 100 json['width'].should == 100
json['height'].should == 200 json['height'].should == 200
end end
end end
it "should handle malformed urls" do it "should handle malformed urls" do
@ -1282,13 +1282,13 @@ describe UsersController do
end end
end end
describe '.clear_profile_background' do describe '.clear_profile_background' do
it 'raises an error when not logged in' do it 'raises an error when not logged in' do
lambda { xhr :put, :clear_profile_background, username: 'asdf' }.should raise_error(Discourse::NotLoggedIn) lambda { xhr :put, :clear_profile_background, username: 'asdf' }.should raise_error(Discourse::NotLoggedIn)
end end
context 'while logged in' do context 'while logged in' do
let!(:user) { log_in } let!(:user) { log_in }
@ -1306,7 +1306,6 @@ describe UsersController do
end end
end end
end end
describe '.destroy' do describe '.destroy' do