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