omniauth 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of omniauth might be problematic. Click here for more details.

Files changed (197) hide show
  1. data/.autotest +9 -0
  2. data/.document +5 -0
  3. data/.gitignore +36 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +7 -0
  6. data/.yardopts +4 -0
  7. data/Gemfile +6 -1
  8. data/README.markdown +121 -0
  9. data/Rakefile +50 -2
  10. data/lib/omniauth/version.rb +18 -4
  11. data/oa-basic/.gemtest +0 -0
  12. data/oa-basic/.rspec +3 -0
  13. data/oa-basic/.yardopts +4 -0
  14. data/oa-basic/LICENSE +19 -0
  15. data/oa-basic/README.rdoc +34 -0
  16. data/oa-basic/Rakefile +6 -0
  17. data/oa-basic/lib/oa-basic.rb +1 -0
  18. data/oa-basic/lib/omniauth/basic.rb +8 -0
  19. data/oa-basic/lib/omniauth/strategies/http_basic.rb +56 -0
  20. data/oa-basic/lib/omniauth/version.rb +19 -0
  21. data/oa-basic/oa-basic.gemspec +28 -0
  22. data/oa-basic/spec/omniauth/strategies/basic_spec.rb +7 -0
  23. data/oa-basic/spec/spec_helper.rb +11 -0
  24. data/oa-core/.gemtest +0 -0
  25. data/oa-core/.rspec +3 -0
  26. data/oa-core/.yardopts +4 -0
  27. data/oa-core/LICENSE +19 -0
  28. data/oa-core/Rakefile +6 -0
  29. data/oa-core/autotest/discover.rb +1 -0
  30. data/oa-core/lib/oa-core.rb +1 -0
  31. data/oa-core/lib/omniauth/builder.rb +33 -0
  32. data/oa-core/lib/omniauth/core.rb +135 -0
  33. data/oa-core/lib/omniauth/form.rb +184 -0
  34. data/oa-core/lib/omniauth/strategy.rb +227 -0
  35. data/oa-core/lib/omniauth/test.rb +12 -0
  36. data/oa-core/lib/omniauth/test/phony_session.rb +8 -0
  37. data/oa-core/lib/omniauth/test/strategy_macros.rb +34 -0
  38. data/oa-core/lib/omniauth/test/strategy_test_case.rb +49 -0
  39. data/oa-core/lib/omniauth/version.rb +19 -0
  40. data/oa-core/oa-core.gemspec +24 -0
  41. data/oa-core/spec/omniauth/builder_spec.rb +20 -0
  42. data/oa-core/spec/omniauth/core_spec.rb +79 -0
  43. data/oa-core/spec/omniauth/strategy_spec.rb +358 -0
  44. data/oa-core/spec/spec_helper.rb +12 -0
  45. data/oa-enterprise/.gemtest +0 -0
  46. data/oa-enterprise/.rspec +3 -0
  47. data/oa-enterprise/.yardopts +4 -0
  48. data/oa-enterprise/LICENSE +19 -0
  49. data/oa-enterprise/README.rdoc +82 -0
  50. data/oa-enterprise/Rakefile +6 -0
  51. data/oa-enterprise/lib/oa-enterprise.rb +1 -0
  52. data/oa-enterprise/lib/omniauth/enterprise.rb +8 -0
  53. data/oa-enterprise/lib/omniauth/strategies/cas.rb +47 -0
  54. data/oa-enterprise/lib/omniauth/strategies/cas/configuration.rb +98 -0
  55. data/oa-enterprise/lib/omniauth/strategies/cas/service_ticket_validator.rb +84 -0
  56. data/oa-enterprise/lib/omniauth/strategies/ldap.rb +111 -0
  57. data/oa-enterprise/lib/omniauth/strategies/ldap/adaptor.rb +279 -0
  58. data/oa-enterprise/lib/omniauth/version.rb +19 -0
  59. data/oa-enterprise/oa-enterprise.gemspec +32 -0
  60. data/oa-enterprise/spec/fixtures/cas_failure.xml +4 -0
  61. data/oa-enterprise/spec/fixtures/cas_success.xml +8 -0
  62. data/oa-enterprise/spec/omniauth/strategies/cas_spec.rb +94 -0
  63. data/oa-enterprise/spec/spec_helper.rb +14 -0
  64. data/oa-more/.gemtest +0 -0
  65. data/oa-more/.rspec +3 -0
  66. data/oa-more/.yardopts +4 -0
  67. data/oa-more/LICENSE +19 -0
  68. data/oa-more/README.rdoc +22 -0
  69. data/oa-more/Rakefile +6 -0
  70. data/oa-more/lib/oa-more.rb +1 -0
  71. data/oa-more/lib/omniauth/more.rb +9 -0
  72. data/oa-more/lib/omniauth/strategies/flickr.rb +86 -0
  73. data/oa-more/lib/omniauth/strategies/windows_live.rb +39 -0
  74. data/oa-more/lib/omniauth/strategies/windows_live/windowslivelogin.rb +1143 -0
  75. data/oa-more/lib/omniauth/strategies/yupoo.rb +67 -0
  76. data/oa-more/lib/omniauth/version.rb +19 -0
  77. data/oa-more/oa-more.gemspec +30 -0
  78. data/oa-more/spec/omniauth/strategies/flickr_spec.rb +7 -0
  79. data/oa-more/spec/spec_helper.rb +11 -0
  80. data/oa-oauth/.gemtest +0 -0
  81. data/oa-oauth/.rspec +3 -0
  82. data/oa-oauth/.yardopts +4 -0
  83. data/oa-oauth/LICENSE +19 -0
  84. data/oa-oauth/README.rdoc +35 -0
  85. data/oa-oauth/Rakefile +6 -0
  86. data/oa-oauth/autotest/discover.rb +1 -0
  87. data/oa-oauth/lib/oa-oauth.rb +1 -0
  88. data/oa-oauth/lib/omniauth/oauth.rb +53 -0
  89. data/oa-oauth/lib/omniauth/strategies/bitly.rb +46 -0
  90. data/oa-oauth/lib/omniauth/strategies/dailymile.rb +64 -0
  91. data/oa-oauth/lib/omniauth/strategies/doit.rb +60 -0
  92. data/oa-oauth/lib/omniauth/strategies/dopplr.rb +53 -0
  93. data/oa-oauth/lib/omniauth/strategies/douban.rb +60 -0
  94. data/oa-oauth/lib/omniauth/strategies/evernote.rb +54 -0
  95. data/oa-oauth/lib/omniauth/strategies/facebook.rb +70 -0
  96. data/oa-oauth/lib/omniauth/strategies/foursquare.rb +62 -0
  97. data/oa-oauth/lib/omniauth/strategies/github.rb +50 -0
  98. data/oa-oauth/lib/omniauth/strategies/goodreads.rb +44 -0
  99. data/oa-oauth/lib/omniauth/strategies/google.rb +80 -0
  100. data/oa-oauth/lib/omniauth/strategies/gowalla.rb +64 -0
  101. data/oa-oauth/lib/omniauth/strategies/hyves.rb +67 -0
  102. data/oa-oauth/lib/omniauth/strategies/identica.rb +49 -0
  103. data/oa-oauth/lib/omniauth/strategies/instagram.rb +56 -0
  104. data/oa-oauth/lib/omniauth/strategies/instapaper.rb +40 -0
  105. data/oa-oauth/lib/omniauth/strategies/linked_in.rb +55 -0
  106. data/oa-oauth/lib/omniauth/strategies/meetup.rb +56 -0
  107. data/oa-oauth/lib/omniauth/strategies/miso.rb +41 -0
  108. data/oa-oauth/lib/omniauth/strategies/mixi.rb +59 -0
  109. data/oa-oauth/lib/omniauth/strategies/netflix.rb +65 -0
  110. data/oa-oauth/lib/omniauth/strategies/oauth.rb +85 -0
  111. data/oa-oauth/lib/omniauth/strategies/oauth2.rb +98 -0
  112. data/oa-oauth/lib/omniauth/strategies/qzone.rb +69 -0
  113. data/oa-oauth/lib/omniauth/strategies/rdio.rb +45 -0
  114. data/oa-oauth/lib/omniauth/strategies/renren.rb +87 -0
  115. data/oa-oauth/lib/omniauth/strategies/salesforce.rb +44 -0
  116. data/oa-oauth/lib/omniauth/strategies/smug_mug.rb +42 -0
  117. data/oa-oauth/lib/omniauth/strategies/sound_cloud.rb +46 -0
  118. data/oa-oauth/lib/omniauth/strategies/t163.rb +57 -0
  119. data/oa-oauth/lib/omniauth/strategies/teambox.rb +49 -0
  120. data/oa-oauth/lib/omniauth/strategies/thirty_seven_signals.rb +41 -0
  121. data/oa-oauth/lib/omniauth/strategies/tqq.rb +64 -0
  122. data/oa-oauth/lib/omniauth/strategies/trade_me.rb +45 -0
  123. data/oa-oauth/lib/omniauth/strategies/trip_it.rb +22 -0
  124. data/oa-oauth/lib/omniauth/strategies/tsina.rb +79 -0
  125. data/oa-oauth/lib/omniauth/strategies/tsohu.rb +57 -0
  126. data/oa-oauth/lib/omniauth/strategies/tumblr.rb +60 -0
  127. data/oa-oauth/lib/omniauth/strategies/twitter.rb +57 -0
  128. data/oa-oauth/lib/omniauth/strategies/type_pad.rb +76 -0
  129. data/oa-oauth/lib/omniauth/strategies/vimeo.rb +54 -0
  130. data/oa-oauth/lib/omniauth/strategies/vkontakte.rb +84 -0
  131. data/oa-oauth/lib/omniauth/strategies/xauth.rb +67 -0
  132. data/oa-oauth/lib/omniauth/strategies/yahoo.rb +55 -0
  133. data/oa-oauth/lib/omniauth/strategies/yammer.rb +43 -0
  134. data/oa-oauth/lib/omniauth/strategies/you_tube.rb +73 -0
  135. data/oa-oauth/lib/omniauth/version.rb +19 -0
  136. data/oa-oauth/oa-oauth.gemspec +33 -0
  137. data/oa-oauth/spec/fixtures/basecamp_200.xml +24 -0
  138. data/oa-oauth/spec/fixtures/campfire_200.json +10 -0
  139. data/oa-oauth/spec/omniauth/strategies/bitly_spec.rb +5 -0
  140. data/oa-oauth/spec/omniauth/strategies/dailymile_spec.rb +5 -0
  141. data/oa-oauth/spec/omniauth/strategies/doit_spec.rb +5 -0
  142. data/oa-oauth/spec/omniauth/strategies/dopplr_spec.rb +5 -0
  143. data/oa-oauth/spec/omniauth/strategies/douban_spec.rb +5 -0
  144. data/oa-oauth/spec/omniauth/strategies/evernote_spec.rb +5 -0
  145. data/oa-oauth/spec/omniauth/strategies/facebook_spec.rb +5 -0
  146. data/oa-oauth/spec/omniauth/strategies/foursquare_spec.rb +18 -0
  147. data/oa-oauth/spec/omniauth/strategies/github_spec.rb +5 -0
  148. data/oa-oauth/spec/omniauth/strategies/goodreads_spec.rb +6 -0
  149. data/oa-oauth/spec/omniauth/strategies/google_spec.rb +5 -0
  150. data/oa-oauth/spec/omniauth/strategies/gowalla_spec.rb +5 -0
  151. data/oa-oauth/spec/omniauth/strategies/hyves_spec.rb +5 -0
  152. data/oa-oauth/spec/omniauth/strategies/identica_spec.rb +5 -0
  153. data/oa-oauth/spec/omniauth/strategies/linked_in_spec.rb +5 -0
  154. data/oa-oauth/spec/omniauth/strategies/meetup_spec.rb +14 -0
  155. data/oa-oauth/spec/omniauth/strategies/miso_spec.rb +5 -0
  156. data/oa-oauth/spec/omniauth/strategies/netflix_spec.rb +5 -0
  157. data/oa-oauth/spec/omniauth/strategies/oauth2_spec.rb +0 -0
  158. data/oa-oauth/spec/omniauth/strategies/oauth_spec.rb +77 -0
  159. data/oa-oauth/spec/omniauth/strategies/rdio_spec.rb +5 -0
  160. data/oa-oauth/spec/omniauth/strategies/salesforce_spec.rb +5 -0
  161. data/oa-oauth/spec/omniauth/strategies/smug_mug_spec.rb +5 -0
  162. data/oa-oauth/spec/omniauth/strategies/sound_cloud_spec.rb +5 -0
  163. data/oa-oauth/spec/omniauth/strategies/t163_spec.rb +5 -0
  164. data/oa-oauth/spec/omniauth/strategies/teambox_spec.rb +5 -0
  165. data/oa-oauth/spec/omniauth/strategies/thirty_seven_signals_spec.rb +5 -0
  166. data/oa-oauth/spec/omniauth/strategies/trade_me_spec.rb +5 -0
  167. data/oa-oauth/spec/omniauth/strategies/trip_it_spec.rb +5 -0
  168. data/oa-oauth/spec/omniauth/strategies/tsina_spec.rb +5 -0
  169. data/oa-oauth/spec/omniauth/strategies/tumblr_spec.rb +5 -0
  170. data/oa-oauth/spec/omniauth/strategies/twitter_spec.rb +20 -0
  171. data/oa-oauth/spec/omniauth/strategies/type_pad_spec.rb +5 -0
  172. data/oa-oauth/spec/omniauth/strategies/vimeo_spec.rb +5 -0
  173. data/oa-oauth/spec/omniauth/strategies/vkontakte_spec.rb +5 -0
  174. data/oa-oauth/spec/omniauth/strategies/yahoo_spec.rb +5 -0
  175. data/oa-oauth/spec/omniauth/strategies/yammer_spec.rb +5 -0
  176. data/oa-oauth/spec/omniauth/strategies/you_tube_spec.rb +5 -0
  177. data/oa-oauth/spec/spec_helper.rb +27 -0
  178. data/oa-oauth/spec/support/shared_examples.rb +29 -0
  179. data/oa-openid/.gemtest +0 -0
  180. data/oa-openid/.rspec +3 -0
  181. data/oa-openid/.yardopts +4 -0
  182. data/oa-openid/LICENSE +19 -0
  183. data/oa-openid/README.rdoc +51 -0
  184. data/oa-openid/Rakefile +6 -0
  185. data/oa-openid/lib/oa-openid.rb +1 -0
  186. data/oa-openid/lib/omniauth/openid.rb +59 -0
  187. data/oa-openid/lib/omniauth/openid/gapps.rb +32 -0
  188. data/oa-openid/lib/omniauth/strategies/google_apps.rb +23 -0
  189. data/oa-openid/lib/omniauth/strategies/open_id.rb +132 -0
  190. data/oa-openid/lib/omniauth/version.rb +19 -0
  191. data/oa-openid/oa-openid.gemspec +29 -0
  192. data/oa-openid/spec/omniauth/strategies/open_id_spec.rb +71 -0
  193. data/oa-openid/spec/spec_helper.rb +14 -0
  194. data/omniauth.gemspec +3 -3
  195. data/tasks/all.rb +134 -0
  196. metadata +199 -9
  197. data/README.rdoc +0 -17
@@ -0,0 +1,6 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
6
+ task :test => :spec
@@ -0,0 +1 @@
1
+ require 'omniauth/enterprise'
@@ -0,0 +1,8 @@
1
+ require 'omniauth/core'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ autoload :CAS, 'omniauth/strategies/cas'
6
+ autoload :LDAP, 'omniauth/strategies/ldap'
7
+ end
8
+ end
@@ -0,0 +1,47 @@
1
+ require 'omniauth/enterprise'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class CAS
6
+ include OmniAuth::Strategy
7
+
8
+ autoload :Configuration, 'omniauth/strategies/cas/configuration'
9
+ autoload :ServiceTicketValidator, 'omniauth/strategies/cas/service_ticket_validator'
10
+
11
+ def initialize(app, options = {}, &block)
12
+ super(app, options[:name] || :cas, options.dup, &block)
13
+ @configuration = OmniAuth::Strategies::CAS::Configuration.new(options)
14
+ end
15
+
16
+ protected
17
+
18
+ def request_phase
19
+ [
20
+ 302,
21
+ {
22
+ 'Location' => @configuration.login_url(callback_url),
23
+ 'Content-Type' => 'text/plain'
24
+ },
25
+ ["You are being redirected to CAS for sign-in."]
26
+ ]
27
+ end
28
+
29
+ def callback_phase
30
+ ticket = request.params['ticket']
31
+ return fail!(:no_ticket, 'No CAS Ticket') unless ticket
32
+ validator = ServiceTicketValidator.new(@configuration, callback_url, ticket)
33
+ @user_info = validator.user_info
34
+ return fail!(:invalid_ticket, 'Invalid CAS Ticket') if @user_info.nil? || @user_info.empty?
35
+ super
36
+ end
37
+
38
+ def auth_hash
39
+ OmniAuth::Utils.deep_merge(super, {
40
+ 'uid' => @user_info.delete('user'),
41
+ 'extra' => @user_info
42
+ })
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,98 @@
1
+ require 'rack'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class CAS
6
+ class Configuration
7
+
8
+ DEFAULT_LOGIN_URL = "%s/login"
9
+
10
+ DEFAULT_SERVICE_VALIDATE_URL = "%s/serviceValidate"
11
+
12
+ # @param [Hash] params configuration options
13
+ # @option params [String, nil] :cas_server the CAS server root URL; probably something like
14
+ # `http://cas.mycompany.com` or `http://cas.mycompany.com/cas`; optional.
15
+ # @option params [String, nil] :cas_login_url (:cas_server + '/login') the URL to which to
16
+ # redirect for logins; options if `:cas_server` is specified,
17
+ # required otherwise.
18
+ # @option params [String, nil] :cas_service_validate_url (:cas_server + '/serviceValidate') the
19
+ # URL to use for validating service tickets; optional if `:cas_server` is
20
+ # specified, requred otherwise.
21
+ # @option params [Boolean, nil] :disable_ssl_verification disable verification for SSL cert,
22
+ # helpful when you developing with a fake cert.
23
+ def initialize(params)
24
+ parse_params params
25
+ end
26
+
27
+ # Build a CAS login URL from +service+.
28
+ #
29
+ # @param [String] service the service (a.k.a. return-to) URL
30
+ #
31
+ # @return [String] a URL like `http://cas.mycompany.com/login?service=...`
32
+ def login_url(service)
33
+ append_service @login_url, service
34
+ end
35
+
36
+ # Build a service-validation URL from +service+ and +ticket+.
37
+ # If +service+ has a ticket param, first remove it. URL-encode
38
+ # +service+ and add it and the +ticket+ as paraemters to the
39
+ # CAS serviceValidate URL.
40
+ #
41
+ # @param [String] service the service (a.k.a. return-to) URL
42
+ # @param [String] ticket the ticket to validate
43
+ #
44
+ # @return [String] a URL like `http://cas.mycompany.com/serviceValidate?service=...&ticket=...`
45
+ def service_validate_url(service, ticket)
46
+ service = service.sub(/[?&]ticket=[^?&]+/, '')
47
+ url = append_service(@service_validate_url, service)
48
+ url << '&ticket=' << Rack::Utils.escape(ticket)
49
+ end
50
+
51
+ def disable_ssl_verification?
52
+ @disable_ssl_verification
53
+ end
54
+
55
+ private
56
+
57
+ def parse_params(params)
58
+ if params[:cas_server].nil? && params[:cas_login_url].nil?
59
+ raise ArgumentError.new(":cas_server or :cas_login_url MUST be provided")
60
+ end
61
+ @login_url = params[:cas_login_url]
62
+ @login_url ||= DEFAULT_LOGIN_URL % params[:cas_server]
63
+ validate_is_url 'login URL', @login_url
64
+
65
+ if params[:cas_server].nil? && params[:cas_service_validate_url].nil?
66
+ raise ArgumentError.new(":cas_server or :cas_service_validate_url MUST be provided")
67
+ end
68
+ @service_validate_url = params[:cas_service_validate_url]
69
+ @service_validate_url ||= DEFAULT_SERVICE_VALIDATE_URL % params[:cas_server]
70
+ validate_is_url 'service-validate URL', @service_validate_url
71
+
72
+ @disable_ssl_verification = params[:disable_ssl_verification]
73
+ end
74
+
75
+ IS_NOT_URL_ERROR_MESSAGE = "%s is not a valid URL"
76
+
77
+ def validate_is_url(name, possibly_a_url)
78
+ url = URI.parse(possibly_a_url) rescue nil
79
+ raise ArgumentError.new(IS_NOT_URL_ERROR_MESSAGE % name) unless url.kind_of?(URI::HTTP)
80
+ end
81
+
82
+ # Adds +service+ as an URL-escaped parameter to +base+.
83
+ #
84
+ # @param [String] base the base URL
85
+ # @param [String] service the service (a.k.a. return-to) URL.
86
+ #
87
+ # @return [String] the new joined URL.
88
+ def append_service(base, service)
89
+ result = base.dup
90
+ result << (result.include?('?') ? '&' : '?')
91
+ result << 'service='
92
+ result << Rack::Utils.escape(service)
93
+ end
94
+
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,84 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'nokogiri'
4
+
5
+ module OmniAuth
6
+ module Strategies
7
+ class CAS
8
+ class ServiceTicketValidator
9
+
10
+ VALIDATION_REQUEST_HEADERS = { 'Accept' => '*/*' }
11
+
12
+ # Build a validator from a +configuration+, a
13
+ # +return_to+ URL, and a +ticket+.
14
+ #
15
+ # @param [OmniAuth::Strategies::CAS::Configuration] configuration the CAS configuration
16
+ # @param [String] return_to_url the URL of this CAS client service
17
+ # @param [String] ticket the service ticket to validate
18
+ def initialize(configuration, return_to_url, ticket)
19
+ @configuration = configuration
20
+ @uri = URI.parse(@configuration.service_validate_url(return_to_url, ticket))
21
+ end
22
+
23
+ # Request validation of the ticket from the CAS server's
24
+ # serviceValidate (CAS 2.0) function.
25
+ #
26
+ # Swallows all XML parsing errors (and returns +nil+ in those cases).
27
+ #
28
+ # @return [Hash, nil] a user information hash if the response is valid; +nil+ otherwise.
29
+ #
30
+ # @raise any connection errors encountered.
31
+ def user_info
32
+ parse_user_info(find_authentication_success(get_service_response_body))
33
+ end
34
+
35
+ private
36
+
37
+ # turns an `<cas:authenticationSuccess>` node into a Hash;
38
+ # returns nil if given nil
39
+ def parse_user_info(node)
40
+ return nil if node.nil?
41
+ node.children.inject({}) do |hash, child|
42
+ unless child.kind_of?(Nokogiri::XML::Text) ||
43
+ child.name == 'cas:proxies' ||
44
+ child.name == 'proxies'
45
+ hash[child.name.sub(/^cas:/, '')] = child.content
46
+ end
47
+ hash
48
+ end
49
+ end
50
+
51
+ # finds an `<cas:authenticationSuccess>` node in
52
+ # a `<cas:serviceResponse>` body if present; returns nil
53
+ # if the passed body is nil or if there is no such node.
54
+ def find_authentication_success(body)
55
+ return nil if body.nil? || body == ''
56
+ begin
57
+ doc = Nokogiri::XML(body)
58
+ begin
59
+ doc.xpath('/cas:serviceResponse/cas:authenticationSuccess')
60
+ rescue Nokogiri::XML::XPath::SyntaxError
61
+ doc.xpath('/serviceResponse/authenticationSuccess')
62
+ end
63
+ rescue Nokogiri::XML::XPath::SyntaxError
64
+ nil
65
+ end
66
+ end
67
+
68
+ # retrieves the `<cas:serviceResponse>` XML from the CAS server
69
+ def get_service_response_body
70
+ result = ''
71
+ http = ::Net::HTTP.new(@uri.host, @uri.port)
72
+ http.use_ssl = @uri.port == 443 || @uri.instance_of?(URI::HTTPS)
73
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? && @configuration.disable_ssl_verification?
74
+ http.start do |c|
75
+ response = c.get "#{@uri.path}?#{@uri.query}", VALIDATION_REQUEST_HEADERS.dup
76
+ result = response.body
77
+ end
78
+ result
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,111 @@
1
+ require 'omniauth/enterprise'
2
+ require 'net/ldap'
3
+ require 'sasl/base'
4
+ require 'sasl'
5
+
6
+ module OmniAuth
7
+ module Strategies
8
+ class LDAP
9
+ include OmniAuth::Strategy
10
+
11
+ autoload :Adaptor, 'omniauth/strategies/ldap/adaptor'
12
+ @@config = {'name' => 'cn',
13
+ 'first_name' => 'givenName',
14
+ 'last_name' => 'sn',
15
+ 'email' => ['mail', "email", 'userPrincipalName'],
16
+ 'phone' => ['telephoneNumber', 'homePhone', 'facsimileTelephoneNumber'],
17
+ 'mobile_number' => ['mobile', 'mobileTelephoneNumber'],
18
+ 'nickname' => ['uid', 'userid', 'sAMAccountName'],
19
+ 'title' => 'title',
20
+ 'location' => {"%0, %1, %2, %3 %4" => [['address', 'postalAddress', 'homePostalAddress', 'street', 'streetAddress'], ['l'], ['st'],['co'],['postOfficeBox']]},
21
+ 'uid' => 'dn',
22
+ 'url' => ['wwwhomepage'],
23
+ 'image' => 'jpegPhoto',
24
+ 'description' => 'description'}
25
+
26
+ # Initialize the LDAP Middleware
27
+ #
28
+ # @param [Rack Application] app Standard Rack middleware argument.
29
+ # @option options [String, 'LDAP Authentication'] :title A title for the authentication form.
30
+ def initialize(app, options = {}, &block)
31
+ super(app, options[:name] || :ldap, options.dup, &block)
32
+ @name_proc = (@options.delete(:name_proc) || Proc.new {|name| name})
33
+ @adaptor = OmniAuth::Strategies::LDAP::Adaptor.new(options)
34
+ end
35
+
36
+ protected
37
+
38
+ def request_phase
39
+ if env['REQUEST_METHOD'] == 'GET'
40
+ get_credentials
41
+ else
42
+ session['omniauth.ldap'] = {'username' => request['username'], 'password' => request['password']}
43
+ redirect callback_path
44
+ end
45
+ end
46
+
47
+ def get_credentials
48
+ OmniAuth::Form.build(:title => (options[:title] || "LDAP Authentication")) do
49
+ text_field 'Login', 'username'
50
+ password_field 'Password', 'password'
51
+ end.to_response
52
+ end
53
+
54
+ def callback_phase
55
+ begin
56
+ creds = session['omniauth.ldap']
57
+ session.delete 'omniauth.ldap'
58
+ @ldap_user_info = {}
59
+ begin
60
+ (@adaptor.bind(:allow_anonymous => true) unless @adaptor.bound?)
61
+ rescue Exception => e
62
+ puts "failed to bind with the default credentials: " + e.message
63
+ end
64
+ @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(creds['username'])),:limit => 1) if @adaptor.bound?
65
+ bind_dn = creds['username']
66
+ bind_dn = @ldap_user_info[:dn].to_a.first if @ldap_user_info[:dn]
67
+ @adaptor.bind(:bind_dn => bind_dn, :password => creds['password'])
68
+ @ldap_user_info = @adaptor.search(:filter => Net::LDAP::Filter.eq(@adaptor.uid, @name_proc.call(creds['username'])),:limit => 1) if @ldap_user_info.empty?
69
+ @user_info = self.class.map_user(@@config, @ldap_user_info)
70
+
71
+ @env['omniauth.auth'] = auth_hash
72
+
73
+ rescue Exception => e
74
+ return fail!(:invalid_credentials, e)
75
+ end
76
+ call_app!
77
+ end
78
+
79
+ def auth_hash
80
+ OmniAuth::Utils.deep_merge(super, {
81
+ 'uid' => @user_info["uid"],
82
+ 'user_info' => @user_info,
83
+ 'extra' => @ldap_user_info
84
+ })
85
+ end
86
+
87
+ def self.map_user(mapper, object)
88
+ user = {}
89
+ mapper.each do |key, value|
90
+ case value
91
+ when String
92
+ user[key] = object[value.downcase.to_sym].to_s if object[value.downcase.to_sym]
93
+ when Array
94
+ value.each {|v| (user[key] = object[v.downcase.to_sym].to_s; break;) if object[v.downcase.to_sym]}
95
+ when Hash
96
+ value.map do |key1, value1|
97
+ pattern = key1.dup
98
+ value1.each_with_index do |v,i|
99
+ part = '';
100
+ v.each {|v1| (part = object[v1.downcase.to_sym].to_s; break;) if object[v1.downcase.to_sym]}
101
+ pattern.gsub!("%#{i}",part||'')
102
+ end
103
+ user[key] = pattern
104
+ end
105
+ end
106
+ end
107
+ user
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,279 @@
1
+ #this code boughts pieces from activeldap and net-ldap
2
+
3
+ require 'rack'
4
+ require 'net/ldap'
5
+ require 'net/ntlm'
6
+ require 'uri'
7
+
8
+ module OmniAuth
9
+ module Strategies
10
+ class LDAP
11
+ class Adaptor
12
+ class LdapError < StandardError; end
13
+ class ConfigurationError < StandardError; end
14
+ class AuthenticationError < StandardError; end
15
+ class ConnectionError < StandardError; end
16
+
17
+ VALID_ADAPTER_CONFIGURATION_KEYS = [:host, :port, :method, :bind_dn, :password,
18
+ :try_sasl, :sasl_mechanisms, :uid, :base, :allow_anonymous]
19
+
20
+ MUST_HAVE_KEYS = [:host, :port, :method, :uid, :base]
21
+
22
+ METHOD = {
23
+ :ssl => :simple_tls,
24
+ :tls => :start_tls,
25
+ :plain => nil,
26
+ }
27
+
28
+ attr_accessor :bind_dn, :password
29
+ attr_reader :connection, :uid, :base
30
+
31
+ def initialize(configuration={})
32
+ @connection = nil
33
+ @disconnected = false
34
+ @bound = false
35
+ @configuration = configuration.dup
36
+ @configuration[:allow_anonymous] ||= false
37
+ @logger = @configuration.delete(:logger)
38
+ message = []
39
+ MUST_HAVE_KEYS.each do |name|
40
+ message << name if configuration[name].nil?
41
+ end
42
+ raise ArgumentError.new(message.join(",") +" MUST be provided") unless message.empty?
43
+ VALID_ADAPTER_CONFIGURATION_KEYS.each do |name|
44
+ instance_variable_set("@#{name}", configuration[name])
45
+ end
46
+ end
47
+
48
+ def connect(options={})
49
+ host = options[:host] || @host
50
+ method = ensure_method(options[:method] || @method || :plain)
51
+ port = options[:port] || @port || ensure_port(method)
52
+ @disconnected = false
53
+ @bound = false
54
+ @bind_tried = false
55
+
56
+ config = {
57
+ :host => host,
58
+ :port => port,
59
+ }
60
+
61
+ config[:encryption] = {:method => method} if method
62
+
63
+ @connection, @uri, @with_start_tls = begin
64
+ uri = construct_uri(host, port, method == :simple_tls)
65
+ with_start_tls = method == :start_tls
66
+ puts ({:uri => uri, :with_start_tls => with_start_tls}).inspect
67
+ [Net::LDAP::Connection.new(config), uri, with_start_tls]
68
+ rescue Net::LDAP::LdapError
69
+ raise ConnectionError, $!.message
70
+ end
71
+ end
72
+
73
+ def unbind(options={})
74
+ @connection.close # Net::LDAP doesn't implement unbind.
75
+ end
76
+
77
+ def bind(options={})
78
+ connect(options) unless connecting?
79
+ begin
80
+ @bind_tried = true
81
+
82
+ bind_dn = (options[:bind_dn] || @bind_dn).to_s
83
+ try_sasl = options.has_key?(:try_sasl) ? options[:try_sasl] : @try_sasl
84
+ if options.has_key?(:allow_anonymous)
85
+ allow_anonymous = options[:allow_anonymous]
86
+ else
87
+ allow_anonymous = @allow_anonymous
88
+ end
89
+ # Rough bind loop:
90
+ # Attempt 1: SASL if available
91
+ # Attempt 2: SIMPLE with credentials if password block
92
+ # Attempt 3: SIMPLE ANONYMOUS if 1 and 2 fail and allow anonymous is set to true
93
+ if try_sasl and sasl_bind(bind_dn, options)
94
+ puts "bound with sasl"
95
+ elsif simple_bind(bind_dn, options)
96
+ puts "bound with simple"
97
+ elsif allow_anonymous and bind_as_anonymous(options)
98
+ puts "bound as anonymous"
99
+ else
100
+ message = yield if block_given?
101
+ message ||= ('All authentication methods for %s exhausted.') % target
102
+ raise AuthenticationError, message
103
+ end
104
+ @bound = true
105
+ rescue Net::LDAP::LdapError
106
+ raise AuthenticationError, $!.message
107
+ end
108
+ end
109
+
110
+ def disconnect!(options={})
111
+ unbind(options)
112
+ @connection = @uri = @with_start_tls = nil
113
+ @disconnected = true
114
+ end
115
+
116
+ def rebind(options={})
117
+ unbind(options) if bound?
118
+ connect(options)
119
+ end
120
+
121
+ def connecting?
122
+ !@connection.nil? and !@disconnected
123
+ end
124
+
125
+ def bound?
126
+ connecting? and @bound
127
+ end
128
+
129
+ def search(options={}, &block)
130
+ base = options[:base]
131
+ filter = options[:filter]
132
+ limit = options[:limit]
133
+
134
+ args = {
135
+ :base => @base,
136
+ :filter => filter,
137
+ :size => limit
138
+ }
139
+
140
+ attributes = {}
141
+ execute(:search, args) do |entry|
142
+ entry.attribute_names.each do |name|
143
+ attributes[name] = entry[name]
144
+ end
145
+ end
146
+ attributes
147
+ end
148
+
149
+ private
150
+
151
+ def execute(method, *args, &block)
152
+ result = @connection.send(method, *args, &block)
153
+ message = nil
154
+
155
+ if result.is_a?(Hash)
156
+ message = result[:errorMessage]
157
+ result = result[:resultCode]
158
+ end
159
+
160
+ unless result.zero?
161
+ message = [Net::LDAP.result2string(result), message].compact.join(": ")
162
+ raise LdapError, message
163
+ end
164
+ end
165
+
166
+ def ensure_port(method)
167
+ if method == :ssl
168
+ URI::LDAPS::DEFAULT_PORT
169
+ else
170
+ URI::LDAP::DEFAULT_PORT
171
+ end
172
+ end
173
+
174
+ def prepare_connection(options)
175
+ end
176
+
177
+ def ensure_method(method)
178
+ method ||= "plain"
179
+ normalized_method = method.to_s.downcase.to_sym
180
+ return METHOD[normalized_method] if METHOD.has_key?(normalized_method)
181
+
182
+ available_methods = METHOD.keys.collect {|m| m.inspect}.join(", ")
183
+ format = "%s is not one of the available connect methods: %s"
184
+ raise ConfigurationError, format % [method.inspect, available_methods]
185
+ end
186
+
187
+ def sasl_bind(bind_dn, options={})
188
+ sasl_mechanisms = options[:sasl_mechanisms] || @sasl_mechanisms
189
+ sasl_mechanisms.each do |mechanism|
190
+ begin
191
+ normalized_mechanism = mechanism.downcase.gsub(/-/, '_')
192
+ sasl_bind_setup = "sasl_bind_setup_#{normalized_mechanism}"
193
+ next unless respond_to?(sasl_bind_setup, true)
194
+ initial_credential, challenge_response = send(sasl_bind_setup, bind_dn, options)
195
+
196
+ args = {
197
+ :method => :sasl,
198
+ :initial_credential => initial_credential,
199
+ :mechanism => mechanism,
200
+ :challenge_response => challenge_response,
201
+ }
202
+
203
+ info = {
204
+ :name => "bind: SASL", :dn => bind_dn, :mechanism => mechanism,
205
+ }
206
+
207
+ execute(:bind, args)
208
+ return true
209
+
210
+ rescue Exception => e
211
+ puts e.message
212
+ end
213
+ end
214
+ false
215
+ end
216
+
217
+ def sasl_bind_setup_digest_md5(bind_dn, options)
218
+ initial_credential = ""
219
+ challenge_response = Proc.new do |cred|
220
+ pref = SASL::Preferences.new :digest_uri => "ldap/#{@host}", :username => bind_dn, :has_password? => true, :password => options[:password]||@password
221
+ sasl = SASL.new("DIGEST-MD5", pref)
222
+ response = sasl.receive("challenge", cred)
223
+ response[1]
224
+ end
225
+ [initial_credential, challenge_response]
226
+ end
227
+
228
+ def sasl_bind_setup_gss_spnego(bind_dn, options)
229
+ puts options.inspect
230
+ user,psw = [bind_dn, options[:password]||@password]
231
+ raise LdapError.new( "invalid binding information" ) unless (user && psw)
232
+
233
+ nego = proc {|challenge|
234
+ t2_msg = Net::NTLM::Message.parse( challenge )
235
+ user, domain = user.split('\\').reverse
236
+ t2_msg.target_name = Net::NTLM::encode_utf16le(domain) if domain
237
+ t3_msg = t2_msg.response( {:user => user, :password => psw}, {:ntlmv2 => true} )
238
+ t3_msg.serialize
239
+ }
240
+ [Net::NTLM::Message::Type1.new.serialize, nego]
241
+ end
242
+
243
+ def simple_bind(bind_dn, options={})
244
+ args = {
245
+ :method => :simple,
246
+ :username => bind_dn,
247
+ :password => (options[:password]||@password).to_s,
248
+ }
249
+ begin
250
+ raise AuthenticationError if args[:password] == ""
251
+ execute(:bind, args)
252
+ true
253
+ rescue Exception
254
+ false
255
+ end
256
+ end
257
+
258
+ def bind_as_anonymous(options={})
259
+ execute(:bind, {:method => :anonymous})
260
+ true
261
+ end
262
+
263
+ def construct_uri(host, port, ssl)
264
+ protocol = ssl ? "ldaps" : "ldap"
265
+ URI.parse("#{protocol}://#{host}:#{port}").to_s
266
+ end
267
+
268
+ def target
269
+ return nil if @uri.nil?
270
+ if @with_start_tls
271
+ "#{@uri}(StartTLS)"
272
+ else
273
+ @uri
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
279
+ end