FEATURE: verbose SSO logging

By enabling the site setting verbose_sso_logging
you can log information every time a user tries initiates SSO
and during SSO failures
This commit is contained in:
Sam 2016-04-08 11:20:01 +10:00
parent 609969bf6f
commit 19ca08857f
5 changed files with 29 additions and 9 deletions

View file

@ -20,7 +20,11 @@ class SessionController < ApplicationController
end end
if SiteSetting.enable_sso? if SiteSetting.enable_sso?
redirect_to DiscourseSingleSignOn.generate_url(return_path) sso = DiscourseSingleSignOn.generate_sso(return_path)
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: Started SSO process\n\n#{sso.diagnostics}")
end
redirect_to sso.to_url
else else
render nothing: true, status: 404 render nothing: true, status: 404
end end
@ -69,10 +73,16 @@ class SessionController < ApplicationController
sso = DiscourseSingleSignOn.parse(request.query_string) sso = DiscourseSingleSignOn.parse(request.query_string)
if !sso.nonce_valid? if !sso.nonce_valid?
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: Nonce has already expired\n\n#{sso.diagnostics}")
end
return render(text: I18n.t("sso.timeout_expired"), status: 419) return render(text: I18n.t("sso.timeout_expired"), status: 419)
end end
if ScreenedIpAddress.should_block?(request.remote_ip) if ScreenedIpAddress.should_block?(request.remote_ip)
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: IP address is blocked #{request.remote_ip}\n\n#{sso.diagnostics}")
end
return render(text: I18n.t("sso.unknown_error"), status: 500) return render(text: I18n.t("sso.unknown_error"), status: 500)
end end
@ -95,6 +105,9 @@ class SessionController < ApplicationController
session["user_created_message"] = activation.message session["user_created_message"] = activation.message
redirect_to users_account_created_path and return redirect_to users_account_created_path and return
else else
if SiteSetting.verbose_sso_logging
Rails.logger.warn("Verbose SSO log: User was logged on #{user.username}\n\n#{sso.diagnostics}")
end
log_on_user user log_on_user user
end end
@ -115,14 +128,9 @@ class SessionController < ApplicationController
rescue ActiveRecord::RecordInvalid => e rescue ActiveRecord::RecordInvalid => e
render text: I18n.t("sso.unknown_error"), status: 500 render text: I18n.t("sso.unknown_error"), status: 500
rescue => e rescue => e
details = {}
SingleSignOn::ACCESSORS.each do |a|
details[a] = sso.send(a)
end
message = "Failed to create or lookup user: #{e}." message = "Failed to create or lookup user: #{e}."
message << "\n\n" << "-" * 100 << "\n\n" message << "\n\n" << "-" * 100 << "\n\n"
message << details.map { |k,v| "#{k}: #{v}" }.join("\n") message << sso.diagnostics
message << "\n\n" << "-" * 100 << "\n\n" message << "\n\n" << "-" * 100 << "\n\n"
message << e.backtrace.join("\n") message << e.backtrace.join("\n")

View file

@ -10,12 +10,16 @@ class DiscourseSingleSignOn < SingleSignOn
SiteSetting.sso_secret SiteSetting.sso_secret
end end
def self.generate_url(return_path="/") def self.generate_sso(return_path="/")
sso = new sso = new
sso.nonce = SecureRandom.hex sso.nonce = SecureRandom.hex
sso.register_nonce(return_path) sso.register_nonce(return_path)
sso.return_sso_url = Discourse.base_url + "/session/sso_login" sso.return_sso_url = Discourse.base_url + "/session/sso_login"
sso.to_url sso
end
def self.generate_url(return_path="/")
generate_sso(return_path).to_url
end end
def register_nonce(return_path) def register_nonce(return_path)

View file

@ -943,6 +943,7 @@ en:
block_common_passwords: "Don't allow passwords that are in the 10,000 most common passwords." block_common_passwords: "Don't allow passwords that are in the 10,000 most common passwords."
enable_sso: "Enable single sign on via an external site (WARNING: USERS' EMAIL ADDRESSES *MUST* BE VALIDATED BY THE EXTERNAL SITE!)" enable_sso: "Enable single sign on via an external site (WARNING: USERS' EMAIL ADDRESSES *MUST* BE VALIDATED BY THE EXTERNAL SITE!)"
verbose_sso_logging: "Log verbose SSO related diagnostics to /logs"
enable_sso_provider: "Implement Discourse SSO provider protocol at the /session/sso_provider endpoint, requires sso_secret to be set" enable_sso_provider: "Implement Discourse SSO provider protocol at the /session/sso_provider endpoint, requires sso_secret to be set"
sso_url: "URL of single sign on endpoint (must include http:// or https://)" sso_url: "URL of single sign on endpoint (must include http:// or https://)"
sso_secret: "Secret string used to cryptographically authenticate SSO information, be sure it is 10 characters or longer" sso_secret: "Secret string used to cryptographically authenticate SSO information, be sure it is 10 characters or longer"

View file

@ -266,6 +266,7 @@ login:
client: true client: true
default: false default: false
enable_sso_provider: false enable_sso_provider: false
verbose_sso_logging: true
sso_url: sso_url:
default: '' default: ''
regex: '^https?:\/\/.+[^\/]$' regex: '^https?:\/\/.+[^\/]$'

View file

@ -55,6 +55,12 @@ class SingleSignOn
sso sso
end end
def diagnostics
SingleSignOn::ACCESSORS.map do |a|
"#{a}: #{sso.send(a)}"
end.join("\n")
end
def sso_secret def sso_secret
@sso_secret || self.class.sso_secret @sso_secret || self.class.sso_secret
end end