You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

open_id_authentication.rb 4.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. require 'uri'
  2. require 'openid'
  3. require 'rack/openid'
  4. module OpenIdAuthentication
  5. def self.new(app)
  6. store = OpenIdAuthentication.store
  7. if store.nil?
  8. Rails.logger.warn "OpenIdAuthentication.store is nil. Using in-memory store."
  9. end
  10. ::Rack::OpenID.new(app, OpenIdAuthentication.store)
  11. end
  12. def self.store
  13. @@store
  14. end
  15. def self.store=(*store_option)
  16. store, *parameters = *([ store_option ].flatten)
  17. @@store = case store
  18. when :memory
  19. require 'openid/store/memory'
  20. OpenID::Store::Memory.new
  21. when :file
  22. require 'openid/store/filesystem'
  23. OpenID::Store::Filesystem.new(Rails.root.join('tmp/openids'))
  24. when :memcache
  25. require 'memcache'
  26. require 'openid/store/memcache'
  27. OpenID::Store::Memcache.new(MemCache.new(parameters))
  28. else
  29. store
  30. end
  31. end
  32. self.store = nil
  33. class InvalidOpenId < StandardError
  34. end
  35. class Result
  36. ERROR_MESSAGES = {
  37. :missing => "Sorry, the OpenID server couldn't be found",
  38. :invalid => "Sorry, but this does not appear to be a valid OpenID",
  39. :canceled => "OpenID verification was canceled",
  40. :failed => "OpenID verification failed",
  41. :setup_needed => "OpenID verification needs setup"
  42. }
  43. def self.[](code)
  44. new(code)
  45. end
  46. def initialize(code)
  47. @code = code
  48. end
  49. def status
  50. @code
  51. end
  52. ERROR_MESSAGES.each_key { |state| define_method("#{state}?") { @code == state } }
  53. def successful?
  54. @code == :successful
  55. end
  56. def unsuccessful?
  57. ERROR_MESSAGES.keys.include?(@code)
  58. end
  59. def message
  60. ERROR_MESSAGES[@code]
  61. end
  62. end
  63. # normalizes an OpenID according to http://openid.net/specs/openid-authentication-2_0.html#normalization
  64. def self.normalize_identifier(identifier)
  65. # clean up whitespace
  66. identifier = identifier.to_s.strip
  67. # if an XRI has a prefix, strip it.
  68. identifier.gsub!(/xri:\/\//i, '')
  69. # dodge XRIs -- TODO: validate, don't just skip.
  70. unless ['=', '@', '+', '$', '!', '('].include?(identifier.at(0))
  71. # does it begin with http? if not, add it.
  72. identifier = +"http://#{identifier}" unless /^http/i.match?(identifier)
  73. # strip any fragments
  74. identifier.gsub!(/\#(.*)$/, '')
  75. begin
  76. uri = URI.parse(identifier)
  77. uri.scheme = uri.scheme.downcase if uri.scheme # URI should do this
  78. identifier = uri.normalize.to_s
  79. rescue URI::InvalidURIError
  80. raise InvalidOpenId.new("#{identifier} is not an OpenID identifier")
  81. end
  82. end
  83. return identifier
  84. end
  85. protected
  86. # The parameter name of "openid_identifier" is used rather than
  87. # the Rails convention "open_id_identifier" because that's what
  88. # the specification dictates in order to get browser auto-complete
  89. # working across sites
  90. def using_open_id?(identifier = nil)
  91. identifier ||= open_id_identifier
  92. !identifier.blank? || request.env[Rack::OpenID::RESPONSE]
  93. end
  94. def authenticate_with_open_id(identifier = nil, options = {}, &block)
  95. identifier ||= open_id_identifier
  96. if request.env[Rack::OpenID::RESPONSE]
  97. complete_open_id_authentication(&block)
  98. else
  99. begin_open_id_authentication(identifier, options, &block)
  100. end
  101. end
  102. private
  103. def open_id_identifier
  104. params[:openid_identifier] || params[:openid_url]
  105. end
  106. def begin_open_id_authentication(identifier, options = {})
  107. options[:identifier] = identifier
  108. value = Rack::OpenID.build_header(options)
  109. response.headers[Rack::OpenID::AUTHENTICATE_HEADER] = value
  110. head :unauthorized
  111. end
  112. def complete_open_id_authentication
  113. response = request.env[Rack::OpenID::RESPONSE]
  114. identifier = response.display_identifier
  115. case response.status
  116. when OpenID::Consumer::SUCCESS
  117. yield Result[:successful], identifier,
  118. OpenID::SReg::Response.from_success_response(response)
  119. when :missing
  120. yield Result[:missing], identifier, nil
  121. when :invalid
  122. yield Result[:invalid], identifier, nil
  123. when OpenID::Consumer::CANCEL
  124. yield Result[:canceled], identifier, nil
  125. when OpenID::Consumer::FAILURE
  126. yield Result[:failed], identifier, nil
  127. when OpenID::Consumer::SETUP_NEEDED
  128. yield Result[:setup_needed], response.setup_url, nil
  129. end
  130. end
  131. end