mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 23:58:31 -05:00
SECURITY: when enabled_local_logins is false users could log in via API
thanks @Nicholas Blanco
This commit is contained in:
parent
1a60969347
commit
be06156629
4 changed files with 60 additions and 50 deletions
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue