001/*
002 * (C) Copyright 2006-2007 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.ui.web.auth.proxy;
023
024import java.io.Serializable;
025import java.util.HashMap;
026import java.util.List;
027import java.util.Map;
028import java.util.regex.Matcher;
029import java.util.regex.Pattern;
030
031import javax.servlet.http.HttpServletRequest;
032import javax.servlet.http.HttpServletResponse;
033import javax.servlet.http.HttpSession;
034
035import org.apache.commons.logging.Log;
036import org.apache.commons.logging.LogFactory;
037import org.nuxeo.ecm.core.api.DocumentModelList;
038import org.nuxeo.ecm.directory.DirectoryException;
039import org.nuxeo.ecm.directory.Session;
040import org.nuxeo.ecm.directory.api.DirectoryService;
041import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
042import org.nuxeo.ecm.platform.ui.web.auth.LoginScreenHelper;
043import org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants;
044import org.nuxeo.ecm.platform.ui.web.auth.NuxeoAuthenticationFilter;
045import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
046import org.nuxeo.ecm.platform.usermanager.UserManager;
047import org.nuxeo.runtime.api.Framework;
048
049public class ProxyAuthenticator implements NuxeoAuthenticationPlugin {
050
051    private static final Log log = LogFactory.getLog(ProxyAuthenticator.class);
052
053    private static final String HEADER_NAME_KEY = "ssoHeaderName";
054
055    private static final String HEADER_NOREDIRECT_KEY = "ssoNeverRedirect";
056
057    public static final String USERNAME_REMOVE_EXPRESSION = "usernameUnwantedPartExpression";
058
059    protected String userIdHeaderName = "remote_user";
060
061    protected String regexp = null;
062
063    protected boolean noRedirect;
064
065    public static final String HTTP_CREDENTIAL_DIRECTORY_FIELD_PROPERTY_NAME = "org.nuxeo.ecm.platform.login.mod_sso.credentialDirectoryField";
066
067    private Pattern usernamePartRemovalPattern;
068
069    @Override
070    public List<String> getUnAuthenticatedURLPrefix() {
071        return null;
072    }
073
074    @Override
075    public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) {
076        return false;
077    }
078
079    @Override
080    public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest,
081            HttpServletResponse httpResponse) {
082        String userName = httpRequest.getHeader(userIdHeaderName);
083        if (userName == null) {
084            return null;
085        }
086        if (regexp != null && usernamePartRemovalPattern != null) {
087            String tmpUsername = userName;
088            Matcher matcher = usernamePartRemovalPattern.matcher(userName);
089            // Remove all instance of regexp from username string
090            userName = matcher.replaceAll("");
091            log.debug(String.format("userName changed from '%s' to '%s'", tmpUsername, userName));
092        }
093
094        String credentialFieldName = Framework.getRuntime().getProperty(HTTP_CREDENTIAL_DIRECTORY_FIELD_PROPERTY_NAME);
095        if (credentialFieldName != null) {
096            // use custom directory field to find the user with the ID given in
097            // the HTTP header
098            String directoryName = Framework.getService(UserManager.class).getUserDirectoryName();
099            try (Session userDir = Framework.getService(DirectoryService.class).open(directoryName)) {
100                Map<String, Serializable> queryFilters = new HashMap<>();
101                queryFilters.put(credentialFieldName, userName);
102                DocumentModelList result = userDir.query(queryFilters);
103                if (result.isEmpty()) {
104                    log.error(String.format("could not find any user with %s='%s' in directory %s", credentialFieldName,
105                            userName, directoryName));
106                    return null;
107                }
108                if (result.size() > 1) {
109                    log.error(String.format("found more than one entry for  %s='%s' in directory %s",
110                            credentialFieldName, userName, directoryName));
111                    return null;
112                }
113                // use the ID of the found user entry as new identification for
114                // the principal
115                userName = result.get(0).getId();
116            } catch (DirectoryException e) {
117                log.error(String.format("could not retrieve user entry with %s='%s':  %s", credentialFieldName,
118                        userName, e.getMessage()), e);
119                return null;
120            }
121        }
122
123        if (!noRedirect) {
124            handleRedirectToValidStartPage(httpRequest, httpResponse);
125        }
126        return new UserIdentificationInfo(userName);
127    }
128
129    /**
130     * Handle redirection so that context is rebuilt correctly see NXP-2060 + NXP-2064
131     */
132    protected void handleRedirectToValidStartPage(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
133        boolean isStartPageValid = false;
134        if (httpRequest.getMethod().equals("GET") || httpRequest.getMethod().equals("POST")) {
135            // try to keep valid start page
136            NuxeoAuthenticationFilter filter = new NuxeoAuthenticationFilter();
137            isStartPageValid = filter.saveRequestedURLBeforeRedirect(httpRequest, httpResponse);
138        }
139        HttpSession session;
140        if (httpResponse.isCommitted()) {
141            session = httpRequest.getSession(false);
142        } else {
143            session = httpRequest.getSession(true);
144        }
145        if (session != null && !isStartPageValid) {
146            session.setAttribute(NXAuthConstants.START_PAGE_SAVE_KEY,
147                    LoginScreenHelper.getStartupPagePath() + "?loginRedirection=true");
148        }
149    }
150
151    @Override
152    public void initPlugin(Map<String, String> parameters) {
153        if (parameters.containsKey(HEADER_NAME_KEY)) {
154            userIdHeaderName = parameters.get(HEADER_NAME_KEY);
155        }
156        if (parameters.containsKey(HEADER_NOREDIRECT_KEY)) {
157            noRedirect = Boolean.parseBoolean(parameters.get(HEADER_NOREDIRECT_KEY));
158        }
159        if (parameters.containsKey(USERNAME_REMOVE_EXPRESSION)) {
160            regexp = parameters.get(USERNAME_REMOVE_EXPRESSION);
161            log.debug(String.format("Will remove all instances of '%s' from userName string.", regexp));
162            usernamePartRemovalPattern = Pattern.compile(regexp);
163        }
164    }
165
166    @Override
167    public Boolean needLoginPrompt(HttpServletRequest httpRequest) {
168        return false;
169    }
170
171}