To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / .svn / pristine / a4 / a494e5a07ce50a0c4f746379917023ec9e432959.svn-base @ 1297:0a574315af3e
History | View | Annotate | Download (7.93 KB)
| 1 |
require 'uri' |
|---|---|
| 2 |
require 'openid/extensions/sreg' |
| 3 |
require 'openid/extensions/ax' |
| 4 |
require 'openid/store/filesystem' |
| 5 |
|
| 6 |
require File.dirname(__FILE__) + '/open_id_authentication/db_store' |
| 7 |
require File.dirname(__FILE__) + '/open_id_authentication/mem_cache_store' |
| 8 |
require File.dirname(__FILE__) + '/open_id_authentication/request' |
| 9 |
require File.dirname(__FILE__) + '/open_id_authentication/timeout_fixes' if OpenID::VERSION == "2.0.4" |
| 10 |
|
| 11 |
module OpenIdAuthentication |
| 12 |
OPEN_ID_AUTHENTICATION_DIR = RAILS_ROOT + "/tmp/openids" |
| 13 |
|
| 14 |
def self.store |
| 15 |
@@store |
| 16 |
end |
| 17 |
|
| 18 |
def self.store=(*store_option) |
| 19 |
store, *parameters = *([ store_option ].flatten) |
| 20 |
|
| 21 |
@@store = case store |
| 22 |
when :db |
| 23 |
OpenIdAuthentication::DbStore.new |
| 24 |
when :mem_cache |
| 25 |
OpenIdAuthentication::MemCacheStore.new(*parameters) |
| 26 |
when :file |
| 27 |
OpenID::Store::Filesystem.new(OPEN_ID_AUTHENTICATION_DIR) |
| 28 |
else |
| 29 |
raise "Unknown store: #{store}"
|
| 30 |
end |
| 31 |
end |
| 32 |
|
| 33 |
self.store = :db |
| 34 |
|
| 35 |
class InvalidOpenId < StandardError |
| 36 |
end |
| 37 |
|
| 38 |
class Result |
| 39 |
ERROR_MESSAGES = {
|
| 40 |
:missing => "Sorry, the OpenID server couldn't be found", |
| 41 |
:invalid => "Sorry, but this does not appear to be a valid OpenID", |
| 42 |
:canceled => "OpenID verification was canceled", |
| 43 |
:failed => "OpenID verification failed", |
| 44 |
:setup_needed => "OpenID verification needs setup" |
| 45 |
} |
| 46 |
|
| 47 |
def self.[](code) |
| 48 |
new(code) |
| 49 |
end |
| 50 |
|
| 51 |
def initialize(code) |
| 52 |
@code = code |
| 53 |
end |
| 54 |
|
| 55 |
def status |
| 56 |
@code |
| 57 |
end |
| 58 |
|
| 59 |
ERROR_MESSAGES.keys.each { |state| define_method("#{state}?") { @code == state } }
|
| 60 |
|
| 61 |
def successful? |
| 62 |
@code == :successful |
| 63 |
end |
| 64 |
|
| 65 |
def unsuccessful? |
| 66 |
ERROR_MESSAGES.keys.include?(@code) |
| 67 |
end |
| 68 |
|
| 69 |
def message |
| 70 |
ERROR_MESSAGES[@code] |
| 71 |
end |
| 72 |
end |
| 73 |
|
| 74 |
# normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization |
| 75 |
def self.normalize_identifier(identifier) |
| 76 |
# clean up whitespace |
| 77 |
identifier = identifier.to_s.strip |
| 78 |
|
| 79 |
# if an XRI has a prefix, strip it. |
| 80 |
identifier.gsub!(/xri:\/\//i, '') |
| 81 |
|
| 82 |
# dodge XRIs -- TODO: validate, don't just skip. |
| 83 |
unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
|
| 84 |
# does it begin with http? if not, add it. |
| 85 |
identifier = "http://#{identifier}" unless identifier =~ /^http/i
|
| 86 |
|
| 87 |
# strip any fragments |
| 88 |
identifier.gsub!(/\#(.*)$/, '') |
| 89 |
|
| 90 |
begin |
| 91 |
uri = URI.parse(identifier) |
| 92 |
uri.scheme = uri.scheme.downcase if uri.scheme # URI should do this |
| 93 |
identifier = uri.normalize.to_s |
| 94 |
rescue URI::InvalidURIError |
| 95 |
raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
|
| 96 |
end |
| 97 |
end |
| 98 |
|
| 99 |
return identifier |
| 100 |
end |
| 101 |
|
| 102 |
# deprecated for OpenID 2.0, where not all OpenIDs are URLs |
| 103 |
def self.normalize_url(url) |
| 104 |
ActiveSupport::Deprecation.warn "normalize_url has been deprecated, use normalize_identifier instead" |
| 105 |
self.normalize_identifier(url) |
| 106 |
end |
| 107 |
|
| 108 |
protected |
| 109 |
def normalize_url(url) |
| 110 |
OpenIdAuthentication.normalize_url(url) |
| 111 |
end |
| 112 |
|
| 113 |
def normalize_identifier(url) |
| 114 |
OpenIdAuthentication.normalize_identifier(url) |
| 115 |
end |
| 116 |
|
| 117 |
# The parameter name of "openid_identifier" is used rather than the Rails convention "open_id_identifier" |
| 118 |
# because that's what the specification dictates in order to get browser auto-complete working across sites |
| 119 |
def using_open_id?(identity_url = nil) #:doc: |
| 120 |
identity_url ||= params[:openid_identifier] || params[:openid_url] |
| 121 |
!identity_url.blank? || params[:open_id_complete] |
| 122 |
end |
| 123 |
|
| 124 |
def authenticate_with_open_id(identity_url = nil, options = {}, &block) #:doc:
|
| 125 |
identity_url ||= params[:openid_identifier] || params[:openid_url] |
| 126 |
|
| 127 |
if params[:open_id_complete].nil? |
| 128 |
begin_open_id_authentication(identity_url, options, &block) |
| 129 |
else |
| 130 |
complete_open_id_authentication(&block) |
| 131 |
end |
| 132 |
end |
| 133 |
|
| 134 |
private |
| 135 |
def begin_open_id_authentication(identity_url, options = {})
|
| 136 |
identity_url = normalize_identifier(identity_url) |
| 137 |
return_to = options.delete(:return_to) |
| 138 |
method = options.delete(:method) |
| 139 |
|
| 140 |
options[:required] ||= [] # reduces validation later |
| 141 |
options[:optional] ||= [] |
| 142 |
|
| 143 |
open_id_request = open_id_consumer.begin(identity_url) |
| 144 |
add_simple_registration_fields(open_id_request, options) |
| 145 |
add_ax_fields(open_id_request, options) |
| 146 |
redirect_to(open_id_redirect_url(open_id_request, return_to, method)) |
| 147 |
rescue OpenIdAuthentication::InvalidOpenId => e |
| 148 |
yield Result[:invalid], identity_url, nil |
| 149 |
rescue OpenID::OpenIDError, Timeout::Error => e |
| 150 |
logger.error("[OPENID] #{e}")
|
| 151 |
yield Result[:missing], identity_url, nil |
| 152 |
end |
| 153 |
|
| 154 |
def complete_open_id_authentication |
| 155 |
params_with_path = params.reject { |key, value| request.path_parameters[key] }
|
| 156 |
params_with_path.delete(:format) |
| 157 |
open_id_response = timeout_protection_from_identity_server { open_id_consumer.complete(params_with_path, requested_url) }
|
| 158 |
identity_url = normalize_identifier(open_id_response.display_identifier) if open_id_response.display_identifier |
| 159 |
|
| 160 |
case open_id_response.status |
| 161 |
when OpenID::Consumer::SUCCESS |
| 162 |
profile_data = {}
|
| 163 |
|
| 164 |
# merge the SReg data and the AX data into a single hash of profile data |
| 165 |
[ OpenID::SReg::Response, OpenID::AX::FetchResponse ].each do |data_response| |
| 166 |
if data_response.from_success_response( open_id_response ) |
| 167 |
profile_data.merge! data_response.from_success_response( open_id_response ).data |
| 168 |
end |
| 169 |
end |
| 170 |
|
| 171 |
yield Result[:successful], identity_url, profile_data |
| 172 |
when OpenID::Consumer::CANCEL |
| 173 |
yield Result[:canceled], identity_url, nil |
| 174 |
when OpenID::Consumer::FAILURE |
| 175 |
yield Result[:failed], identity_url, nil |
| 176 |
when OpenID::Consumer::SETUP_NEEDED |
| 177 |
yield Result[:setup_needed], open_id_response.setup_url, nil |
| 178 |
end |
| 179 |
end |
| 180 |
|
| 181 |
def open_id_consumer |
| 182 |
OpenID::Consumer.new(session, OpenIdAuthentication.store) |
| 183 |
end |
| 184 |
|
| 185 |
def add_simple_registration_fields(open_id_request, fields) |
| 186 |
sreg_request = OpenID::SReg::Request.new |
| 187 |
|
| 188 |
# filter out AX identifiers (URIs) |
| 189 |
required_fields = fields[:required].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
| 190 |
optional_fields = fields[:optional].collect { |f| f.to_s unless f =~ /^https?:\/\// }.compact
|
| 191 |
|
| 192 |
sreg_request.request_fields(required_fields, true) unless required_fields.blank? |
| 193 |
sreg_request.request_fields(optional_fields, false) unless optional_fields.blank? |
| 194 |
sreg_request.policy_url = fields[:policy_url] if fields[:policy_url] |
| 195 |
open_id_request.add_extension(sreg_request) |
| 196 |
end |
| 197 |
|
| 198 |
def add_ax_fields( open_id_request, fields ) |
| 199 |
ax_request = OpenID::AX::FetchRequest.new |
| 200 |
|
| 201 |
# look through the :required and :optional fields for URIs (AX identifiers) |
| 202 |
fields[:required].each do |f| |
| 203 |
next unless f =~ /^https?:\/\// |
| 204 |
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, true ) ) |
| 205 |
end |
| 206 |
|
| 207 |
fields[:optional].each do |f| |
| 208 |
next unless f =~ /^https?:\/\// |
| 209 |
ax_request.add( OpenID::AX::AttrInfo.new( f, nil, false ) ) |
| 210 |
end |
| 211 |
|
| 212 |
open_id_request.add_extension( ax_request ) |
| 213 |
end |
| 214 |
|
| 215 |
def open_id_redirect_url(open_id_request, return_to = nil, method = nil) |
| 216 |
open_id_request.return_to_args['_method'] = (method || request.method).to_s |
| 217 |
open_id_request.return_to_args['open_id_complete'] = '1' |
| 218 |
open_id_request.redirect_url(root_url, return_to || requested_url) |
| 219 |
end |
| 220 |
|
| 221 |
def requested_url |
| 222 |
relative_url_root = self.class.respond_to?(:relative_url_root) ? |
| 223 |
self.class.relative_url_root.to_s : |
| 224 |
request.relative_url_root |
| 225 |
"#{request.protocol}#{request.host_with_port}#{relative_url_root}#{request.path}"
|
| 226 |
end |
| 227 |
|
| 228 |
def timeout_protection_from_identity_server |
| 229 |
yield |
| 230 |
rescue Timeout::Error |
| 231 |
Class.new do |
| 232 |
def status |
| 233 |
OpenID::FAILURE |
| 234 |
end |
| 235 |
|
| 236 |
def msg |
| 237 |
"Identity server timed out" |
| 238 |
end |
| 239 |
end.new |
| 240 |
end |
| 241 |
end |