oa-enterprise 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.rdoc ADDED
File without changes
data/LICENSE.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 James A. Rosen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,38 @@
1
+ = OmniAuth::Enterprise
2
+
3
+ OmniAuth strategies for use in your intranet.
4
+
5
+ == Installation
6
+
7
+ To get just enterprise functionality:
8
+
9
+ gem install oa-enterprise
10
+
11
+ For the full auth suite:
12
+
13
+ gem install omniauth
14
+
15
+ == Stand-Alone Example
16
+
17
+ Use the strategy as a middleware in your application:
18
+
19
+ require 'omniauth/enterprise'
20
+
21
+ use OmniAuth::Strategies::CAS, :server => 'http://cas.mycompany.com/cas'
22
+
23
+ Then simply direct users to '/auth/cas' to have them sign in via your company's CAS server.
24
+ See OmniAuth::Strategies::CAS::Configuration for more configuration options.
25
+
26
+
27
+ == OmniAuth Builder
28
+
29
+ If CAS is one of several authentication strategies, use the OmniAuth Builder:
30
+
31
+ require 'omniauth/enterprise'
32
+ require 'omniauth/oauth' # for Campfire
33
+ require 'openid/store/filesystem'
34
+
35
+ use OmniAuth::Builder do
36
+ provider :cas, :server => 'http://cas.mycompany.com/cas'
37
+ provider :campfire
38
+ end
@@ -0,0 +1,7 @@
1
+ require 'omniauth/core'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ autoload :CAS, 'omniauth/strategies/cas'
6
+ end
7
+ end
@@ -0,0 +1,88 @@
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 <tt>:cas_server</tt> 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 <tt>:cas_server</tt> is
20
+ # specified, requred otherwise.
21
+ def initialize(params)
22
+ parse_params params
23
+ end
24
+
25
+ # Build a CAS login URL from +service+.
26
+ #
27
+ # @param [String] service the service (a.k.a. return-to) URL
28
+ #
29
+ # @return [String] a URL like
30
+ # "http://cas.mycompany.com/login?service=..."
31
+ def login_url(service)
32
+ append_service @login_url, service
33
+ end
34
+
35
+ # Build a service-validation URL from +service+ and +ticket+.
36
+ #
37
+ # @param [String] service the service (a.k.a. return-to) URL
38
+ # @param [String] ticket the ticket to validate
39
+ #
40
+ # @return [String] a URL like
41
+ # "http://cas.mycompany.com/serviceValidate?service=...&ticket=..."
42
+ def service_validate_url(service, ticket)
43
+ url = append_service @service_validate_url, service
44
+ url << '&ticket=' << Rack::Utils.escape(ticket)
45
+ end
46
+
47
+ private
48
+
49
+ def parse_params(params)
50
+ if params[:cas_server].nil? && params[:cas_login_url].nil?
51
+ raise ArgumentError.new(":cas_server or :cas_login_url MUST be provided")
52
+ end
53
+ @login_url = params[:cas_login_url]
54
+ @login_url ||= DEFAULT_LOGIN_URL % params[:cas_server]
55
+ validate_is_url 'login URL', @login_url
56
+
57
+ if params[:cas_server].nil? && params[:cas_service_validate_url].nil?
58
+ raise ArgumentError.new(":cas_server or :cas_service_validate_url MUST be provided")
59
+ end
60
+ @service_validate_url = params[:cas_service_validate_url]
61
+ @service_validate_url ||= DEFAULT_SERVICE_VALIDATE_URL % params[:cas_server]
62
+ validate_is_url 'service-validate URL', @service_validate_url
63
+ end
64
+
65
+ IS_NOT_URL_ERROR_MESSAGE = "%s is not a valid URL"
66
+
67
+ def validate_is_url(name, possibly_a_url)
68
+ url = URI.parse(possibly_a_url) rescue nil
69
+ raise ArgumentError.new(IS_NOT_URL_ERROR_MESSAGE % name) unless url.kind_of?(URI::HTTP)
70
+ end
71
+
72
+ # Adds +service+ as an URL-escaped parameter to +base+.
73
+ #
74
+ # @param [String] base the base URL
75
+ # @param [String] service the service (a.k.a. return-to) URL.
76
+ #
77
+ # @return [String] the new joined URL.
78
+ def append_service(base, service)
79
+ result = base.dup
80
+ result << (result.include?('?') ? '&' : '?')
81
+ result << 'service='
82
+ result << Rack::Utils.escape(service)
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,80 @@
1
+ require 'nokogiri'
2
+
3
+ module OmniAuth
4
+ module Strategies
5
+ class CAS
6
+ class ServiceTicketValidator
7
+
8
+ VALIDATION_REQUEST_HEADERS = { 'Accept' => '*/*' }
9
+
10
+ # Build a validator from a +configuration+, a
11
+ # +return_to+ URL, and a +ticket+.
12
+ #
13
+ # @param [OmniAuth::Strategies::CAS::Configuration] configuration the CAS configuration
14
+ # @param [String] return_to_url the URL of this CAS client service
15
+ # @param [String] ticket the service ticket to validate
16
+ def initialize(configuration, return_to_url, ticket)
17
+ @uri = URI.parse(configuration.service_validate_url(return_to_url, ticket))
18
+ end
19
+
20
+ # Request validation of the ticket from the CAS server's
21
+ # serviceValidate (CAS 2.0) function.
22
+ #
23
+ # Swallows all XML parsing errors (and returns +nil+ in those cases).
24
+ #
25
+ # @return [Hash, nil] a user information hash if the response is valid; +nil+ otherwise.
26
+ #
27
+ # @raise any connection errors encountered.
28
+ def user_info
29
+ parse_user_info(find_authentication_success(get_service_response_body))
30
+ end
31
+
32
+ private
33
+
34
+ # turns an <cas:authenticationSuccess> node into a Hash;
35
+ # returns nil if given nil
36
+ def parse_user_info(node)
37
+ return nil if node.nil?
38
+ node.children.inject({}) do |hash, child|
39
+ unless child.kind_of?(Nokogiri::XML::Text) ||
40
+ child.name == 'cas:proxies' ||
41
+ child.name == 'proxies'
42
+ hash[child.name.sub(/^cas:/, '')] = child.content
43
+ end
44
+ hash
45
+ end
46
+ end
47
+
48
+ # finds an <cas:authenticationSuccess> node in
49
+ # a <cas:serviceResponse> body if present; returns nil
50
+ # if the passed body is nil or if there is no such node.
51
+ def find_authentication_success(body)
52
+ return nil if body.nil? || body == ''
53
+ begin
54
+ doc = Nokogiri::XML(body)
55
+ begin
56
+ doc.xpath('/cas:serviceResponse/cas:authenticationSuccess')
57
+ rescue Nokogiri::XML::XPath::SyntaxError
58
+ doc.xpath('/serviceResponse/authenticationSuccess')
59
+ end
60
+ rescue Nokogiri::XML::XPath::SyntaxError
61
+ nil
62
+ end
63
+ end
64
+
65
+ # retrieves the <cas:serviceResponse> XML from the CAS server
66
+ def get_service_response_body
67
+ result = ''
68
+ http = Net::HTTP.new(@uri.host, @uri.port)
69
+ http.use_ssl = @uri.port == 443 || @uri.instance_of?(URI::HTTPS)
70
+ http.start do |c|
71
+ response = c.get "#{@uri.path}?#{@uri.query}", VALIDATION_REQUEST_HEADERS
72
+ result = response.body
73
+ end
74
+ result
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ 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 = {})
12
+ super(app, options.delete(:name) || :cas)
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) unless ticket
32
+ validator = ServiceTicketValidator.new(@configuration, callback_url, ticket)
33
+ @user_info = validator.user_info
34
+ return fail!(:invalid_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
metadata ADDED
@@ -0,0 +1,198 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oa-enterprise
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - James A. Rosen
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-16 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ version_requirements: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - "="
26
+ - !ruby/object:Gem::Version
27
+ hash: 23
28
+ segments:
29
+ - 0
30
+ - 0
31
+ - 4
32
+ version: 0.0.4
33
+ requirement: *id001
34
+ name: oa-core
35
+ prerelease: false
36
+ type: :runtime
37
+ - !ruby/object:Gem::Dependency
38
+ version_requirements: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 1
46
+ - 4
47
+ - 2
48
+ version: 1.4.2
49
+ requirement: *id002
50
+ name: nokogiri
51
+ prerelease: false
52
+ type: :runtime
53
+ - !ruby/object:Gem::Dependency
54
+ version_requirements: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirement: *id003
64
+ name: rake
65
+ prerelease: false
66
+ type: :development
67
+ - !ruby/object:Gem::Dependency
68
+ version_requirements: &id004 !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ hash: 15
74
+ segments:
75
+ - 0
76
+ - 0
77
+ - 8
78
+ version: 0.0.8
79
+ requirement: *id004
80
+ name: mg
81
+ prerelease: false
82
+ type: :development
83
+ - !ruby/object:Gem::Dependency
84
+ version_requirements: &id005 !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ hash: 27
90
+ segments:
91
+ - 1
92
+ - 3
93
+ - 0
94
+ version: 1.3.0
95
+ requirement: *id005
96
+ name: rspec
97
+ prerelease: false
98
+ type: :development
99
+ - !ruby/object:Gem::Dependency
100
+ version_requirements: &id006 !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ~>
104
+ - !ruby/object:Gem::Version
105
+ hash: 19
106
+ segments:
107
+ - 1
108
+ - 3
109
+ - 4
110
+ version: 1.3.4
111
+ requirement: *id006
112
+ name: webmock
113
+ prerelease: false
114
+ type: :development
115
+ - !ruby/object:Gem::Dependency
116
+ version_requirements: &id007 !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ~>
120
+ - !ruby/object:Gem::Version
121
+ hash: 3
122
+ segments:
123
+ - 0
124
+ - 5
125
+ - 4
126
+ version: 0.5.4
127
+ requirement: *id007
128
+ name: rack-test
129
+ prerelease: false
130
+ type: :development
131
+ - !ruby/object:Gem::Dependency
132
+ version_requirements: &id008 !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ~>
136
+ - !ruby/object:Gem::Version
137
+ hash: 1
138
+ segments:
139
+ - 1
140
+ - 4
141
+ - 3
142
+ version: 1.4.3
143
+ requirement: *id008
144
+ name: json
145
+ prerelease: false
146
+ type: :development
147
+ description: Enterprise strategies for OmniAuth.
148
+ email: james.a.rosen@gmail.com
149
+ executables: []
150
+
151
+ extensions: []
152
+
153
+ extra_rdoc_files: []
154
+
155
+ files:
156
+ - lib/omniauth/enterprise.rb
157
+ - lib/omniauth/strategies/cas/configuration.rb
158
+ - lib/omniauth/strategies/cas/service_ticket_validator.rb
159
+ - lib/omniauth/strategies/cas.rb
160
+ - README.rdoc
161
+ - LICENSE.rdoc
162
+ - CHANGELOG.rdoc
163
+ has_rdoc: true
164
+ homepage: http://github.com/intridea/omniauth
165
+ licenses: []
166
+
167
+ post_install_message:
168
+ rdoc_options: []
169
+
170
+ require_paths:
171
+ - lib
172
+ required_ruby_version: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ hash: 3
178
+ segments:
179
+ - 0
180
+ version: "0"
181
+ required_rubygems_version: !ruby/object:Gem::Requirement
182
+ none: false
183
+ requirements:
184
+ - - ">="
185
+ - !ruby/object:Gem::Version
186
+ hash: 3
187
+ segments:
188
+ - 0
189
+ version: "0"
190
+ requirements: []
191
+
192
+ rubyforge_project:
193
+ rubygems_version: 1.3.7
194
+ signing_key:
195
+ specification_version: 3
196
+ summary: Enterprise strategies for OmniAuth.
197
+ test_files: []
198
+