001/*
002 *  Copyright 2016 Anyware Services
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 */
016package org.ametys.plugins.contentio.synchronize.impl;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.configuration.Configuration;
025import org.apache.avalon.framework.configuration.ConfigurationException;
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.commons.lang.StringUtils;
029import org.slf4j.Logger;
030
031import org.ametys.plugins.contentio.synchronize.SynchronizableContentsCollection;
032import org.ametys.plugins.contentio.synchronize.impl.LDAPCollectionHelper.LDAPCollectionHelperSearchResult;
033import org.ametys.runtime.i18n.I18nizableText;
034
035/**
036 * Implementation of {@link SynchronizableContentsCollection} to be synchronized with a LDAP data source
037 */
038public class LDAPSynchronizableContentsCollection extends AbstractDataSourceSynchronizableContentsCollection implements Component
039{
040    private static final String __PARAM_LDAP_SCOPE = "scope";
041    private static final String __PARAM_LDAP_RELATIVE_DN = "peopleDN";
042    private static final String __PARAM_LDAP_FILTER = "baseFilter";
043    
044    private static final int __DEFAULT_PAGE_SIZE = 500;
045    
046    /** The page size */
047    protected int _pageSize;
048
049    /** The helper for LDAP connection */
050    protected LDAPCollectionHelper _ldapHelper;
051    
052    @Override
053    public void service(ServiceManager serviceManager) throws ServiceException
054    {
055        super.service(serviceManager);
056        _ldapHelper = (LDAPCollectionHelper) serviceManager.lookup(LDAPCollectionHelper.ROLE);
057    }
058        
059    @Override
060    protected void configureDataSource(Configuration configuration) throws ConfigurationException
061    {
062        super.configureDataSource(configuration);
063        _pageSize = configuration.getChild("pageSize").getValueAsInteger(__DEFAULT_PAGE_SIZE);
064    }
065    
066    @Override
067    protected Map<String, Map<String, Object>> internalSearch(Map<String, Object> searchParameters, int offset, int limit, List<Object> sort, Logger logger)
068    {
069        // Sort is ignored in LDAP request
070        
071        Map<String, List<String>> mapping = getMapping();
072        Map<String, Map<String, Object>> searchResults = new HashMap<>();
073        
074        String idField = getIdField();
075        List<String> remoteKeys = mapping.get(idField);
076        if (remoteKeys != null && remoteKeys.size() > 0)
077        {
078            // Filter from search parameters
079            String filter = getFilter();
080            List<String> filters = new ArrayList<>();
081            if (StringUtils.isNotEmpty(filter))
082            {
083                filters.add(filter);
084            }
085            if (searchParameters != null)
086            {
087                for (String parameterName : searchParameters.keySet())
088                {
089                    filters.add(parameterName + "=" + searchParameters.get(parameterName));
090                }
091            }
092            String filtersReduced = filters.stream().filter(StringUtils::isNotEmpty).map(s -> "(" + s + ")").reduce("", (s1, s2) -> s1 + s2);
093            if (!filtersReduced.isEmpty())
094            {
095                filtersReduced = "(&" + filtersReduced + ")";
096            }
097
098            try
099            {
100                LDAPCollectionHelperSearchResult results = _ldapHelper.search(getId(), _pageSize, getRelativeDN(), filtersReduced, getSearchScope(), offset, limit, mapping, remoteKeys.get(0), logger, getDataSourceId());
101    
102                searchResults = results.searchResults();
103                for (Map<String, Object> result : searchResults.values())
104                {
105                    Object sccValues = result.get(remoteKeys.get(0));
106                    result.put(SCC_UNIQUE_ID, sccValues instanceof List<?> ? ((List<?>) sccValues).get(0) : sccValues);
107                }
108                
109                _nbError = results.nbErrors();
110                _hasGlobalError = results.hasGlobalError();
111            }
112            catch (Exception e)
113            {
114                throw new RuntimeException("An error occured when importing from LDAP UserDirectory", e);
115            }
116        }
117        else
118        {
119            _nbError = 1;
120            logger.error("Missing LDAP attribute in mapping for field '{}' holding the unique identifier", idField);
121        }
122        
123        return searchResults;
124    }
125    
126    /**
127     * Get the scope for LDAP search
128     * @return The scope
129     */
130    protected String getSearchScope()
131    {
132        return (String) getParameterValues().get(__PARAM_LDAP_SCOPE);
133    }
134    
135    /**
136     * Get the LDAP filter
137     * @return the filter
138     */
139    protected String getFilter()
140    {
141        return (String) getParameterValues().get(__PARAM_LDAP_FILTER);
142    }
143    
144    /**
145     * Get the LDAP relative DN
146     * @return the relative DN 
147     */
148    protected String getRelativeDN()
149    {
150        return (String) getParameterValues().get(__PARAM_LDAP_RELATIVE_DN);
151    }
152    
153    @Override
154    protected void configureSearchModel()
155    {
156        for (String columnOrCriteria : _columnsAndCriteria)
157        {
158            _searchModelConfiguration.addCriterion(columnOrCriteria);
159            _searchModelConfiguration.addColumn(columnOrCriteria, new I18nizableText(columnOrCriteria), false);
160        }
161    }
162    
163    @Override
164    protected Map<String, Map<String, List<Object>>> getRemoteValues(Map<String, Object> searchParameters, Logger logger)
165    {
166        Map<String, Map<String, List<Object>>> organizedResults = super.getRemoteValues(searchParameters, logger);
167        _ldapHelper.transformTypedAttributes(organizedResults, getContentType(), getMapping().keySet());
168        return organizedResults;
169    }
170}