I wanted to code a JSF application where the login page is a JSF page rather
than a CAS page. I see this as a use case that cannot be overlooked in
present day application development.

I understand that it is discouraged to present credentials to an application
but an applications security model shouldn't have to be custom for every
security solution plugged in.

In my case I'm using spring security as the mechanism to glue the security
stuff together. If I switch from plain security to CAS security it seems
that all documentation points to me doing custom things for the login page
such as iframe etc..

So here's what I've come up with. I'd appreciate feedback on this solution:

In login-webflow.xml I made it so if ticketGrantingTicketId is passed as a
parameter on the /cas/login URL then
Service cookie can be issued directly using the ticketGrantingTicketId. In
my case I get the ticketGrantingTicketId using the CAS restful api.

        <action-state id="initialFlowSetup">
                <action bean="initialFlowSetupAction" />
<!-- garpinc replace -->
<!--
                <transition on="success"
to="ticketGrantingTicketExistsCheck" />
-->
<!-- garpinc with -->
                <transition on="success"
to="ticketGrantingTicketIdExistsCheck" />
<!-- garpinc end replace -->
        </action-state>

        <!-- added by garpinc -->
        <decision-state id="ticketGrantingTicketIdExistsCheck">
                <if test="${requestParameters.ticketGrantingTicketId ==
null}" then="ticketGrantingTicketExistsCheck"
else="populateFromRequestParams" />
        </decision-state>

        <action-state id="populateFromRequestParams">
                <set attribute="ticketGrantingTicketId" 
                     value="requestParameters.ticketGrantingTicketId"
                     scope="flow"/>
                <transition on="success" to="sendTicketGrantingTicket" />
        </action-state>
        <!-- end of garpinc add -->


Then I define a JSF controller. The pseudo steps are as follows
1) username/password provided to jsf page
2) adaptAuthenticationRequest is called to change this into an
Authentication CasAuthenticationProvider can process
3) user is authenticated using provider
4) to participate in SSO getFinalOutcome is called which redirects to
/cas/login with ticketGrantingTicketId and service which is page you want to
go to on successful login.

public class CasAuthenticationController extends
                AbstractAuthenticationController {
        
        private static final String CAS_TGR = "CAS_TGR";
        private ServiceProperties serviceProperties;

        public void setServiceProperties(ServiceProperties
serviceProperties) {
                this.serviceProperties = serviceProperties;
        }
        
        private String secureCasURL;

        public void setSecureCasURL(String secureCasURL) {
                this.secureCasURL = secureCasURL;
        }

        private String ticketURL;

        public void setTicketURL(String ticketURL) {
                this.ticketURL = ticketURL;
        }
        
        private boolean useCasSSO = true;

        public void setUseCasSSO(boolean useCasSSO) {
                this.useCasSSO = useCasSSO;
        }


        protected void updateFacesContext(Authentication aut) {

        }


        final protected String getFinalOutcome(String outcome) {
                
                if (outcome.equals("success") && useCasSSO) {
                        FacesContext ctx =
FacesContext.getCurrentInstance();
                        ExternalContext extCtx = ctx.getExternalContext();

                        HttpServletRequest request =    
                        (HttpServletRequest) extCtx.getRequest();
                        String ticket = (String)
request.getAttribute(CAS_TGR);

                        try {
                                extCtx.redirect(
                                                  secureCasURL
                                                + "/login?service="
                                                + request.getScheme() +
"://" + request.getServerName() + ":" + request.getServerPort() +
request.getContextPath()
                                                +
"/view/secure/facelet/content/home.iface&ticketGrantingTicketId=" + ticket);
                                // this is ignored because extCtx.redirect
                                return "redirect";
                        } catch (IOException e) {
                                throw new RuntimeException(e);
                        }
                        
                } else {
                        return outcome;
                }
        }
        
        protected Authentication adaptAuthenticationRequest(
                        UsernamePasswordAuthenticationToken
usernamePasswordAuthenticationToken) throws Exception {

        HttpClient client = new HttpClient();

        PostMethod post = new PostMethod(ticketURL);
        NameValuePair[] usernamePasswordArray = {
                        new
NameValuePair("username",usernamePasswordAuthenticationToken.getPrincipal().
toString()),
                        new
NameValuePair("password",usernamePasswordAuthenticationToken.getCredentials(
).toString())
        };
        post.setRequestBody(usernamePasswordArray);
        int responseCode = client.executeMethod(post);
        if (responseCode != 201) {
                String body = post.getResponseBodyAsString();
                Pattern pattern = Pattern.compile(".*<h3>(.*)</h3>.*",
Pattern.DOTALL);
                Matcher matcher = pattern.matcher(body);
                if (matcher.matches()) {
                        String casError = matcher.group(1);
                throw new AuthenticationServiceException(casError);
                } else {
                        throw new RuntimeException("body does not contain an
error:" + body);
                }

        }
        String ticketGrantingResource =
post.getResponseHeader("Location").getValue();
        
        Pattern pattern = Pattern.compile(".*\\/(.*)");
        Matcher matcher = pattern.matcher(ticketGrantingResource);
        if (matcher.matches()) {
                String ticketGrantingResourceToStore = matcher.group(1);
                // Update cookie in browser for SSO
                HttpServletRequest request =    
                        (HttpServletRequest)
FacesContext.getCurrentInstance()
 
.getExternalContext().getRequest();
                request.setAttribute(CAS_TGR,
ticketGrantingResourceToStore);

        } else {
                throw new RuntimeException("ticketGrantingResource not in
right format:" + ticketGrantingResource);
        }
        
        post = new PostMethod(ticketGrantingResource);
        NameValuePair[] serviceArray = {
                        new
NameValuePair("service",serviceProperties.getService()),
        };
        
        post.setRequestBody(serviceArray);
        responseCode = client.executeMethod(post);
        if (responseCode != 200) {
                throw new RuntimeException("response code was: " +
responseCode);
        }
        String ticket = post.getResponseBodyAsString();
        
        return new UsernamePasswordAuthenticationToken(
                        CasProcessingFilter.CAS_STATEFUL_IDENTIFIER,
                        ticket);

        }


        
}

<<attachment: winmail.dat>>

_______________________________________________
Yale CAS mailing list
cas@tp.its.yale.edu
http://tp.its.yale.edu/mailman/listinfo/cas

Reply via email to