/* * © Copyright IBM Corp. 2013 * * 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.URLEncoder; import java.util.Iterator; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.cookie.Cookie; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.CoreProtocolPNames; 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.service.debug.ProxyDebugUtil; import com.ibm.sbt.services.client.ClientServicesException; import com.ibm.sbt.services.endpoints.js.JSReference; import com.ibm.sbt.services.util.SSLUtil; /** * Bean that provides a Form based authentication. * @author Manish Kataria */ public abstract class FormEndpoint extends AbstractEndpoint { private String authenticationPage; private String user; private String password; // provides form page url where login should be performed public String loginFormUrl; // For already authenticated users use this cookie cache, these cookies are persisted after initial form based authentication private String cookieCache; public String getCookieCache() { return cookieCache; } public String getLoginFormUrl() { return loginFormUrl; } public void setLoginFormUrl(String loginFormUrl) { this.loginFormUrl = loginFormUrl; } /* * Converts list of cookies to string which can be added to request header */ public void setCookieCache(List<Cookie> cookies) { StringBuilder cookieBuilder = new StringBuilder(); if(cookies !=null){ for (Iterator<Cookie> iterator = cookies.iterator(); iterator.hasNext();) { Cookie cookie = iterator.next(); cookieBuilder.append(cookie.getName() + "=" + cookie.getValue()); cookieBuilder.append(";"); } } cookieCache = cookieBuilder.toString(); } /* * Helper method for passing cookies directly as String */ public void setCookieCache(String cookies) { cookieCache = cookies; } @Override public boolean isAuthenticated() throws ClientServicesException { if (StringUtil.isEmpty(getCookieCache())) { return false; } else { return true; } } @Override public void initialize(DefaultHttpClient httpClient)throws ClientServicesException { if (StringUtil.isNotEmpty(getCookieCache())) { HttpRequestInterceptor basicInterceptor = new CookieInterceptor(getCookieCache()); httpClient.addRequestInterceptor(basicInterceptor, 0); } } private static class CookieInterceptor implements HttpRequestInterceptor { private String cookieCache; public CookieInterceptor(String cookieCache) { this.cookieCache = cookieCache; } @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { request.setHeader("Cookie", cookieCache); } } public boolean login(String user, String password)throws AuthenticationException { boolean validAuthentication = false; String requestUrl = getUrl(); setUser(user); setPassword(password); try { if(!(getLoginFormUrl().startsWith("/"))){ requestUrl = requestUrl.concat("/"); } requestUrl = requestUrl.concat(getLoginFormUrl()); BasicCookieStore cookieStore = new BasicCookieStore(); DefaultHttpClient defaultHttpClient = new DefaultHttpClient(); if(isForceTrustSSLCertificate()){ defaultHttpClient = SSLUtil.wrapHttpClient(defaultHttpClient); // Configure httpclient to accept all SSL certificates } if (isForceDisableExpectedContinue()) { defaultHttpClient.getParams().setParameter( CoreProtocolPNames.USE_EXPECT_CONTINUE, false); } if (StringUtil.isNotEmpty(getHttpProxy())) { defaultHttpClient = ProxyDebugUtil.wrapHttpClient(defaultHttpClient, getHttpProxy()); // Configure httpclient to direct all traffic through proxy clients } defaultHttpClient.setCookieStore(cookieStore); HttpPost httpost = new HttpPost(requestUrl); List<NameValuePair> formParams = getLoginFormParameters(); // retrieve platform specific login parameters httpost.setEntity(new UrlEncodedFormEntity(formParams, "UTF-8")); //getting to interface to avoid //java.lang.NoSuchMethodError: org/apache/http/impl/client/DefaultHttpClient.execute(Lorg/apache/http/client/methods/HttpUriRequest;)Lorg/apache/http/client/methods/CloseableHttpResponse; //when run from different version of HttpClient (that's why it is deprecated) HttpClient httpClient = defaultHttpClient; HttpResponse resp = httpClient.execute(httpost); int code = resp.getStatusLine().getStatusCode(); if (code == HttpStatus.SC_OK) { validAuthentication = true; } List<Cookie> cookies = cookieStore.getCookies(); setCookieCache(cookies); } catch (IOException e) { throw new AuthenticationException(e,"FormEndpoint failed to authenticate"); } return validAuthentication; } @Override /* * This method is exactly same as authenticate method from Basic endpoint, we need a login page here as well. */ 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,"Authentication page not found. Could not redirect to login page"); } } else { throw new ClientServicesException(null,"Authentication page is empty in the basic authentication bean"); } } } @Override public void logout() throws AuthenticationException { // Clear out the cookie string setCookieCache(""); } /* * Should be overriden by specific endpoints like Connections form endpoint etc. * Implementation should provide specific parameters which need to be passed along with authentication request. */ public abstract List<NameValuePair> getLoginFormParameters(); /* * Should be overriden by specific endpoints like or Connectionsformendpoint etc. * Implementation should provide Url where the form needs to be submitted. */ public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAuthenticationPage() { return authenticationPage; } public void setAuthenticationPage(String authenticationPage) { this.authenticationPage = authenticationPage; } @Override public String getHttpProxy() { String proxyinfo = super.getHttpProxy(); if (StringUtil.isEmpty(proxyinfo)) { Context context = Context.getUnchecked(); if (context != null) { proxyinfo = Context.get().getProperty("sbt.httpProxy"); } } return proxyinfo; } @Override public JSReference getAuthenticator(String endpointName, String sbtUrl) { JSReference reference = new JSReference("sbt/authenticator/Basic"); reference.getProperties().put("url", sbtUrl); return reference; } @Override public String getAuthType() { return "form"; } }