Class: DiscourseConnectProvider

Inherits:
DiscourseConnectBase show all
Defined in:
lib/discourse_connect_provider.rb

Defined Under Namespace

Classes: BlankReturnUrl, BlankSecret, InvalidParameterValueError

Constant Summary

Constants inherited from DiscourseConnectBase

DiscourseConnectBase::ACCESSORS, DiscourseConnectBase::BOOLS, DiscourseConnectBase::FIXNUMS

Instance Attribute Summary

Attributes inherited from DiscourseConnectBase

#sso_secret, #sso_url

Class Method Summary collapse

Methods inherited from DiscourseConnectBase

#custom_fields, #diagnostics, nonce_expiry_time, nonce_expiry_time=, #payload, #sign, sign, sso_secret, sso_url, #to_h, #to_json, #to_url, #unsigned_payload, used_nonce_expiry_time

Class Method Details

.lookup_return_sso_url(parsed_payload) ⇒ Object



52
53
54
55
56
# File 'lib/discourse_connect_provider.rb', line 52

def self.lookup_return_sso_url(parsed_payload)
  decoded = Base64.decode64(parsed_payload["sso"])
  decoded_hash = Rack::Utils.parse_query(decoded)
  decoded_hash["return_sso_url"]
end

.lookup_sso_secret(return_sso_url, parsed_payload) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/discourse_connect_provider.rb', line 58

def self.lookup_sso_secret(return_sso_url, parsed_payload)
  return nil unless return_sso_url && SiteSetting.enable_discourse_connect_provider

  return_url_host = URI.parse(return_sso_url).host

  provider_secrets =
    SiteSetting
      .discourse_connect_provider_secrets
      .split("\n")
      .map { |row| row.split("|", 2) }
      .sort_by { |k, _| k }
      .reverse

  first_domain_match = nil

  pair =
    provider_secrets.find do |domain, configured_secret|
      if WildcardDomainChecker.check_domain(domain, return_url_host)
        first_domain_match ||= configured_secret
        sign(parsed_payload["sso"], configured_secret) == parsed_payload["sig"]
      end
    end

  # falls back to a secret which will fail to validate in DiscourseConnectBase
  # this ensures error flow is correct
  pair.present? ? pair[1] : first_domain_match
end

.parse(payload, sso_secret = nil, **init_kwargs) ⇒ Object

Raises:



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/discourse_connect_provider.rb', line 16

def self.parse(payload, sso_secret = nil, **init_kwargs)
  # We extract the return_sso_url parameter early; we need the URL's host
  # in order to lookup the correct SSO secret in our site settings.
  parsed_payload = Rack::Utils.parse_query(payload)
  return_sso_url = lookup_return_sso_url(parsed_payload)

  raise ParseError if !return_sso_url

  sso_secret ||= lookup_sso_secret(return_sso_url, parsed_payload)

  if sso_secret.blank?
    begin
      host = URI.parse(return_sso_url).host
      Rails.logger.warn(
        "SSO failed; website #{host} is not in the `discourse_connect_provider_secrets` site settings",
      )
    rescue StandardError => e
      # going for StandardError cause URI::Error may not be enough, eg it parses to something not
      # responding to host
      Discourse.warn_exception(
        e,
        message: "SSO failed; invalid or missing return_sso_url in SSO payload",
      )
    end

    raise BlankSecret
  end

  sso = super(payload, sso_secret, **init_kwargs)

  # Do general parameter validation now, after signature-verification has succeeded.
  raise InvalidParameterValueError.new("prompt") if (sso.prompt != nil) && (sso.prompt != "none")

  sso
end