2014-02-24 22:30:49 -05:00
|
|
|
class SingleSignOn
|
2014-04-21 23:52:13 -04:00
|
|
|
ACCESSORS = [:nonce, :name, :username, :email,
|
2014-04-23 19:41:03 -04:00
|
|
|
:about_me, :external_id]
|
2014-02-24 22:30:49 -05:00
|
|
|
FIXNUMS = []
|
|
|
|
NONCE_EXPIRY_TIME = 10.minutes
|
|
|
|
|
|
|
|
attr_accessor(*ACCESSORS)
|
|
|
|
attr_accessor :sso_secret, :sso_url
|
|
|
|
|
|
|
|
def self.sso_secret
|
|
|
|
raise RuntimeError, "sso_secret not implemented on class, be sure to set it on instance"
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.sso_url
|
|
|
|
raise RuntimeError, "sso_url not implemented on class, be sure to set it on instance"
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.parse(payload, sso_secret = nil)
|
|
|
|
sso = new
|
|
|
|
sso.sso_secret = sso_secret if sso_secret
|
|
|
|
|
|
|
|
parsed = Rack::Utils.parse_query(payload)
|
|
|
|
if sso.sign(parsed["sso"]) != parsed["sig"]
|
|
|
|
raise RuntimeError, "Bad signature for payload"
|
|
|
|
end
|
|
|
|
|
|
|
|
decoded = Base64.decode64(parsed["sso"])
|
|
|
|
decoded_hash = Rack::Utils.parse_query(decoded)
|
|
|
|
|
|
|
|
ACCESSORS.each do |k|
|
|
|
|
val = decoded_hash[k.to_s]
|
|
|
|
val = val.to_i if FIXNUMS.include? k
|
|
|
|
sso.send("#{k}=", val)
|
|
|
|
end
|
2014-04-21 23:52:13 -04:00
|
|
|
|
|
|
|
decoded_hash.each do |k,v|
|
|
|
|
# 1234567
|
|
|
|
# custom.
|
|
|
|
#
|
|
|
|
if k[0..6] == "custom."
|
|
|
|
field = k[7..-1]
|
|
|
|
sso.custom_fields[field] = v
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-24 22:30:49 -05:00
|
|
|
sso
|
|
|
|
end
|
|
|
|
|
2014-04-21 23:52:13 -04:00
|
|
|
def sso_secret
|
|
|
|
@sso_secret || self.class.sso_secret
|
|
|
|
end
|
|
|
|
|
|
|
|
def sso_url
|
|
|
|
@sso_url || self.class.sso_url
|
|
|
|
end
|
|
|
|
|
|
|
|
def custom_fields
|
|
|
|
@custom_fields ||= {}
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-02-24 22:30:49 -05:00
|
|
|
def sign(payload)
|
2014-02-25 17:44:41 -05:00
|
|
|
OpenSSL::HMAC.hexdigest("sha256", sso_secret, payload)
|
2014-02-24 22:30:49 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def to_url(base_url=nil)
|
2014-03-19 17:14:09 -04:00
|
|
|
base = "#{base_url || sso_url}"
|
|
|
|
"#{base}#{base.include?('?') ? '&' : '?'}#{payload}"
|
2014-02-24 22:30:49 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def payload
|
|
|
|
payload = Base64.encode64(unsigned_payload)
|
|
|
|
"sso=#{CGI::escape(payload)}&sig=#{sign(payload)}"
|
|
|
|
end
|
|
|
|
|
|
|
|
def unsigned_payload
|
|
|
|
payload = {}
|
|
|
|
ACCESSORS.each do |k|
|
|
|
|
next unless (val = send k)
|
|
|
|
|
|
|
|
payload[k] = val
|
|
|
|
end
|
|
|
|
|
2014-04-21 23:52:13 -04:00
|
|
|
if @custom_fields
|
|
|
|
@custom_fields.each do |k,v|
|
|
|
|
payload["custom.#{k}"] = v.to_s
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-02-24 22:30:49 -05:00
|
|
|
Rack::Utils.build_query(payload)
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|