Class: RubyCAS::Filter
- Inherits:
-
Object
- Object
- RubyCAS::Filter
- Defined in:
- lib/rubycas-client-rails.rb
Direct Known Subclasses
Defined Under Namespace
Classes: GatewayFilter
Constant Summary collapse
- @@client =
These are initialized when you call setup.
nil
- @@log =
nil
- @@fake_user =
nil
- @@fake_extra_attributes =
nil
Class Method Summary collapse
-
.fake(username, extra_attributes = nil) ⇒ Object
used to allow faking for testing with cucumber and other tools.
- .filter(controller) ⇒ Object
-
.login_to_service(controller, credentials, return_path) ⇒ Object
allow controllers to reuse the existing config to auto-login to the service.
-
.login_url(controller) ⇒ Object
Returns the login URL for the current controller.
-
.logout(controller, service = nil) ⇒ Object
Clears the given controller’s local Rails session, does some local CAS cleanup, and redirects to the CAS logout page.
- .redirect_to_cas_for_authentication(controller) ⇒ Object
- .setup(config) ⇒ Object
- .unauthorized!(controller, vr = nil) ⇒ Object
- .use_gatewaying? ⇒ Boolean
Class Method Details
.fake(username, extra_attributes = nil) ⇒ Object
used to allow faking for testing with cucumber and other tools. use like
CASClient::Frameworks::Rails::Filter.fake("homer")
you can also fake extra attributes by including a second parameter
CASClient::Frameworks::Rails::Filter.fake("homer", {:roles => ['dad', 'husband']})
162 163 164 165 |
# File 'lib/rubycas-client-rails.rb', line 162 def fake(username, extra_attributes = nil) @@fake_user = username @@fake_extra_attributes = extra_attributes end |
.filter(controller) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/rubycas-client-rails.rb', line 30 def filter(controller) raise "Cannot use the CASClient filter because it has not yet been configured." if config.nil? if @@fake_user controller.session[client.username_session_key] = @@fake_user controller.session[:casfilteruser] = @@fake_user controller.session[client.extra_attributes_session_key] = @@fake_extra_attributes return true end last_st = controller.session[:cas_last_valid_ticket] if single_sign_out(controller) controller.send(:render, :text => "CAS Single-Sign-Out request intercepted.") return false end st = read_ticket(controller) is_new_session = true if st && last_st && last_st.ticket == st.ticket && last_st.service == st.service # warn() rather than info() because we really shouldn't be re-validating the same ticket. # The only situation where this is acceptable is if the user manually does a refresh and # the same ticket happens to be in the URL. log.warn("Re-using previously validated ticket since the ticket id and service are the same.") st = last_st is_new_session = false elsif last_st && !config[:authenticate_on_every_request] && controller.session[client.username_session_key] # Re-use the previous ticket if the user already has a local CAS session (i.e. if they were already # previously authenticated for this service). This is to prevent redirection to the CAS server on every # request. # # This behaviour can be disabled (so that every request is routed through the CAS server) by setting # the :authenticate_on_every_request config option to true. However, this is not desirable since # it will almost certainly break POST request, AJAX calls, etc. log.debug "Existing local CAS session detected for #{controller.session[client.username_session_key].inspect}. "+ "Previous ticket #{last_st.ticket.inspect} will be re-used." st = last_st is_new_session = false end if st client.validate_service_ticket(st) unless st.has_been_validated? vr = st.response if st.is_valid? if is_new_session log.info("Ticket #{st.ticket.inspect} for service #{st.service.inspect} belonging to user #{vr.user.inspect} is VALID.") controller.session[client.username_session_key] = vr.user.dup controller.session[client.extra_attributes_session_key] = HashWithIndifferentAccess.new(vr.extra_attributes) if vr.extra_attributes if vr.extra_attributes log.debug("Extra user attributes provided along with ticket #{st.ticket.inspect}: #{vr.extra_attributes.inspect}.") end # RubyCAS-Client 1.x used :casfilteruser as it's username session key, # so we need to set this here to ensure compatibility with configurations # built around the old client. controller.session[:casfilteruser] = vr.user if config[:enable_single_sign_out] f = store_service_session_lookup(st, controller.request.[:id] || controller.session.session_id) log.debug("Wrote service session lookup file to #{f.inspect} with session id #{controller.request.[:id] || controller.session.session_id.inspect}.") end end # Store the ticket in the session to avoid re-validating the same service # ticket with the CAS server. controller.session[:cas_last_valid_ticket] = st if vr.pgt_iou unless controller.session[:cas_pgt] && controller.session[:cas_pgt].ticket && controller.session[:cas_pgt].iou == vr.pgt_iou log.info("Receipt has a proxy-granting ticket IOU. Attempting to retrieve the proxy-granting ticket...") pgt = client.retrieve_proxy_granting_ticket(vr.pgt_iou) if pgt log.debug("Got PGT #{pgt.ticket.inspect} for PGT IOU #{pgt.iou.inspect}. This will be stored in the session.") controller.session[:cas_pgt] = pgt # For backwards compatibility with RubyCAS-Client 1.x configurations... controller.session[:casfilterpgt] = pgt else log.error("Failed to retrieve a PGT for PGT IOU #{vr.pgt_iou}!") end else log.info("PGT is present in session and PGT IOU #{vr.pgt_iou} matches the saved PGT IOU. Not retrieving new PGT.") end end return true else log.warn("Ticket #{st.ticket.inspect} failed validation -- #{vr.failure_code}: #{vr.}") (controller, vr) return false end else # no service ticket was present in the request if returning_from_gateway?(controller) log.info "Returning from CAS gateway without authentication." # unset, to allow for the next request to be authenticated if necessary controller.session[:cas_sent_to_gateway] = false if log.info "This CAS client is configured to use gatewaying, so we will permit the user to continue without authentication." controller.session[client.username_session_key] = nil return true else log.warn "The CAS client is NOT configured to allow gatewaying, yet this request was gatewayed. Something is not right!" end end (controller) return false end rescue OpenSSL::SSL::SSLError log.error("SSL Error: hostname was not match with the server certificate. You can try to disable the ssl verification with a :force_ssl_verification => false in your configurations file.") (controller) return false end |
.login_to_service(controller, credentials, return_path) ⇒ Object
allow controllers to reuse the existing config to auto-login to the service
Use this from within a controller. Pass the controller, the login-credentials and the path that you want the user resdirected to on success.
When writing a login-action you must check the return-value of the response to see if it failed!
If it worked - you need to redirect the user to the service - path, because that has the ticket that will actually log them into your system
example: def autologin
resp = CASClient::Frameworks::Rails::Filter.login_to_service(self, credentials, dashboard_url)
if resp.is_faiulure?
flash[:error] = 'Login failed'
render :action => 'login'
else
return redirect_to(@resp.service_redirect_url)
end
end
205 206 207 208 209 210 211 212 213 214 |
# File 'lib/rubycas-client-rails.rb', line 205 def login_to_service(controller, credentials, return_path) resp = @@client.login_to_service(credentials, return_path) if resp.is_failure? log.info("Validation failed for service #{return_path.inspect} reason: '#{resp.}'") else log.info("Ticket #{resp.ticket.inspect} for service #{return_path.inspect} is VALID.") end resp end |
.login_url(controller) ⇒ Object
Returns the login URL for the current controller. Useful when you want to provide a “Login” link in a GatewayFilter’ed action.
174 175 176 177 178 179 |
# File 'lib/rubycas-client-rails.rb', line 174 def login_url(controller) service_url = read_service_url(controller) url = client.add_service_to_login_url(service_url) log.debug("Generated login url: #{url}") return url end |
.logout(controller, service = nil) ⇒ Object
Clears the given controller’s local Rails session, does some local CAS cleanup, and redirects to the CAS logout page. Additionally, the request.referer
value from the controller
instance is passed to the CAS server as a ‘destination’ parameter. This allows RubyCAS server to provide a follow-up login page allowing the user to log back in to the service they just logged out from using a different username and password. Other CAS server implemenations may use this ‘destination’ parameter in different ways. If given, the optional service
URL overrides request.referer
.
227 228 229 230 231 232 233 |
# File 'lib/rubycas-client-rails.rb', line 227 def logout(controller, service = nil) referer = service || controller.request.referer st = controller.session[:cas_last_valid_ticket] delete_service_session_lookup(st) if st controller.send(:reset_session) controller.send(:redirect_to, client.logout_url(referer)) end |
.redirect_to_cas_for_authentication(controller) ⇒ Object
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
# File 'lib/rubycas-client-rails.rb', line 247 def redirect_to_cas_for_authentication(controller) redirect_url = login_url(controller) if controller.session[:cas_sent_to_gateway] = true redirect_url << "&gateway=true" else controller.session[:cas_sent_to_gateway] = false end if controller.session[:previous_redirect_to_cas] && controller.session[:previous_redirect_to_cas] > (Time.now - 1.second) log.warn("Previous redirect to the CAS server was less than a second ago. The client at #{controller.request.remote_ip.inspect} may be stuck in a redirection loop!") controller.session[:cas_validation_retry_count] ||= 0 if controller.session[:cas_validation_retry_count] > 3 log.error("Redirection loop intercepted. Client at #{controller.request.remote_ip.inspect} will be redirected back to login page and forced to renew authentication.") redirect_url += "&renew=1&redirection_loop_intercepted=1" end controller.session[:cas_validation_retry_count] += 1 else controller.session[:cas_validation_retry_count] = 0 end controller.session[:previous_redirect_to_cas] = Time.now log.debug("Redirecting to #{redirect_url.inspect}") controller.send(:redirect_to, redirect_url) end |
.setup(config) ⇒ Object
23 24 25 26 27 28 |
# File 'lib/rubycas-client-rails.rb', line 23 def setup(config) @@config = config @@config[:logger] = Rails.logger unless @@config[:logger] @@client = CASClient::Client.new(@@config) @@log = @@client.log end |
.unauthorized!(controller, vr = nil) ⇒ Object
235 236 237 238 239 240 241 242 243 244 245 |
# File 'lib/rubycas-client-rails.rb', line 235 def (controller, vr = nil) if controller.params[:format] == "xml" if vr controller.send(:render, :xml => "<errors><error>#{vr.}</error></errors>", :status => 401) else controller.send(:head, 401) end else redirect_to_cas_for_authentication(controller) end end |
.use_gatewaying? ⇒ Boolean
167 168 169 |
# File 'lib/rubycas-client-rails.rb', line 167 def @@config[:use_gatewaying] end |