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.apache.log4j.MDC;
020import org.kuali.rice.core.api.CoreApiServiceLocator;
021import org.kuali.rice.core.api.config.property.ConfigurationService;
022import org.kuali.rice.core.api.exception.RiceRuntimeException;
023import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
024import org.kuali.rice.coreservice.framework.CoreFrameworkServiceLocator;
025import org.kuali.rice.coreservice.framework.parameter.ParameterService;
026import org.kuali.rice.kew.api.KewApiConstants;
027import org.kuali.rice.kim.api.KimConstants;
028import org.kuali.rice.kim.api.identity.AuthenticationService;
029import org.kuali.rice.kim.api.identity.IdentityService;
030import org.kuali.rice.kim.api.identity.principal.Principal;
031import org.kuali.rice.kim.api.permission.PermissionService;
032import org.kuali.rice.kim.api.services.KimApiServiceLocator;
033import org.kuali.rice.krad.UserSession;
034import org.kuali.rice.krad.exception.AuthenticationException;
035import org.kuali.rice.krad.util.KRADConstants;
036import org.kuali.rice.krad.util.KRADUtils;
037
038import javax.servlet.Filter;
039import javax.servlet.FilterChain;
040import javax.servlet.FilterConfig;
041import javax.servlet.ServletException;
042import javax.servlet.ServletRequest;
043import javax.servlet.ServletResponse;
044import javax.servlet.http.Cookie;
045import javax.servlet.http.HttpServletRequest;
046import javax.servlet.http.HttpServletResponse;
047import javax.xml.namespace.QName;
048import java.io.IOException;
049import java.util.Collections;
050import java.util.UUID;
051
052/**
053 * A filter for processing user logins and creating a {@link org.kuali.rice.krad.UserSession}
054 *
055 * @author Kuali Rice Team (rice.collab@kuali.org)
056 * @see org.kuali.rice.krad.UserSession
057 */
058public class UserLoginFilter implements Filter {
059
060    private static final String MDC_USER = "user";
061
062    private IdentityService identityService;
063    private PermissionService permissionService;
064    private ConfigurationService kualiConfigurationService;
065    private ParameterService parameterService;
066
067    private FilterConfig filterConfig;
068
069    @Override
070    public void init(FilterConfig config) throws ServletException {
071        this.filterConfig = config;
072    }
073
074    @Override
075    public void doFilter(ServletRequest request, ServletResponse response,
076            FilterChain chain) throws IOException, ServletException {
077        this.doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
078    }
079
080    private void doFilter(HttpServletRequest request, HttpServletResponse response,
081            FilterChain chain) throws IOException, ServletException {
082        try {
083            establishUserSession(request);
084            establishSessionCookie(request, response);
085            establishBackdoorUser(request);
086
087            addToMDC(request);
088
089            chain.doFilter(request, response);
090        } finally {
091            removeFromMDC();
092        }
093    }
094
095    @Override
096    public void destroy() {
097        filterConfig = null;
098    }
099
100    /**
101     * Checks if a user can be authenticated and if so establishes a UserSession for that user.
102     */
103    private void establishUserSession(HttpServletRequest request) {
104        if (!isUserSessionEstablished(request)) {
105            String principalName = ((AuthenticationService) GlobalResourceLoader.getResourceLoader().getService(
106                    new QName("kimAuthenticationService"))).getPrincipalName(request);
107            if (StringUtils.isBlank(principalName)) {
108                throw new AuthenticationException("Blank User from AuthenticationService - This should never happen.");
109            }
110
111            Principal principal = getIdentityService().getPrincipalByPrincipalName(principalName);
112            if (principal == null) {
113                throw new AuthenticationException("Unknown User: " + principalName);
114            }
115
116            if (!isAuthorizedToLogin(principal.getPrincipalId())) {
117                throw new AuthenticationException(
118                        "You cannot log in, because you are not an active Kuali user.\nPlease ask someone to activate your account if you need to use Kuali Systems.\nThe user id provided was: "
119                                + principalName + ".\n");
120            }
121
122            final UserSession userSession = new UserSession(principalName);
123            if (userSession.getPerson() == null) {
124                throw new AuthenticationException("Invalid User: " + principalName);
125            }
126
127            request.getSession().setAttribute(KRADConstants.USER_SESSION_KEY, userSession);
128        }
129    }
130
131    /**
132     * checks if the passed in principalId is authorized to log in.
133     */
134    private boolean isAuthorizedToLogin(String principalId) {
135        return getPermissionService().isAuthorized(principalId, KimConstants.KIM_TYPE_DEFAULT_NAMESPACE,
136                KimConstants.PermissionNames.LOG_IN, Collections.singletonMap("principalId", principalId));
137    }
138
139    /**
140     * Creates a session id cookie if one does not exists.  Write the cookie out to the response with that session id.
141     * Also, sets the cookie on the established user session.
142     */
143    private void establishSessionCookie(HttpServletRequest request, HttpServletResponse response) {
144        String kualiSessionId = this.getKualiSessionId(request.getCookies());
145        if (kualiSessionId == null) {
146            kualiSessionId = UUID.randomUUID().toString();
147            response.addCookie(new Cookie(KRADConstants.KUALI_SESSION_ID, kualiSessionId));
148        }
149        KRADUtils.getUserSessionFromRequest(request).setKualiSessionId(kualiSessionId);
150    }
151
152    /**
153     * gets the kuali session id from an array of cookies.  If a session id does not exist returns null.
154     */
155    private String getKualiSessionId(final Cookie[] cookies) {
156        if (cookies != null) {
157            for (Cookie cookie : cookies) {
158                if (KRADConstants.KUALI_SESSION_ID.equals(cookie.getName())) {
159                    return cookie.getValue();
160                }
161            }
162        }
163        return null;
164    }
165
166    /**
167     * establishes the backdoor user on the established user id if backdoor capabilities are valid.
168     */
169    private void establishBackdoorUser(HttpServletRequest request) {
170        final String backdoor = request.getParameter(KRADConstants.BACKDOOR_PARAMETER);
171        if (StringUtils.isNotBlank(backdoor)) {
172            if (!getKualiConfigurationService().getPropertyValueAsString(KRADConstants.PROD_ENVIRONMENT_CODE_KEY)
173                    .equalsIgnoreCase(getKualiConfigurationService().getPropertyValueAsString(
174                            KRADConstants.ENVIRONMENT_KEY))) {
175                if (getParameterService().getParameterValueAsBoolean(KRADConstants.KUALI_RICE_WORKFLOW_NAMESPACE,
176                        KRADConstants.DetailTypes.BACKDOOR_DETAIL_TYPE, KewApiConstants.SHOW_BACK_DOOR_LOGIN_IND)) {
177                    try {
178                        KRADUtils.getUserSessionFromRequest(request).setBackdoorUser(backdoor);
179                    } catch (RiceRuntimeException re) {
180                        //Ignore so BackdoorAction can redirect to invalid_backdoor_portal
181                    }
182                }
183            }
184        }
185    }
186
187    private void addToMDC(HttpServletRequest request) {
188        MDC.put(MDC_USER, KRADUtils.getUserSessionFromRequest(request).getPrincipalName());
189    }
190
191    private void removeFromMDC() {
192        MDC.remove(MDC_USER);
193    }
194
195    /**
196     * Checks if the user who made the request has a UserSession established
197     *
198     * @param request the HTTPServletRequest object passed in
199     * @return true if the user session has been established, false otherwise
200     */
201    private boolean isUserSessionEstablished(HttpServletRequest request) {
202        return (request.getSession().getAttribute(KRADConstants.USER_SESSION_KEY) != null);
203    }
204
205    private IdentityService getIdentityService() {
206        if (this.identityService == null) {
207            this.identityService = KimApiServiceLocator.getIdentityService();
208        }
209
210        return this.identityService;
211    }
212
213    private PermissionService getPermissionService() {
214        if (this.permissionService == null) {
215            this.permissionService = KimApiServiceLocator.getPermissionService();
216        }
217
218        return this.permissionService;
219    }
220
221    private ConfigurationService getKualiConfigurationService() {
222        if (this.kualiConfigurationService == null) {
223            this.kualiConfigurationService = CoreApiServiceLocator.getKualiConfigurationService();
224        }
225
226        return this.kualiConfigurationService;
227    }
228
229    private ParameterService getParameterService() {
230        if (this.parameterService == null) {
231            this.parameterService = CoreFrameworkServiceLocator.getParameterService();
232        }
233
234        return this.parameterService;
235    }
236}