/* * © 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.URLEncoder; import javax.servlet.http.HttpServletRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthState; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.protocol.ClientContext; import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; 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.security.authentication.password.consumer.UserPassword; import com.ibm.sbt.security.credential.store.CredentialStore; import com.ibm.sbt.security.credential.store.CredentialStoreException; import com.ibm.sbt.security.credential.store.CredentialStoreFactory; 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 basic authentication. * <p> * </p> * @author Philippe Riand */ public class BasicEndpoint extends AbstractEndpoint { // Key used by the bean when it needs to redirect public static final String REDIRECT_PAGE_KEY = "xsp.endpoint.redirectpage"; // Type used to store the credentials public static final String STORE_TYPE = "Basic"; private String user; private String password; private String authenticationPage; private boolean storeAlreadyTried; public BasicEndpoint() { } public BasicEndpoint(String user, String password, String authenticationPage) { this.user = user; this.password = password; this.authenticationPage = authenticationPage; } @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 getUserIdentity() throws ClientServicesException { try { String u = getUser(); if(StringUtil.isEmpty(u)) { readFromStore(); u = getUser(); } return u; } catch(AuthenticationException ex) { throw new ClientServicesException(ex); } } 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() { if(StringUtil.isEmpty(authenticationPage)){ // return default authentication page if it is not given in managed bean return "/sbt/loginForm.html"; } return authenticationPage; } public void setAuthenticationPage(String authenticationPage) { this.authenticationPage = authenticationPage; } @Override public String getAuthType() { return "basic"; } @Override public boolean isAuthenticated() throws ClientServicesException { try { String u = getUser(); if(StringUtil.isEmpty(u)) { return readFromStore(); } return true; } catch(AuthenticationException ex) { throw new ClientServicesException(ex); } } @Override public void authenticate(boolean force) throws ClientServicesException { if ((HttpServletRequest)Context.get().getHttpRequest() == null) { return; } if(force || !isAuthenticated()) { String authPage = getAuthenticationPage(); Context context = Context.get(); if(StringUtil.isNotEmpty(authPage)) { // to ignore if the authentication page value contains endpoint parameter, we are now making use of this.getName() to get the endpoint name // and no need to explicitly set in authentication page value if(authPage.contains("?endpoint=") || authPage.contains("?") ){ authPage = authPage.substring(0, authPage.indexOf(("?"))); } 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 = this.getName(); 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(null,"Authentication page not found. Could not redirect to login page"); } } else { throw new ClientServicesException(null,"Authentication page is empty in the basic authentication bean"); } } } public boolean readFromStore() throws AuthenticationException { try { if(!storeAlreadyTried) { synchronized (this) { Context context = Context.getUnchecked(); if (context != null) { UserPassword u = null; CredentialStore cs = CredentialStoreFactory.getCredentialStore(getCredentialStore()); if(cs!=null) { u = (UserPassword)cs.load(getUrl(),STORE_TYPE,context.getCurrentUserId()); } if(u!=null) { this.user = u.getUser(); this.password = u.getPassword(); return true; } storeAlreadyTried = true; } } } return false; } catch(CredentialStoreException ex) { throw new AuthenticationException(ex,"Error while reading basic credentials from the store"); } } public boolean writeToStore() throws AuthenticationException { try { Context context = Context.getUnchecked(); if (context != null) { CredentialStore cs = CredentialStoreFactory.getCredentialStore(getCredentialStore()); if(cs!=null) { UserPassword u = new UserPassword(user,password); cs.store(getUrl(), STORE_TYPE, context.getCurrentUserId(), u); return true; } } return false; } catch(CredentialStoreException ex) { throw new AuthenticationException(ex,"Error while writing basic credentials to the store"); } } public boolean clearFromStore() throws AuthenticationException { try { Context context = Context.getUnchecked(); if (context != null) { CredentialStore cs = CredentialStoreFactory.getCredentialStore(getCredentialStore()); if(cs!=null) { cs.remove(getUrl(), STORE_TYPE, context.getCurrentUserId()); return true; } } return false; } catch(CredentialStoreException ex) { throw new AuthenticationException(ex,"Error while deleting basic credentials from the store"); } } public boolean login(String user, String password) throws AuthenticationException { return login(user,password,false); } public boolean login(String user, String password, boolean writeToStore) throws AuthenticationException { setUser(user); setPassword(password); if(!isAuthenticationValid()) { setUser(null); setPassword(null); return false; } if(writeToStore) { writeToStore(); } else { clearFromStore(); } // How can we verify the user is properly authenticated? return true; } @Override public void logout() throws AuthenticationException { setUser(null); setPassword(null); clearFromStore(); } public void redirect()throws ClientServicesException{ Context context = Context.get(); String nextPage = (String)context.getSessionMap().get(REDIRECT_PAGE_KEY); if(StringUtil.isEmpty(nextPage)) nextPage=((HttpServletRequest)context.getHttpRequest()).getParameter("redirectURL"); if (StringUtil.isNotEmpty(nextPage)) { // TODO // context.getExternalContext().getSessionMap().remove(REDIRECT_PAGE_KEY); // XSPContext ctx = // XSPContext.getXSPContext(FacesContext.getCurrentInstance()); try { context.sendRedirect(nextPage); } catch (IOException e) { throw new ClientServicesException(e,"Error redirecting to the following URL"+nextPage); } } } @Override public void initialize(DefaultHttpClient httpClient) throws ClientServicesException { String usr = getUserIdentity(); if(StringUtil.isNotEmpty(usr)) { String pwd = getPassword(); UsernamePasswordCredentials creds = new UsernamePasswordCredentials(usr,pwd); HttpRequestInterceptor basicInterceptor = new BasicInterceptor(creds); httpClient.addRequestInterceptor(basicInterceptor, 0); } } private static class BasicInterceptor implements HttpRequestInterceptor { private UsernamePasswordCredentials credentials; public BasicInterceptor(UsernamePasswordCredentials credentials) { this.credentials = credentials; } @Override public void process(HttpRequest request, HttpContext context)throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); if (authState != null && authState.getAuthScheme() == null) { AuthScheme scheme = new BasicSchemeFactory().newInstance(new BasicHttpParams()); authState.setAuthScheme(scheme); authState.setCredentials(credentials); } } } }