castanet 0.0.1

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.
@@ -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
+