001/*
002 * (C) Copyright 2010 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 * Contributors:
016 *     Nuxeo - initial API and implementation
017 */
018
019package org.nuxeo.ecm.platform.shibboleth.auth;
020
021import java.io.IOException;
022import java.util.HashMap;
023import java.util.List;
024import java.util.Map;
025
026import javax.servlet.http.HttpServletRequest;
027import javax.servlet.http.HttpServletResponse;
028
029import org.apache.commons.lang3.StringUtils;
030import org.apache.commons.logging.Log;
031import org.apache.commons.logging.LogFactory;
032import org.nuxeo.ecm.core.api.DocumentModel;
033import org.nuxeo.ecm.directory.DirectoryException;
034import org.nuxeo.ecm.directory.Session;
035import org.nuxeo.ecm.directory.api.DirectoryService;
036import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo;
037import org.nuxeo.ecm.platform.shibboleth.service.ShibbolethAuthenticationService;
038import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin;
039import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension;
040import org.nuxeo.ecm.platform.usermanager.UserManager;
041import org.nuxeo.runtime.api.Framework;
042import org.nuxeo.usermapper.extension.UserMapper;
043import org.nuxeo.usermapper.service.UserMapperService;
044
045public class ShibbolethAuthenticationPlugin implements NuxeoAuthenticationPlugin,
046        NuxeoAuthenticationPluginLogoutExtension {
047
048    private static final Log log = LogFactory.getLog(ShibbolethAuthenticationPlugin.class);
049
050    public static final String EXTERNAL_MAPPER_PARAM = "mapper";
051
052    public static final String DEFAULT_EXTERNAL_MAPPER = "shibboleth";
053
054    protected UserMapper externalMapper = null;
055
056    protected ShibbolethAuthenticationService getService() {
057        return Framework.getService(ShibbolethAuthenticationService.class);
058    }
059
060    @Override
061    public List<String> getUnAuthenticatedURLPrefix() {
062        return null;
063    }
064
065    @Override
066    public Boolean handleLoginPrompt(HttpServletRequest httpRequest, HttpServletResponse httpResponse, String baseURL) {
067        if (getService() == null) {
068            return false;
069        }
070        String loginURL = getService().getLoginURL(httpRequest);
071        if (loginURL == null) {
072            log.error("Unable to handle Shibboleth login, no loginURL registered");
073            return false;
074        }
075        try {
076            httpResponse.sendRedirect(loginURL);
077        } catch (IOException e) {
078            String errorMessage = String.format("Unable to handle Shibboleth login on %s", loginURL);
079            log.error(errorMessage, e);
080            return false;
081        }
082        return true;
083    }
084
085    @Override
086    public Boolean handleLogout(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
087        if (getService() == null) {
088            return false;
089        }
090        String logoutURL = getService().getLogoutURL(httpRequest);
091        if (logoutURL == null) {
092            return false;
093        }
094        try {
095            httpResponse.sendRedirect(logoutURL);
096        } catch (IOException e) {
097            log.error("Unable to handle Shibboleth logout", e);
098            return false;
099        }
100        return true;
101    }
102
103    @Override
104    public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest httpRequest,
105            HttpServletResponse httpResponse) {
106        if (getService() == null) {
107            return null;
108        }
109
110        String userId = getService().getUserID(httpRequest);
111        if (userId == null || StringUtils.EMPTY.equals(userId)) {
112            return null;
113        } else {
114            UserMapperService ums = Framework.getService(UserMapperService.class);
115            if (ums != null) {
116                if (ums.getAvailableMappings().contains(DEFAULT_EXTERNAL_MAPPER)) {
117                    externalMapper = ums.getMapper(DEFAULT_EXTERNAL_MAPPER);
118                }
119            }
120        }
121        UserManager userManager = Framework.getService(UserManager.class);
122        Map<String, Object> fieldMap = getService().getUserMetadata(userManager.getUserIdField(), httpRequest);
123
124        if (externalMapper != null) {
125            Map<String, Object> nativeObject = new HashMap<String, Object>();
126            nativeObject.putAll(fieldMap);
127            nativeObject.put("userId", userId);
128            externalMapper.getOrCreateAndUpdateNuxeoPrincipal(nativeObject);
129        } else {
130            try (Session userDir = Framework.getService(DirectoryService.class).open(userManager.getUserDirectoryName())) {
131                Framework.doPrivileged(() -> {
132                    DocumentModel entry = userDir.getEntry(userId);
133                    if (entry == null) {
134                        userDir.createEntry(fieldMap);
135                    } else {
136                        fieldMap.forEach((k, v) -> entry.setProperty(userManager.getUserSchemaName(), k, v));
137                        userDir.updateEntry(entry);
138                    }
139                });
140            } catch (DirectoryException e) {
141                log.error("Failed to get or create user entry", e);
142            }
143        }
144
145        return new UserIdentificationInfo(userId, userId);
146    }
147
148    @Override
149    public Boolean needLoginPrompt(HttpServletRequest httpRequest) {
150        return true;
151    }
152
153    @Override
154    public void initPlugin(Map<String, String> parameters) {
155    }
156
157}