FEATURE: SSO to handle return_path automatically

This commit is contained in:
Sam 2014-02-26 09:58:30 +11:00
parent 890d06ac04
commit 440435f023
5 changed files with 20 additions and 16 deletions

View file

@ -15,7 +15,8 @@ Discourse.ApplicationRoute = Em.Route.extend({
bootbox.alert(I18n.t("read_only_mode.login_disabled")); bootbox.alert(I18n.t("read_only_mode.login_disabled"));
} else { } else {
if(Discourse.SiteSettings.enable_sso) { if(Discourse.SiteSettings.enable_sso) {
window.location = Discourse.getURL('/session/sso'); var returnPath = encodeURIComponent(window.location.pathname);
window.location = Discourse.getURL('/session/sso?return_path=' + returnPath);
} else { } else {
Discourse.Route.showModal(this, 'login'); Discourse.Route.showModal(this, 'login');
this.controllerFor('login').resetForm(); this.controllerFor('login').resetForm();

View file

@ -9,7 +9,7 @@ class SessionController < ApplicationController
def sso def sso
if SiteSetting.enable_sso if SiteSetting.enable_sso
redirect_to DiscourseSingleSignOn.generate_url redirect_to DiscourseSingleSignOn.generate_url(params[:return_path] || '/')
else else
render nothing: true, status: 404 render nothing: true, status: 404
end end
@ -27,11 +27,12 @@ class SessionController < ApplicationController
return return
end end
return_path = sso.return_path
sso.expire_nonce! sso.expire_nonce!
if user = sso.lookup_or_create_user if user = sso.lookup_or_create_user
log_on_user user log_on_user user
redirect_to sso.return_url || "/" redirect_to return_path
else else
render text: "unable to log on user", status: 500 render text: "unable to log on user", status: 500
end end

View file

@ -8,17 +8,16 @@ class DiscourseSingleSignOn < SingleSignOn
SiteSetting.sso_secret SiteSetting.sso_secret
end end
def self.generate_url(return_url="/") def self.generate_url(return_path="/")
sso = new sso = new
sso.return_url = return_url
sso.nonce = SecureRandom.hex sso.nonce = SecureRandom.hex
sso.register_nonce sso.register_nonce(return_path)
sso.to_url sso.to_url
end end
def register_nonce def register_nonce(return_path)
if nonce if nonce
$redis.setex(nonce_key, NONCE_EXPIRY_TIME, payload) $redis.setex(nonce_key, NONCE_EXPIRY_TIME, return_path)
end end
end end
@ -26,6 +25,10 @@ class DiscourseSingleSignOn < SingleSignOn
nonce && $redis.get(nonce_key).present? nonce && $redis.get(nonce_key).present?
end end
def return_path
$redis.get(nonce_key) || "/"
end
def expire_nonce! def expire_nonce!
if nonce if nonce
$redis.del nonce_key $redis.del nonce_key

View file

@ -1,5 +1,5 @@
class SingleSignOn class SingleSignOn
ACCESSORS = [:nonce, :return_url, :name, :username, :email, :about_me, :external_id] ACCESSORS = [:nonce, :name, :username, :email, :about_me, :external_id]
FIXNUMS = [] FIXNUMS = []
NONCE_EXPIRY_TIME = 10.minutes NONCE_EXPIRY_TIME = 10.minutes

View file

@ -13,11 +13,11 @@ describe SessionController do
SiteSetting.stubs("sso_secret").returns(@sso_secret) SiteSetting.stubs("sso_secret").returns(@sso_secret)
end end
def get_sso def get_sso(return_path)
nonce = SecureRandom.hex nonce = SecureRandom.hex
dso = DiscourseSingleSignOn.new dso = DiscourseSingleSignOn.new
dso.nonce = nonce dso.nonce = nonce
dso.register_nonce dso.register_nonce(return_path)
sso = SingleSignOn.new sso = SingleSignOn.new
sso.nonce = nonce sso.nonce = nonce
@ -26,7 +26,7 @@ describe SessionController do
end end
it 'can take over an account' do it 'can take over an account' do
sso = get_sso sso = get_sso("/")
user = Fabricate(:user) user = Fabricate(:user)
sso.email = user.email sso.email = user.email
sso.external_id = "abc" sso.external_id = "abc"
@ -41,14 +41,14 @@ describe SessionController do
end end
it 'allows you to create an account' do it 'allows you to create an account' do
sso = get_sso sso = get_sso('/a/')
sso.external_id = '666' # the number of the beast sso.external_id = '666' # the number of the beast
sso.email = 'bob@bob.com' sso.email = 'bob@bob.com'
sso.name = 'Sam Saffron' sso.name = 'Sam Saffron'
sso.username = 'sam' sso.username = 'sam'
get :sso_login, Rack::Utils.parse_query(sso.payload) get :sso_login, Rack::Utils.parse_query(sso.payload)
response.should redirect_to('/') response.should redirect_to('/a/')
logged_on_user = Discourse.current_user_provider.new(request.env).current_user logged_on_user = Discourse.current_user_provider.new(request.env).current_user
@ -61,9 +61,8 @@ describe SessionController do
it 'allows login to existing account with valid nonce' do it 'allows login to existing account with valid nonce' do
sso = get_sso sso = get_sso('/hello/world')
sso.external_id = '997' sso.external_id = '997'
sso.return_url = '/hello/world'
user = Fabricate(:user) user = Fabricate(:user)
user.create_single_sign_on_record(external_id: '997', last_payload: '') user.create_single_sign_on_record(external_id: '997', last_payload: '')