CAS without CAS login page using restful api and modifiedlogin-webflow.xml

Keith Garry Boyce garry at consultsure.com
Sat Jan 17 08:07:17 EST 2009


Anyone?

> _____________________________________________ 
> From: 	cas-bounces at tp.its.yale.edu
> [mailto:cas-bounces at tp.its.yale.edu]  On Behalf Of Keith Garry Boyce
> Sent:	Friday, January 16, 2009 9:53 AM
> To:	cas at tp.its.yale.edu
> Subject:	CAS without CAS login page using restful api and
> modifiedlogin-webflow.xml
> 
> 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.getCredential
> s().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);
> 
> 	}
> 
> 
> 	
> } << File: ATT00037.txt >> 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: winmail.dat
Type: application/ms-tnef
Size: 4990 bytes
Desc: not available
Url : http://tp.its.yale.edu/pipermail/cas/attachments/20090117/e1d39015/attachment.bin 


More information about the cas mailing list