castanet 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,172 @@
1
+ require 'castanet'
2
+
3
+ %%{
4
+ machine ticket_validate;
5
+
6
+ action save_username { r.username = buffer; buffer = '' }
7
+ action save_failure_code { r.failure_code = buffer; buffer = '' }
8
+ action save_failure_reason { r.failure_reason = buffer.strip; buffer = '' }
9
+ action save_pgt_iou { r.pgt_iou = buffer; buffer = '' }
10
+ action save_proxy { r.proxies << buffer; buffer = '' }
11
+ action set_authenticated { r.ok = true }
12
+
13
+ include common "common.rl";
14
+
15
+ # Leaf tags
16
+ # ---------
17
+
18
+ pgt_iou = "<cas:proxyGrantingTicket>"
19
+ ticket @buffer
20
+ "</cas:proxyGrantingTicket>" %save_pgt_iou;
21
+ proxy = "<cas:proxy>"
22
+ char_data @buffer
23
+ "</cas:proxy>" %save_proxy;
24
+ user = "<cas:user>"
25
+ char_data @buffer
26
+ "</cas:user>" %save_username;
27
+
28
+ # Non-leaf tags
29
+ # -------------
30
+
31
+ authentication_failure_start = "<cas:authenticationFailure code="
32
+ quote
33
+ failure_code %save_failure_code
34
+ quote
35
+ ">";
36
+ authentication_failure_end = "</cas:authenticationFailure>";
37
+
38
+ authentication_success_start = "<cas:authenticationSuccess>";
39
+ authentication_success_end = "</cas:authenticationSuccess>";
40
+ proxies = "<cas:proxies>"
41
+ ( space* proxy space* )*
42
+ "</cas:proxies>";
43
+
44
+ # Top-level elements
45
+ # ------------------
46
+
47
+ ok_cas_st = ( service_response_start
48
+ space*
49
+ authentication_success_start
50
+ space*
51
+ user
52
+ space*
53
+ pgt_iou?
54
+ space*
55
+ proxies?
56
+ space*
57
+ authentication_success_end
58
+ space*
59
+ service_response_end ) @set_authenticated;
60
+
61
+ failed_cas_st = ( service_response_start
62
+ space*
63
+ authentication_failure_start
64
+ failure_reason %save_failure_reason
65
+ authentication_failure_end
66
+ space*
67
+ service_response_end );
68
+
69
+ main := ok_cas_st | failed_cas_st;
70
+ }%%
71
+
72
+ module Castanet::Responses
73
+ ##
74
+ # A parsed representation of responses from `/serviceValidate` or
75
+ # `/proxyValidate`.
76
+ #
77
+ # The responses for the above services are identical, so we implement their
78
+ # parser with the same state machine.
79
+ #
80
+ # The code in this class implements a state machine generated by Ragel. The
81
+ # state machine definition is in ticket_validate.rl.
82
+ #
83
+ # @see http://www.jasig.org/cas/protocol CAS 2.0 protocol, sections 2.5, 2.6,
84
+ # and appendix A
85
+ class TicketValidate
86
+ ##
87
+ # Whether or not this response passed CAS authentication.
88
+ #
89
+ # @return [Boolean]
90
+ attr_accessor :ok
91
+
92
+ alias_method :ok?, :ok
93
+
94
+ ##
95
+ # The failure code returned on authentication failure.
96
+ #
97
+ # @return [String, nil]
98
+ attr_accessor :failure_code
99
+
100
+ ##
101
+ # The reason given by the CAS server for authentication failure.
102
+ #
103
+ # @return [String, nil]
104
+ attr_accessor :failure_reason
105
+
106
+ ##
107
+ # The PGT IOU returned by an authentication success message.
108
+ #
109
+ # @return [String, nil]
110
+ attr_accessor :pgt_iou
111
+
112
+ ##
113
+ # A list of authentication proxies for this ticket.
114
+ #
115
+ # Each participant in an authentication chain adds one entry to this list.
116
+ # As an example, assume the existence of two services:
117
+ #
118
+ # 1. frontend
119
+ # 2. backend
120
+ #
121
+ # If `frontend` proxied access to `backend`, the proxy list would be
122
+ #
123
+ # 1. backend
124
+ # 2. frontend
125
+ #
126
+ # The proxy chain has an unbounded maximum length. The proxy order
127
+ # specified in the CAS response is preserved.
128
+ #
129
+ # For proxy tickets that fail validation, this will be an empty list. It
130
+ # should also be an empty list for service tickets too, although that's
131
+ # really up to the CAS server.
132
+ #
133
+ # Although this list is technically a valid component of an authentication
134
+ # response issued by `/serviceValidate`, it's really only applicable to
135
+ # proxy tickets.
136
+ #
137
+ # @see http://www.jasig.org/cas/protocol CAS 2.0 protocol, section 2.6.2
138
+ # @return [Array]
139
+ attr_accessor :proxies
140
+
141
+ ##
142
+ # The name of the owner of the validated service or proxy ticket.
143
+ #
144
+ # This information is only present on authentication success.
145
+ #
146
+ # @return [String, nil]
147
+ attr_accessor :username
148
+
149
+ ##
150
+ # Generates a {TicketValidate} object from a CAS response.
151
+ #
152
+ # @param [String] response the CAS response
153
+ # @return [TicketValidate}
154
+ def self.from_cas(response)
155
+ data = response.strip.unpack('U*')
156
+ buffer = ''
157
+
158
+ %% write init;
159
+
160
+ new.tap do |r|
161
+ %% write exec;
162
+ end
163
+ end
164
+
165
+ def initialize
166
+ self.ok = false
167
+ self.proxies = []
168
+ end
169
+
170
+ %% write data;
171
+ end
172
+ end
@@ -0,0 +1,180 @@
1
+ require 'castanet'
2
+
3
+ require 'forwardable'
4
+ require 'uri'
5
+
6
+ module Castanet
7
+ class ServiceTicket
8
+ extend Forwardable
9
+ include Responses
10
+ include QueryBuilding
11
+
12
+ ##
13
+ # Set this to `true` to _not_ use HTTPS for CAS server communication.
14
+ #
15
+ # In almost all cases where CAS is used, there is no good reason to avoid
16
+ # HTTPS. However, if you
17
+ #
18
+ # 1. need to have access to CAS server messages and
19
+ # 2. are in an isolated development environment
20
+ #
21
+ # then it may make sense to disable HTTPS.
22
+ #
23
+ # This is usually set by {Castanet::Client}.
24
+ #
25
+ # @return [Boolean]
26
+ attr_accessor :https_disabled
27
+
28
+ ##
29
+ # The proxy callback URL to use for service validation.
30
+ #
31
+ # @return [String, nil]
32
+ attr_accessor :proxy_callback_url
33
+
34
+ ##
35
+ # The URL of the service to use for retrieving PGTs.
36
+ #
37
+ # @return [String, nil]
38
+ attr_accessor :proxy_retrieval_url
39
+
40
+ ##
41
+ # The URL of the CAS server's serviceValidate service.
42
+ #
43
+ # @return [String, nil]
44
+ attr_accessor :service_validate_url
45
+
46
+ ##
47
+ # The wrapped service ticket.
48
+ #
49
+ # @return [String, nil]
50
+ attr_reader :ticket
51
+
52
+ ##
53
+ # The wrapped service URL.
54
+ #
55
+ # @return [String, nil]
56
+ attr_reader :service
57
+
58
+ ##
59
+ # The response from the CAS server.
60
+ #
61
+ # {ServiceTicket} sets this attribute whilst executing {#present!}, but it
62
+ # can be manually set for e.g. testing purposes.
63
+ #
64
+ # @return [#ok?, #pgt_iou]
65
+ attr_accessor :response
66
+
67
+ def_delegators :response, :ok?, :pgt_iou, :username
68
+
69
+ ##
70
+ # The PGT associated with this service ticket.
71
+ #
72
+ # This is set after a successful invocation of {#retrieve_pgt!}.
73
+ #
74
+ # @return [String, nil]
75
+ attr_accessor :pgt
76
+
77
+ def initialize(ticket, service)
78
+ @service = service
79
+ @ticket = ticket
80
+ end
81
+
82
+ ##
83
+ # Validates `ticket` for the service URL given in `service`. If
84
+ # {#proxy_callback_url} is not nil, also attempts to retrieve the PGTIOU
85
+ # for this service ticket.
86
+ #
87
+ # CAS service tickets are one-time-use only
88
+ # =========================================
89
+ #
90
+ # This method checks `ticket` against `service` using the CAS server, so you
91
+ # must take care to only validate a given `ticket` _once_.
92
+ #
93
+ # Since ServiceTicket does not maintain any state with regard to whether a
94
+ # ServiceTicket instance has already been presented, multiple presentations
95
+ # of the same ticket will result in behavior like this:
96
+ #
97
+ # st = service_ticket(ticket, service)
98
+ # st.present!
99
+ #
100
+ # st.ok? # => true
101
+ #
102
+ # st.present!
103
+ #
104
+ # st.ok? # => false
105
+ #
106
+ # @see http://www.jasig.org/cas/protocol CAS 2.0 protocol, sections 2.5 and
107
+ # 3.1.1
108
+ #
109
+ # @return void
110
+ def present!
111
+ uri = URI.parse(validation_url).tap do |u|
112
+ u.query = validation_parameters
113
+ end
114
+
115
+ http = Net::HTTP.new(uri.host, uri.port).tap do |h|
116
+ h.use_ssl = !https_disabled
117
+ end
118
+
119
+ http.start do |h|
120
+ cas_response = h.get(uri.to_s)
121
+
122
+ self.response = parsed_ticket_validate_response(cas_response.body)
123
+ end
124
+ end
125
+
126
+ ##
127
+ # Retrieves a PGT from {#proxy_retrieval_url} using the PGT IOU.
128
+ #
129
+ # CAS 2.0 does not specify whether PGTIOUs are one-time-use only.
130
+ # Therefore, Castanet does not prevent multiple invocations of
131
+ # `retrieve_pgt!`; however, it is safest to assume that PGTIOUs are
132
+ # one-time-use only.
133
+ #
134
+ # CAS 2.0 also does not specify the response format for proxy callbacks.
135
+ # `retrieve_pgt!` assumes that a `200` response from {#proxy_retrieval_url}
136
+ # will contain the PGT and only the PGT.
137
+ #
138
+ # The retrieved PGT will be written to {#pgt} if this method succeeds.
139
+ #
140
+ # @return void
141
+ def retrieve_pgt!
142
+ uri = URI.parse(proxy_retrieval_url).tap do |u|
143
+ u.query = query(['pgtIou', pgt_iou])
144
+ end
145
+
146
+ http = Net::HTTP.new(uri.host, uri.port).tap do |h|
147
+ h.use_ssl = !https_disabled
148
+ end
149
+
150
+ http.start do |h|
151
+ self.pgt = h.get(uri.to_s).body
152
+ end
153
+ end
154
+
155
+ protected
156
+
157
+ ##
158
+ # The URL to use for ticket validation.
159
+ #
160
+ # @return [String]
161
+ def validation_url
162
+ service_validate_url
163
+ end
164
+
165
+ private
166
+
167
+ ##
168
+ # Builds a query string for use with the `serviceValidate` service.
169
+ #
170
+ # @see http://www.jasig.org/cas/protocol CAS 2.0 protocol, section 2.5.1
171
+ # @param [String] ticket a service ticket
172
+ # @param [String] service a service URL
173
+ # @return [String] a query component of a URI
174
+ def validation_parameters
175
+ query(['ticket', ticket],
176
+ ['service', service],
177
+ ['pgtUrl', proxy_callback_url])
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,3 @@
1
+ module Castanet
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,184 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: castanet
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - David Yip
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-03 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: autotest
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ prerelease: false
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: ci_reporter
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :development
44
+ prerelease: false
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: cucumber
48
+ requirement: &id003 !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ segments:
54
+ - 0
55
+ version: "0"
56
+ type: :development
57
+ prerelease: false
58
+ version_requirements: *id003
59
+ - !ruby/object:Gem::Dependency
60
+ name: mechanize
61
+ requirement: &id004 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ segments:
67
+ - 0
68
+ version: "0"
69
+ type: :development
70
+ prerelease: false
71
+ version_requirements: *id004
72
+ - !ruby/object:Gem::Dependency
73
+ name: rack
74
+ requirement: &id005 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: *id005
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec
87
+ requirement: &id006 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ segments:
93
+ - 2
94
+ - 0
95
+ version: "2.0"
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: *id006
99
+ - !ruby/object:Gem::Dependency
100
+ name: webmock
101
+ requirement: &id007 !ruby/object:Gem::Requirement
102
+ none: false
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ segments:
107
+ - 0
108
+ version: "0"
109
+ type: :development
110
+ prerelease: false
111
+ version_requirements: *id007
112
+ - !ruby/object:Gem::Dependency
113
+ name: yard
114
+ requirement: &id008 !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ type: :development
123
+ prerelease: false
124
+ version_requirements: *id008
125
+ description: A small, snappy CAS 2.0 client library for Ruby applications
126
+ email:
127
+ - yipdw@northwestern.edu
128
+ executables: []
129
+
130
+ extensions: []
131
+
132
+ extra_rdoc_files: []
133
+
134
+ files:
135
+ - README.md
136
+ - History.md
137
+ - LICENSE
138
+ - lib/castanet/client.rb
139
+ - lib/castanet/proxy_ticket.rb
140
+ - lib/castanet/proxy_ticket_error.rb
141
+ - lib/castanet/query_building.rb
142
+ - lib/castanet/responses/common.rl
143
+ - lib/castanet/responses/proxy.rb
144
+ - lib/castanet/responses/proxy.rl
145
+ - lib/castanet/responses/ticket_validate.rb
146
+ - lib/castanet/responses/ticket_validate.rl
147
+ - lib/castanet/responses.rb
148
+ - lib/castanet/service_ticket.rb
149
+ - lib/castanet/version.rb
150
+ - lib/castanet.rb
151
+ has_rdoc: true
152
+ homepage: ""
153
+ licenses: []
154
+
155
+ post_install_message:
156
+ rdoc_options: []
157
+
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ segments:
166
+ - 0
167
+ version: "0"
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ segments:
174
+ - 0
175
+ version: "0"
176
+ requirements: []
177
+
178
+ rubyforge_project:
179
+ rubygems_version: 1.3.7
180
+ signing_key:
181
+ specification_version: 3
182
+ summary: A CAS client library
183
+ test_files: []
184
+