99 lines
3.1 KiB
Ruby
99 lines
3.1 KiB
Ruby
|
|
# This module contains the client code for Vanilla jsConnect single sign on
|
||
|
|
# Author:: Todd Burry (mailto:todd@vanillaforums.com)
|
||
|
|
# Version:: 1.0b
|
||
|
|
# Copyright:: Copyright 2008, 2009 Vanilla Forums Inc.
|
||
|
|
# License http://www.opensource.org/licenses/gpl-2.0.php GPLv2
|
||
|
|
|
||
|
|
|
||
|
|
module JsConnect
|
||
|
|
|
||
|
|
@@log = Logging.logger[JsConnect]
|
||
|
|
|
||
|
|
def JsConnect.error(code, message)
|
||
|
|
return {"error" => code, "message" => message}
|
||
|
|
end
|
||
|
|
|
||
|
|
def JsConnect.getJsConnectString(user, request = {}, client_id = "", secret = "", secure = true)
|
||
|
|
error = nil
|
||
|
|
|
||
|
|
timestamp = request["timestamp"].to_i
|
||
|
|
current_timestamp = JsConnect.timestamp
|
||
|
|
|
||
|
|
if secure
|
||
|
|
# Make sure the request coming in is signed properly
|
||
|
|
|
||
|
|
if !request['client_id']
|
||
|
|
error = JsConnect.error('invalid_request', 'The client_id parameter is missing.')
|
||
|
|
elsif request['client_id'] != client_id
|
||
|
|
error = JsConnect.error('invalid_client', "Unknown client #{request['client_id']}.")
|
||
|
|
elsif request['timestamp'].nil? and request['signature'].nil?
|
||
|
|
@@log.debug("no timestamp right? #{request['timestamp']}, #{request['signature']}")
|
||
|
|
if user and !user.empty?
|
||
|
|
error = {'name' => user['name'], 'photourl' => user['photourl']}
|
||
|
|
else
|
||
|
|
error = {'name' => '', 'photourl' => ''}
|
||
|
|
end
|
||
|
|
elsif request['timestamp'].nil?
|
||
|
|
error = JsConnect.error('invalid_request', 'The timestamp is missing or invalid.')
|
||
|
|
elsif !request['signature']
|
||
|
|
error = JsConnect.error('invalid_request', 'The signature is missing.')
|
||
|
|
elsif (current_timestamp - timestamp).abs > 30 * 60
|
||
|
|
error = JsConnect.error('invalid_request', 'The timestamp is invalid.')
|
||
|
|
else
|
||
|
|
# Make sure the timestamp's signature checks out.
|
||
|
|
timestamp_sig = Digest::MD5.hexdigest(timestamp.to_s + secret)
|
||
|
|
if timestamp_sig != request['signature']
|
||
|
|
error = JsConnect.error('access_denied', 'Signature invalid.')
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if error
|
||
|
|
@@log.debug("not valid request: #{error}")
|
||
|
|
result = error
|
||
|
|
elsif user and !user.empty?
|
||
|
|
result = user.clone
|
||
|
|
@@log.debug("logging in: #{error}")
|
||
|
|
JsConnect.signJsConnect(result, client_id, secret, true)
|
||
|
|
else
|
||
|
|
@@log.debug("anonymous")
|
||
|
|
result = {"name" => "", "photourl" => ""}
|
||
|
|
end
|
||
|
|
|
||
|
|
json = ActiveSupport::JSON.encode(result);
|
||
|
|
if request["callback"]
|
||
|
|
return "#{request["callback"]}(#{json});"
|
||
|
|
else
|
||
|
|
return json
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def JsConnect.signJsConnect(data, client_id, secret, set_data = false)
|
||
|
|
# Build the signature string. This is essentially a querystring representation of data, sorted by key
|
||
|
|
keys = data.keys.sort { |a,b| a.downcase <=> b.downcase }
|
||
|
|
|
||
|
|
sig_str = ""
|
||
|
|
|
||
|
|
keys.each do |key|
|
||
|
|
if sig_str.length > 0
|
||
|
|
sig_str += "&"
|
||
|
|
end
|
||
|
|
|
||
|
|
value = data[key]
|
||
|
|
@@log.debug("key #{key}, value #{value}")
|
||
|
|
sig_str += CGI.escape(key) + "=" + CGI.escape(value)
|
||
|
|
end
|
||
|
|
|
||
|
|
signature = Digest::MD5.hexdigest(sig_str + secret);
|
||
|
|
|
||
|
|
if set_data
|
||
|
|
data["clientid"] = client_id
|
||
|
|
data["signature"] = signature
|
||
|
|
end
|
||
|
|
return signature
|
||
|
|
end
|
||
|
|
|
||
|
|
def JsConnect.timestamp
|
||
|
|
return Time.now.to_i
|
||
|
|
end
|
||
|
|
end
|