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