/* * © Copyright IBM Corp. 2012 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package com.ibm.sbt.services.endpoints; import java.io.IOException; import java.net.URL; import java.net.URLEncoder; import java.text.MessageFormat; import java.util.logging.Level; import java.util.logging.Logger; import javax.servlet.http.HttpServletRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.client.CookieStore; import org.apache.http.client.protocol.ClientContext; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.cookie.BasicClientCookie2; import org.apache.http.protocol.HttpContext; import com.ibm.commons.runtime.Context; import com.ibm.commons.runtime.util.UrlUtil; import com.ibm.commons.util.PathUtil; import com.ibm.commons.util.StringUtil; import com.ibm.sbt.security.authentication.AuthenticationException; import com.ibm.sbt.service.core.handlers.AuthCredsHandler; import com.ibm.sbt.service.core.servlet.ServiceServlet; import com.ibm.sbt.services.client.ClientServicesException; import com.ibm.sbt.services.endpoints.js.JSReference; /** * Bean that provides a authentication using an LTPA token. * * @author Philippe Riand * @author Niklas Heidloff * @author Paul Bastide <pbastide@us.ibm.com> */ public class SSOEndpoint extends AbstractEndpoint { private static final long serialVersionUID = 1L; private String domain = null; private String authenticationPage = null; static final String sourceClass = SSOEndpoint.class.getName(); static final Logger logger = Logger.getLogger(sourceClass); public SSOEndpoint() { } @Override public String getAuthType(){ return "sso"; } @Override public boolean isAuthenticated() throws ClientServicesException { return true; } @Override public void authenticate(boolean force) throws ClientServicesException { if(force || !isAuthenticated()) { String authPage = getAuthenticationPage(); Context context = Context.get(); if(StringUtil.isNotEmpty(authPage)) { try{ if(!UrlUtil.isAbsoluteUrl(authPage)){ authPage = UrlUtil.makeUrlAbsolute((HttpServletRequest)context.getHttpRequest(), authPage); } String redirectUrl = UrlUtil.getRequestUrl((HttpServletRequest)context.getHttpRequest());// change needed to handle portlethttprequest String endPointName = authPage.substring(authPage.indexOf("=")+1, authPage.length()); String baseUrl = UrlUtil.getBaseUrl(((HttpServletRequest)context.getHttpRequest())); String servletPath = ServiceServlet.getServletPath(); String basicProxyUrl = AuthCredsHandler.URL_PATH; // constructing proxy action url String postToProxy = PathUtil.concat(baseUrl, servletPath, '/'); postToProxy = PathUtil.concat(postToProxy,basicProxyUrl, '/'); postToProxy = PathUtil.concat(postToProxy,endPointName, '/'); postToProxy = PathUtil.concat(postToProxy,"JavaApp", '/'); // encode URL's postToProxy = URLEncoder.encode(postToProxy,"UTF-8"); redirectUrl = URLEncoder.encode(redirectUrl,"UTF-8"); // passing proxy action url as a parameter to the authentication page authPage = PathUtil.concat(authPage,"proxyPath",'&'); authPage = PathUtil.concat(authPage,postToProxy,'='); // passing redirectURL as a parameter to the authentication page authPage = PathUtil.concat(authPage,"redirectURL",'&'); authPage = PathUtil.concat(authPage,redirectUrl,'='); context.sendRedirect(authPage); } catch (IOException e) { throw new ClientServicesException(e,"LTPA token refresh failed because: "+e.getMessage()); } } else { throw new ClientServicesException(null,"LTPA token expired or invalid. Cannot refresh: authentication page is not set"); } } } @Override public JSReference getAuthenticator(String endpointName, String sbtUrl) { JSReference reference = new JSReference("sbt/authenticator/SSO"); reference.getProperties().put("url", sbtUrl); return reference; } public void redirect() { } @Override public void initialize(DefaultHttpClient httpClient) { HttpRequestInterceptor ltpaInterceptor = new LtpaInterceptor(getUrl(), getDomain()); httpClient.addRequestInterceptor(ltpaInterceptor, 0); } private static class LtpaInterceptor implements HttpRequestInterceptor { String _domain; boolean siteminder = false; public LtpaInterceptor(String url, String domain) { /** * Customer Reported Issue with SiteMinder * SSO Endpoint is extended with SMSESSION * * REF: Must add to managed-beans.xml * <managed-property><property-name>siteminder</property-name><value>true</value></managed-property> */ Context ctx = Context.get(); String siteminderProp = ctx.getProperty("siteminder"); if((siteminderProp != null) && (siteminderProp.compareTo("true")==0)){ siteminder = true; } if (domain == null) { try { URL u = new URL(url); _domain = u.getHost(); //handles ipv6 hosts if (_domain.startsWith("[") && _domain.endsWith("]")) { _domain = _domain.substring(0, _domain.length()-1); } } catch (Exception e ) { //fall back for incomplete urls _domain = url.substring(url.indexOf("//")+2); if(_domain.indexOf(":")!=-1) { _domain = _domain.substring(0, _domain.indexOf(":")); } } // Fix in calculating the domain correctly // If the host is qs.renovations.com, domain should be renovations.com and not qs.renovations.com if(StringUtil.isNotEmpty(_domain) && countMatch(_domain,'.')>1){ _domain = _domain.substring(_domain.indexOf('.')+1,_domain.length()); } } else { _domain = domain; } if (logger.isLoggable(Level.INFO)) { String msg = MessageFormat.format("SSO endpoint domain for {0} is {1}", url, _domain); logger.log(Level.INFO, msg); } } private static int countMatch(String source, char match) { int count=0; if(StringUtil.isNotEmpty(source)) { for(int i=0; i<source.length(); i++) { if(StringUtil.equals(source.charAt(i), match)) { count++; } } } return count; } public String getRawCookieValue(javax.servlet.http.Cookie cookie, HttpServletRequest request) { try { String header = request.getHeader("cookie"); String[] headerParts = StringUtil.splitString(header, "; ", false); String name = cookie.getName(); for (int i=0; i<headerParts.length; i++) { String cookieHeader = headerParts[i]; if (cookieHeader.startsWith(name)) { String value = cookieHeader.substring(name.length()+1); return value; } } } catch (Exception e) { if (logger.isLoggable(Level.INFO)) { logger.log(Level.INFO, "Unable to parse cookie header", e); } } return cookie.getValue(); } @Override @SuppressWarnings("unchecked") public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { CookieStore cookieStore; cookieStore = new BasicCookieStore(); Context ctx = Context.get(); boolean ltpaTokenFound = false; java.util.Map<java.lang.String, java.lang.Object> cookieMap = ctx.getRequestCookieMap(); if(cookieMap.containsKey("LtpaToken")) { javax.servlet.http.Cookie cookie = (javax.servlet.http.Cookie) cookieMap.get("LtpaToken"); BasicClientCookie2 cookie1 = new BasicClientCookie2(cookie.getName(), getRawCookieValue(cookie, ctx.getHttpRequest())); if(cookie.getDomain()!=null) { cookie1.setDomain(cookie.getDomain()); } else { cookie1.setDomain(_domain); } if(cookie.getPath()!=null) { cookie1.setPath(cookie.getPath()); } else { cookie1.setPath("/"); } cookieStore.addCookie(cookie1); ltpaTokenFound = true; } if(cookieMap.containsKey("LtpaToken2")) { javax.servlet.http.Cookie cookie = (javax.servlet.http.Cookie) cookieMap.get("LtpaToken2"); BasicClientCookie2 cookie2 = new BasicClientCookie2(cookie.getName(), getRawCookieValue(cookie, ctx.getHttpRequest())); if(cookie.getDomain()!=null) { cookie2.setDomain(cookie.getDomain()); } else { cookie2.setDomain(_domain); } if(cookie.getPath()!=null) { cookie2.setPath(cookie.getPath()); } else { cookie2.setPath("/"); } cookieStore.addCookie(cookie2); ltpaTokenFound = true; } /** * Customer Reported Issue with SiteMinder * SSO Endpoint is extended with SMSESSION * * REF: shortcircuits using siteminder context check */ if(siteminder && cookieMap.containsKey("SMSESSION")) { javax.servlet.http.Cookie cookie = (javax.servlet.http.Cookie) cookieMap.get("SMSESSION"); BasicClientCookie2 cookie2 = new BasicClientCookie2(cookie.getName(), getRawCookieValue(cookie, ctx.getHttpRequest())); if(cookie.getDomain()!=null) { cookie2.setDomain(cookie.getDomain()); } else { cookie2.setDomain(_domain); } if(cookie.getPath()!=null) { cookie2.setPath(cookie.getPath()); } else { cookie2.setPath("/"); } cookieStore.addCookie(cookie2); ltpaTokenFound = true; } if (!ltpaTokenFound && logger.isLoggable(Level.INFO)) { String uri = ""; try { uri = request.getRequestLine().getUri(); } catch (Exception e) {} logger.log(Level.INFO, "Unable to find LTPA token for request "+uri); } context.setAttribute(ClientContext.COOKIE_STORE, cookieStore); } } @Override public void logout() throws AuthenticationException { // TODO Auto-generated method stub } public String getAuthenticationPage() { return authenticationPage; } public void setAuthenticationPage(String authenticationPage) { this.authenticationPage = authenticationPage; } public String getDomain() { return domain; } public void setDomain(String domain) { this.domain = domain; } }