Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

The CAS web server application uses Spring Web Flow and the URL handler ultimately sends the login request to GenerateServiceTicketAction. This is where the service ticket is placed in Request scope to be sent to the CAS client filter.

Code Block
borderStylesolid
titleGenerateServiceTicketAction.java (CAS Server)borderStylesolid
public final class GenerateServiceTicketAction extends AbstractAction {
    
    /** Instance of CentralAuthenticationService. */
    @NotNull
    private CentralAuthenticationService centralAuthenticationService;

    protected Event doExecute(final RequestContext context) {
        final Service service = WebUtils.getService(context);
        final String ticketGrantingTicket = WebUtils.getTicketGrantingTicketId(context);

        try {
            final String serviceTicketId = this.centralAuthenticationService
                .grantServiceTicket(ticketGrantingTicket,
                    service);
            WebUtils.putServiceTicketInRequestScope(context,
                serviceTicketId);
            return success();
        } catch (final TicketException e) {
            if (isGatewayPresent(context)) {
                return result("gateway");
            }
        }

        return error();
    }
 ...

When the ticket has been placed in the request scope, CAS will now redirect the user request to the application using the service URL and the service ticket generated. The default in the switch-case block would handle the redirected request now to the application server.

Code Block
borderStylesolid
titleDynamicRedirectViewSelector.java (CAS Server)borderStylesolid
public final class DynamicRedirectViewSelector implements ViewSelector {

    public ViewSelection makeRefreshSelection(final RequestContext context) {
        return makeEntrySelection(context);
    }

    public boolean isEntrySelectionRenderable(final RequestContext request) {
        return false;
    }

    public ViewSelection makeEntrySelection(final RequestContext request) {
        final WebApplicationService service = WebUtils.getService(request);
        final String ticket = WebUtils.getServiceTicketFromRequestScope(request);
        final Response serviceResponse = service.getResponse(ticket);
            
        switch (serviceResponse.getResponseType()) {
            case POST:
                final Map<String, Object> model = new HashMap<String, Object>();
                model.put("parameters", serviceResponse.getAttributes());
                model.put("originalUrl", service.getId());
                return new ApplicationView("postResponseView", model);
                
            default:
                return new ExternalRedirect(service.getResponse(ticket).getUrl());
        }
    }
}

<STOP!> This is a critical timepoint. The CAS server has processed the user's request and the result of this redirection would be the expected application content. In the interim, before this request is processed, the application server using CAS client code will send an HTTPS request to the CAS server and have it authenticate the requesting user sharing no connection or resources with the original CAS user request. The CAS client servlet filter here sends the HTTPS request for user authentication. Ignore the proxy list for now, but notice the proxy validator gathers the service and the ticket. This method returns the user returned by the CAS server over HTTPS and within the CAS returning XML message.

Code Block
borderStylesolid
titleCASFilter.java (CAS Client)borderStylesolid
...
    private String getAuthenticatedUser(HttpServletRequest request)
        throws ServletException {
        ProxyTicketValidator pv = null;
        try {
            pv = new ProxyTicketValidator();
            pv.setCasValidateUrl(casValidate);
            pv.setServiceTicket(request.getParameter("ticket"));
            pv.setService(getService(request));
	    pv.setRenew(Boolean.valueOf(casRenew).booleanValue());
            pv.validate();
            if (!pv.isAuthenticationSuccesful())
                throw new ServletException(
                    "CAS authentication error: " + pv.getErrorCode() + ": " + pv.getErrorMessage());
            if (pv.getProxyList().size() != 0) {
                // ticket was proxied
                if (casAuthorizedProxy == null) {
                    throw new ServletException("this page does not accept proxied tickets");
                } else {
                    boolean authorized = false;
                    String proxy = (String)pv.getProxyList().get(0);
                    StringTokenizer casProxies =
                        new StringTokenizer(casAuthorizedProxy);
                    while (casProxies.hasMoreTokens()) {
                        if (proxy.equals(casProxies.nextToken())) {
                            authorized = true;
                            break;
                        }
                    }
                    if (!authorized) {
                        throw new ServletException(
                            "unauthorized top-level proxy: '"
                                + pv.getProxyList().get(0)
                                + "'");
                    }
                }
            }
            return pv.getUser();
        } catch (SAXException ex) {
            String xmlResponse = "";
            if (pv != null)
                xmlResponse = pv.getResponse();
            throw new ServletException(ex + " " + xmlResponse);
        } catch (ParserConfigurationException ex) {
            throw new ServletException(ex);
        } catch (IOException ex) {
            throw new ServletException(ex);
        }
    }

...

The return pv.getUser() method comes from the class ServiceTicketValidator. ProxyTicketValidator (pv) extends ServiceTicketValidator. The validate() method will populate the user member if the CAS XML message comes back successfully. This method is where the separate HTTPS request is created to validate the questioning user.

Code Block
borderStylesolid
titleServiceTicketValidator.java (CAS Client)borderStylesolid
...
  public void validate()
      throws IOException, SAXException, ParserConfigurationException {
    if (casValidateUrl == null || st == null)
      throw new IllegalStateException("must set validation URL and ticket");
    clear();
    attemptedAuthentication = true;
    StringBuffer sb = new StringBuffer();
    sb.append(casValidateUrl);
    if (casValidateUrl.indexOf('?') == -1)
      sb.append('?');
    else
      sb.append('&');
    sb.append("service=" + service + "&ticket=" + st);
    if (proxyCallbackUrl != null)
      sb.append("&pgtUrl=" + proxyCallbackUrl);
    if (renew)
      sb.append("&renew=true");
    String url = sb.toString();
    String response = SecureURL.retrieve(url);
    this.entireResponse = response;

    // parse the response and set appropriate properties
    if (response != null) {
      XMLReader r =
        SAXParserFactory.newInstance().newSAXParser().getXMLReader();
      r.setFeature("http://xml.org/sax/features/namespaces", false);
      r.setContentHandler(newHandler());
      r.parse(new InputSource(new StringReader(response)));
    }
  }
...

...