oa-enterprise 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
+