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.web.site;
017
018import java.io.File;
019import java.io.FileInputStream;
020import java.io.FileNotFoundException;
021import java.io.IOException;
022import java.io.InputStream;
023import java.nio.file.Files;
024import java.nio.file.Paths;
025import java.util.ArrayList;
026import java.util.Collection;
027import java.util.HashMap;
028import java.util.HashSet;
029import java.util.List;
030import java.util.Map;
031import java.util.Set;
032import java.util.stream.Collectors;
033
034import org.apache.avalon.framework.service.ServiceException;
035import org.apache.avalon.framework.service.ServiceManager;
036import org.apache.cocoon.ProcessingException;
037import org.apache.cocoon.generation.ServiceableGenerator;
038import org.apache.cocoon.xml.AttributesImpl;
039import org.apache.cocoon.xml.XMLUtils;
040import org.apache.commons.lang3.tuple.Pair;
041import org.apache.excalibur.xml.sax.SAXParser;
042import org.xml.sax.InputSource;
043import org.xml.sax.SAXException;
044
045import org.ametys.core.authentication.CredentialProvider;
046import org.ametys.core.authentication.CredentialProviderFactory;
047import org.ametys.core.authentication.CredentialProviderModel;
048import org.ametys.core.datasource.LDAPDataSourceManager;
049import org.ametys.core.datasource.SQLDataSourceManager;
050import org.ametys.core.user.directory.UserDirectory;
051import org.ametys.core.user.directory.UserDirectoryFactory;
052import org.ametys.core.user.directory.UserDirectoryModel;
053import org.ametys.core.user.population.PopulationContextHelper;
054import org.ametys.core.user.population.UserPopulation;
055import org.ametys.core.user.population.UserPopulationDAO;
056import org.ametys.core.util.IgnoreRootHandler;
057import org.ametys.runtime.config.Config;
058import org.ametys.runtime.model.ElementDefinition;
059import org.ametys.runtime.model.type.ModelItemTypeConstants;
060import org.ametys.web.repository.site.SiteManager;
061
062/**
063 * Sax the datasources files limited to datasources useful for front-office
064 */
065public class SitesPopulationsGenerator extends ServiceableGenerator
066{
067    private SiteManager _siteManager;
068    private PopulationContextHelper _populationContextHelper;
069    private UserPopulationDAO _userPopulationDAO;
070    private UserDirectoryFactory _userDirectoryFactory;
071    private CredentialProviderFactory _credentialProviderFactory;
072    private SQLDataSourceManager _sqlDataSourceManager;
073    private LDAPDataSourceManager _ldapDataSourceManager;
074
075    @Override
076    public void service(ServiceManager smanager) throws ServiceException
077    {
078        super.service(smanager);
079        _siteManager = (SiteManager) smanager.lookup(SiteManager.ROLE);
080        _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE);
081        _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE);
082        _userDirectoryFactory = (UserDirectoryFactory) manager.lookup(UserDirectoryFactory.ROLE);
083        _credentialProviderFactory = (CredentialProviderFactory) manager.lookup(CredentialProviderFactory.ROLE);
084        _sqlDataSourceManager = (SQLDataSourceManager) manager.lookup(SQLDataSourceManager.ROLE);
085        _ldapDataSourceManager = (LDAPDataSourceManager) manager.lookup(LDAPDataSourceManager.ROLE);
086    }
087    
088    public void generate() throws IOException, SAXException, ProcessingException
089    {
090        contentHandler.startDocument();
091        XMLUtils.startElement(contentHandler, "Populations");
092        
093        _saxUsedStuff();
094        _saxPopulationFile();
095        _saxDatabasesFiles();
096        _saxMonitoringInfo();
097        _saxCaptchaInfo();
098        _saxMaxUploadSizeInfo();
099        
100        XMLUtils.endElement(contentHandler, "Populations");
101        contentHandler.endDocument();
102    }
103    
104    private void _saxMonitoringInfo() throws SAXException
105    {
106        boolean enabled = Config.getInstance().getValue("cache.monitoring.schedulers.enable");
107        
108        AttributesImpl attrs = new AttributesImpl();
109        attrs.addCDATAAttribute("enabled", enabled ? "true" : "false");
110        XMLUtils.startElement(contentHandler, "Monitoring", attrs);
111
112        if (enabled)
113        {
114            String datasourceId = Config.getInstance().getValue("cache.monitoring.datasource.jdbc.pool");
115            XMLUtils.createElement(contentHandler, "Datasource", _replaceDefaultIds(datasourceId));
116        }
117        
118        XMLUtils.endElement(contentHandler, "Monitoring");
119    }
120    
121    private void _saxCaptchaInfo() throws SAXException
122    {
123        String type = Config.getInstance().getValue("runtime.captcha.type");
124        String publicKey = Config.getInstance().getValue("runtime.captcha.recaptcha.publickey");
125        String secretKey = Config.getInstance().getValue("runtime.captcha.recaptcha.secretkey");
126        
127        AttributesImpl attrs = new AttributesImpl();
128        attrs.addCDATAAttribute("type", type);
129        attrs.addCDATAAttribute("publicKey", publicKey);
130        attrs.addCDATAAttribute("secretKey", secretKey);
131        
132        XMLUtils.createElement(contentHandler, "Captcha", attrs);
133    }
134    
135    private void _saxMaxUploadSizeInfo() throws SAXException
136    {
137        long uploadMaxSize = Config.getInstance().getValue("runtime.upload.max-size");
138        
139        XMLUtils.createElement(contentHandler, "UploadMaxSize", Long.toString(uploadMaxSize));
140    }
141
142    private void _saxDatabasesFiles() throws IOException, SAXException
143    {
144        _saxSQLDatabaseFile();
145
146        _saxLDAPDatabaseFile();
147    }
148
149    private void _saxLDAPDatabaseFile() throws SAXException, IOException
150    {
151        XMLUtils.startElement(contentHandler, "LDAPDatasources");
152
153        File file = _ldapDataSourceManager.getFileConfiguration();
154        if (file.exists())
155        {
156            SAXParser saxParser = null;
157            try (InputStream is = new FileInputStream(file))
158            {
159                saxParser = (SAXParser) manager.lookup(SAXParser.ROLE);
160                saxParser.parse(new InputSource(is), new IgnoreRootHandler(contentHandler));
161            }
162            catch (FileNotFoundException e)
163            {
164                throw new IOException(e);
165            }
166            catch (ServiceException e)
167            {
168                throw new SAXException("Unable to get a SAX parser", e);
169            }
170            finally
171            {
172                manager.release(saxParser);
173            }
174        }
175
176        XMLUtils.endElement(contentHandler, "LDAPDatasources");
177    }
178
179    private void _saxSQLDatabaseFile() throws SAXException, IOException
180    {
181        XMLUtils.startElement(contentHandler, "SQLDatasources");
182
183        File file = _sqlDataSourceManager.getFileConfiguration();
184        if (file.exists())
185        {
186            SAXParser saxParser = null;
187            try (InputStream is = new FileInputStream(file))
188            {
189                saxParser = (SAXParser) manager.lookup(SAXParser.ROLE);
190                saxParser.parse(new InputSource(is), new IgnoreRootHandler(contentHandler));
191            }
192            catch (FileNotFoundException e)
193            {
194                throw new IOException(e);
195            }
196            catch (ServiceException e)
197            {
198                throw new SAXException("Unable to get a SAX parser", e);
199            }
200            finally
201            {
202                manager.release(saxParser);
203            }
204        }
205        
206        XMLUtils.endElement(contentHandler, "SQLDatasources");
207    }
208
209    private void _saxPopulationFile() throws SAXException, IOException
210    {
211        XMLUtils.startElement(contentHandler, "UserPopulations");
212        File file = _userPopulationDAO.getConfigurationFile();
213
214        if (file != null && file.exists())
215        {
216            SAXParser saxParser = null;
217            try (InputStream is = Files.newInputStream(Paths.get(file.getAbsolutePath())))
218            {
219                saxParser = (SAXParser) manager.lookup(SAXParser.ROLE);
220                saxParser.parse(new InputSource(is), new IgnoreRootHandler(contentHandler));
221            }
222            catch (ServiceException e)
223            {
224                throw new SAXException("Unable to get a SAX parser", e);
225            }
226            finally
227            {
228                manager.release(saxParser);
229            }
230        }
231        
232        XMLUtils.endElement(contentHandler, "UserPopulations");
233    }
234
235    private void _saxUsedStuff() throws SAXException
236    {
237        Set<UserPopulation> usedPopulations = _getPopulationsUsedBySites();
238        
239        XMLUtils.startElement(contentHandler, "InUse");
240        
241        _saxPopulationsInUse(usedPopulations);
242        
243        _saxDatasourcesInUse(usedPopulations);
244
245        XMLUtils.endElement(contentHandler, "InUse");
246    }
247
248    private void _saxDatasourcesInUse(Set<UserPopulation> usedPopulations) throws SAXException
249    {
250        Pair<Set<String>, Map<String, Set<String>>> usedDatasources = _getDatasourcesUsedByPopulations(usedPopulations);
251        
252        XMLUtils.startElement(contentHandler, "Datasources");
253        
254        for (String datasourceId : usedDatasources.getLeft())
255        {
256            XMLUtils.createElement(contentHandler, "Datasource", _replaceDefaultIds(datasourceId));
257        }
258        
259        XMLUtils.endElement(contentHandler, "Datasources");
260        
261        XMLUtils.startElement(contentHandler, "Datasources-Model");
262        
263        Map<String, Set<String>> datasourcesPerModel = usedDatasources.getRight();
264        for (String modelId : datasourcesPerModel.keySet())
265        {
266            XMLUtils.startElement(contentHandler, modelId);
267            
268            for (String parameter : datasourcesPerModel.get(modelId))
269            {
270                XMLUtils.createElement(contentHandler, parameter);
271            }
272            
273            XMLUtils.endElement(contentHandler, modelId);
274        }
275        
276        XMLUtils.endElement(contentHandler, "Datasources-Model");
277    }
278
279    private void _saxPopulationsInUse(Set<UserPopulation> usedPopulations) throws SAXException
280    {
281        XMLUtils.startElement(contentHandler, "UserPopulations");
282        for (UserPopulation userPopulation : usedPopulations)
283        {
284            XMLUtils.createElement(contentHandler, "UserPopulation", userPopulation.getId());
285        }
286        XMLUtils.endElement(contentHandler, "UserPopulations");
287    }
288
289    private String _replaceDefaultIds(String datasourceId)
290    {
291        // Default sources won't be default anymore => change it
292        if (_ldapDataSourceManager.getDefaultDataSourceId().equals(datasourceId))
293        {
294            return _ldapDataSourceManager.getDefaultDataSourceDefinition().getId();
295        }
296        else if (_sqlDataSourceManager.getDefaultDataSourceId().equals(datasourceId))
297        {
298            return _sqlDataSourceManager.getDefaultDataSourceDefinition().getId();
299        }
300        else
301        {
302            return datasourceId;
303        }
304    }
305
306    private Set<UserPopulation> _getPopulationsUsedBySites()
307    {
308        // Retrieve the sites to build the contexts to search on
309        Collection<String> siteNames = _siteManager.getSiteNames();
310        
311        // We return all the populations linked to at least one site
312        List<String> contexts = new ArrayList<>();
313        for (String siteName : siteNames)
314        {
315            contexts.add("/sites/" + siteName);
316            contexts.add("/sites-fo/" + siteName);
317        }
318        
319        Set<String> populations = _populationContextHelper.getUserPopulationsOnContexts(contexts, false, false);
320        return populations.stream().map(_userPopulationDAO::getUserPopulation).collect(Collectors.toSet());
321    }
322    
323    private Pair<Set<String>, Map<String, Set<String>>> _getDatasourcesUsedByPopulations(Set<UserPopulation> usedPopulations)
324    {
325        Set<String> datasourcesInUse = new HashSet<>();
326        Map<String, Set<String>> datasourcesPerModel = new HashMap<>();
327        
328        for (UserPopulation userPopulation : usedPopulations)
329        {
330            for (UserDirectory userDirectory : userPopulation.getUserDirectories())
331            {
332                String userDirectoryModelId = userDirectory.getUserDirectoryModelId();
333                UserDirectoryModel userDirectoryModel = _userDirectoryFactory.getExtension(userDirectoryModelId);
334                
335                Map<String, Object> parameterValues = userDirectory.getParameterValues();
336                
337                Map<String, ? extends ElementDefinition> userDirectoryModelParameters = userDirectoryModel.getParameters();
338                for (String userDirectoryModelParameterId : userDirectoryModelParameters.keySet())
339                {
340                    ElementDefinition userDirectoryModelParameter = userDirectoryModelParameters.get(userDirectoryModelParameterId);
341                    if (ModelItemTypeConstants.DATASOURCE_ELEMENT_TYPE_ID.equals(userDirectoryModelParameter.getType().getId()))
342                    {
343                        String datasourceId = (String) parameterValues.get(userDirectoryModelParameterId);
344                        datasourcesInUse.add(datasourceId);
345                        _addDatasourceToModel(userDirectoryModelId, userDirectoryModelParameterId, datasourcesPerModel);
346                    }
347                }
348            }
349            
350            for (CredentialProvider credentialProvider : userPopulation.getCredentialProviders())
351            {
352                String credentialProviderModelId = credentialProvider.getCredentialProviderModelId();
353                CredentialProviderModel credentialProviderModel = _credentialProviderFactory.getExtension(credentialProviderModelId);
354                
355                Map<String, Object> parameterValues = credentialProvider.getParameterValues();
356                
357                Map<String, ? extends ElementDefinition> credentialProviderModelParameters = credentialProviderModel.getParameters();
358                for (String credentialProviderParameterId : credentialProviderModelParameters.keySet())
359                {
360                    ElementDefinition credentialProviderModelParameter = credentialProviderModelParameters.get(credentialProviderParameterId);
361                    if (ModelItemTypeConstants.DATASOURCE_ELEMENT_TYPE_ID.equals(credentialProviderModelParameter.getType().getId()))
362                    {
363                        String datasourceId = (String) parameterValues.get(credentialProviderParameterId);
364                        datasourcesInUse.add(datasourceId);
365                        _addDatasourceToModel(credentialProviderModelId, credentialProviderParameterId, datasourcesPerModel);
366                    }
367                }
368            }
369        }
370        
371        return Pair.of(datasourcesInUse, datasourcesPerModel);
372    }
373    
374    private void _addDatasourceToModel(String modelId, String datasource, Map<String, Set<String>> datasourcesPerModel)
375    {
376        Set<String> datasources = datasourcesPerModel.get(modelId);
377        if (datasources == null)
378        {
379            datasources = new HashSet<>(); 
380            datasourcesPerModel.put(modelId, datasources);
381        }
382        
383        datasources.add(datasource);
384    }
385}