001/** 002 * Copyright 2005-2018 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.rice.krad.web.filter; 017 018import org.apache.commons.lang.StringUtils; 019import org.kuali.rice.core.api.config.property.ConfigContext; 020import org.kuali.rice.kim.api.identity.IdentityService; 021import org.kuali.rice.kim.api.identity.principal.Principal; 022import org.kuali.rice.kim.api.services.KimApiServiceLocator; 023import org.kuali.rice.krad.UserSession; 024import org.kuali.rice.krad.exception.AuthenticationException; 025import org.kuali.rice.krad.util.KRADConstants; 026import org.kuali.rice.krad.util.KRADUtils; 027 028import javax.servlet.Filter; 029import javax.servlet.FilterChain; 030import javax.servlet.FilterConfig; 031import javax.servlet.ServletException; 032import javax.servlet.ServletRequest; 033import javax.servlet.ServletResponse; 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletRequestWrapper; 036import javax.servlet.http.HttpServletResponse; 037import java.io.IOException; 038import java.net.URLEncoder; 039 040/** 041 * A login filter which forwards to a login page that allows for the desired 042 * authentication ID to be entered (with testing password if option enabled) 043 * 044 * @author Kuali Rice Team (rice.collab@kuali.org) 045 */ 046public class DummyLoginFilter implements Filter { 047 private String loginPath; 048 private boolean showPassword = false; 049 050 @Override 051 public void init(FilterConfig config) throws ServletException { 052 loginPath = ConfigContext.getCurrentContextConfig().getProperty("loginPath"); 053 showPassword = Boolean.valueOf(ConfigContext.getCurrentContextConfig().getProperty("showPassword")).booleanValue(); 054 055 if (loginPath == null) { 056 loginPath = "/kr-login/login?viewId=DummyLoginView"; 057 } 058 } 059 060 @Override 061 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 062 this.doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain); 063 } 064 065 private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { 066 final UserSession session = KRADUtils.getUserSessionFromRequest(request); 067 068 if (session == null) { 069 loginRequired(request, response, chain); 070 071 return; 072 073 } else { 074 // Perform request as signed in user 075 request = new HttpServletRequestWrapper(request) { 076 @Override 077 public String getRemoteUser() { 078 return session.getPrincipalName(); 079 } 080 }; 081 } 082 chain.doFilter(request, response); 083 } 084 085 private void loginRequired(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { 086 if (StringUtils.isNotBlank(request.getParameter("__login_user"))) { 087 performLoginAttempt(request, response); 088 } else { 089 // ignore ajax calls from login screen 090 if (StringUtils.equals(request.getPathInfo(),"/listener")) { 091 return; 092 } 093 094 // allow redirect and form submit from login screen 095 if (StringUtils.equals(request.getPathInfo(),"/login")) { 096 chain.doFilter(request, response); 097 } else { 098 // no session has been established and this is not a login form submission, so redirect to login page 099 response.sendRedirect(getLoginRedirectUrl(request)); 100 } 101 } 102 } 103 104 private void performLoginAttempt(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { 105 IdentityService auth = KimApiServiceLocator.getIdentityService(); 106 final String user = request.getParameter("__login_user"); 107 String password = request.getParameter("__login_pw"); 108 109 // if passwords are used, they cannot be blank 110 if (showPassword && StringUtils.isBlank(password)) { 111 handleInvalidLogin(request, response); 112 return; 113 } 114 115 // Very simple password checking. Nothing hashed or encrypted. This is strictly for demonstration purposes only. 116 // password must have non null value on krim_prncpl_t record 117 Principal principal = showPassword ? auth.getPrincipalByPrincipalNameAndPassword(user, password) : auth.getPrincipalByPrincipalName(user); 118 if (principal == null) { 119 handleInvalidLogin(request, response); 120 return; 121 } 122 123 UserSession userSession = new UserSession(user); 124 125 // Test if session was successfully build for this user 126 if ( userSession.getPerson() == null ) { 127 throw new AuthenticationException("Invalid User: " + user ); 128 } 129 130 request.getSession().setAttribute(KRADConstants.USER_SESSION_KEY, userSession); 131 132 // wrap the request with the signed in user 133 // UserLoginFilter and WebAuthenticationService will build the session 134 request = new HttpServletRequestWrapper(request) { 135 @Override 136 public String getRemoteUser() { 137 return user; 138 } 139 }; 140 141 StringBuilder redirectUrl = new StringBuilder(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.APPLICATION_URL_KEY)); 142 redirectUrl.append(findTargetUrl(request)); 143 response.sendRedirect(redirectUrl.toString()); 144 } 145 146 /** 147 * Handles and invalid login attempt. 148 * 149 * Sets error message and redirects to login screen 150 * 151 * @param request the incoming request 152 * @param response the outgoing response 153 * @throws javax.servlet.ServletException if unable to handle the invalid login 154 * @throws java.io.IOException if unable to handle the invalid login 155 */ 156 private void handleInvalidLogin(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 157 StringBuilder redirectUrl = new StringBuilder(getLoginRedirectUrl(request)); 158 redirectUrl.append("&login_message=Invalid Login"); 159 response.sendRedirect(redirectUrl.toString()); 160 } 161 162 @Override 163 public void destroy() { 164 loginPath = null; 165 } 166 167 /** 168 * Construct Url to login screen with original target Url in returnLocation property 169 * 170 * @param request 171 * @return Url string 172 * @throws IOException 173 */ 174 private String getLoginRedirectUrl(HttpServletRequest request) throws IOException { 175 String targetUrl = findTargetUrl(request); 176 177 StringBuilder redirectUrl = new StringBuilder(ConfigContext.getCurrentContextConfig().getProperty(KRADConstants.APPLICATION_URL_KEY)); 178 redirectUrl.append(loginPath); 179 redirectUrl.append("&returnLocation="); 180 redirectUrl.append(URLEncoder.encode(targetUrl,"UTF-8")); 181 182 return redirectUrl.toString(); 183 } 184 185 /** 186 * Construct a url from a HttpServletRequest with login properties removed 187 * 188 * @param request 189 * @return Url string 190 */ 191 private String findTargetUrl(HttpServletRequest request) { 192 StringBuilder targetUrl = new StringBuilder(); 193 targetUrl.append(request.getServletPath()); 194 195 if (StringUtils.isNotBlank(request.getPathInfo())) { 196 targetUrl.append(request.getPathInfo()); 197 } 198 199 // clean login params from query string 200 if (StringUtils.isNotBlank(request.getQueryString())) { 201 targetUrl.append("?"); 202 203 for (String keyValuePair : request.getQueryString().split("&")) { 204 if (isValidProperty(keyValuePair)) targetUrl.append("&").append(keyValuePair); 205 } 206 207 } 208 209 // clean up delimiters and return url string 210 return targetUrl.toString().replace("&&","&").replace("?&","?"); 211 } 212 213 /** 214 * Test if property is needed (ie: Not a login property) 215 * 216 * @param keyValuePair 217 * @return Boolean 218 */ 219 private Boolean isValidProperty(String keyValuePair) { 220 int eq = keyValuePair.indexOf("="); 221 222 if (eq < 0) { 223 // key with no value 224 return Boolean.FALSE; 225 } 226 227 String key = keyValuePair.substring(0, eq); 228 if (!key.equals("__login_pw") 229 && !key.equals("__login_user") 230 && !key.equals("login_message")) { 231 return Boolean.TRUE; 232 } 233 234 return Boolean.FALSE; 235 } 236 237}