/* * oxTrust is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. * * Copyright (c) 2014, Gluu */ package org.gluu.oxtrust.service; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.PostConstruct; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.inject.Inject; import javax.inject.Named; import org.gluu.config.oxtrust.AppConfiguration; import org.gluu.model.GluuStatus; import org.gluu.oxtrust.model.GluuSAMLTrustRelationship; import org.gluu.oxtrust.model.GluuValidationStatus; import org.gluu.oxtrust.service.cdi.event.MetadataValidationEvent; import org.gluu.saml.metadata.SAMLMetadataParser; import org.gluu.service.cdi.async.Asynchronous; import org.gluu.service.cdi.event.Scheduled; import org.gluu.service.timer.event.TimerEvent; import org.gluu.service.timer.schedule.TimerSchedule; import org.gluu.util.StringHelper; import org.gluu.xml.GluuErrorHandler; import org.slf4j.Logger; /** * @author �Oleksiy Tataryn� * @author Yuriy Mochan * */ @ApplicationScoped @Named public class MetadataValidationTimer { private final static int DEFAULT_INTERVAL = 60; // 60 seconds @Inject private Logger log; @Inject private Event<TimerEvent> timerEvent; @Inject private AppConfiguration appConfiguration; @Inject private TrustService trustService; @Inject private SAMLMetadataParser samlMetadataParser; @Inject private Shibboleth3ConfService shibboleth3ConfService; private AtomicBoolean isActive; private LinkedBlockingQueue<String> metadataUpdates; @PostConstruct public void init() { this.isActive = new AtomicBoolean(true); try { this.metadataUpdates = new LinkedBlockingQueue<String>(); } finally { this.isActive.set(false); } } public void initTimer() { log.debug("Initializing Metadata Validation Timer"); final int delay = 30; final int interval = DEFAULT_INTERVAL; timerEvent.fire(new TimerEvent(new TimerSchedule(delay, interval), new MetadataValidationEvent(), Scheduled.Literal.INSTANCE)); } @Asynchronous public void processMetadataValidationTimerEvent( @Observes @Scheduled MetadataValidationEvent metadataValidationEvent) { if (this.isActive.get()) { return; } if (!this.isActive.compareAndSet(false, true)) { return; } try { procesMetadataValidation(); } catch (Throwable ex) { log.error("Exception happened while reloading application configuration", ex); } finally { this.isActive.set(false); } } private void procesMetadataValidation() { log.debug("Starting metadata validation"); boolean result = validateMetadata(shibboleth3ConfService.getIdpMetadataTempDir(), shibboleth3ConfService.getIdpMetadataDir()); log.debug("Metadata validation finished with result: '{}'", result); if (result) { regenerateConfigurationFiles(); } } public void queue(String fileName) { synchronized (metadataUpdates) { metadataUpdates.add(fileName); } } public boolean isQueued(String gluuSAMLspMetaDataFN) { synchronized (metadataUpdates) { for (String filename : metadataUpdates) { if (filename.contains(gluuSAMLspMetaDataFN)) { return true; } } return false; } } public String getValidationStatus(String gluuSAMLspMetaDataFN, GluuSAMLTrustRelationship trust) { if (trust.getValidationStatus() == null && trust.getGluuContainerFederation() != null) { return GluuValidationStatus.SUCCESS.getDisplayName(); } if (trust.getValidationStatus() == null) { return GluuValidationStatus.PENDING.getDisplayName(); } synchronized (metadataUpdates) { boolean result = false; for (String filename : metadataUpdates) { if (filename.contains(gluuSAMLspMetaDataFN)) { result = true; break; } } if (result) { return GluuValidationStatus.SCHEDULED.getDisplayName(); } else { return trust.getValidationStatus().getDisplayName(); } } } private void regenerateConfigurationFiles() { boolean createConfig = appConfiguration.isConfigGeneration(); if (createConfig) { List<GluuSAMLTrustRelationship> trustRelationships = trustService.getAllActiveTrustRelationships(); shibboleth3ConfService.generateConfigurationFiles(trustRelationships); log.info("IDP config generation files finished. TR count: '{}'", trustRelationships.size()); } } /** * @param shib3IdpTempmetadataFolder * @param shib3IdpMetadataFolder */ private boolean validateMetadata(String shib3IdpTempmetadataFolder, String shib3IdpMetadataFolder) { boolean result = false; log.trace("Starting metadata validation process."); String metadataFN = null; synchronized (metadataUpdates) { if (!metadataUpdates.isEmpty()) { metadataFN = metadataUpdates.poll(); } } synchronized (this) { if (StringHelper.isNotEmpty(metadataFN)) { String metadataPath = shib3IdpTempmetadataFolder + metadataFN; String destinationMetadataName = metadataFN.replaceAll(".{4}\\..{4}$", ""); String destinationMetadataPath = shib3IdpMetadataFolder + destinationMetadataName; GluuSAMLTrustRelationship tr = trustService.getTrustByUnpunctuatedInum( metadataFN.split("-" + Shibboleth3ConfService.SHIB3_IDP_SP_METADATA_FILE)[0]); if (tr == null) { metadataUpdates.add(metadataFN); return false; } tr.setValidationStatus(GluuValidationStatus.PENDING); trustService.updateTrustRelationship(tr); GluuErrorHandler errorHandler = null; List<String> validationLog = null; try { errorHandler = shibboleth3ConfService.validateMetadata(metadataPath); } catch (Exception e) { tr.setValidationStatus(GluuValidationStatus.FAILED); tr.setStatus(GluuStatus.INACTIVE); validationLog = new ArrayList<String>(); validationLog.add(e.getMessage()); log.warn("Validation of " + tr.getInum() + " failed: " + e.getMessage()); tr.setValidationLog(validationLog); trustService.updateTrustRelationship(tr); return false; } if (errorHandler.isValid()) { tr.setValidationLog(errorHandler.getLog()); tr.setValidationStatus(GluuValidationStatus.SUCCESS); if (shibboleth3ConfService.renameMetadata(metadataPath, destinationMetadataPath)) { log.error("Failed to move metadata file to location:" + destinationMetadataPath); tr.setStatus(GluuStatus.INACTIVE); } else { tr.setSpMetaDataFN(destinationMetadataName); } boolean federation = shibboleth3ConfService.isFederation(tr); tr.setFederation(federation); String metadataFile = shibboleth3ConfService.getIdpMetadataDir() + tr.getSpMetaDataFN(); List<String> entityIdList = samlMetadataParser.getEntityIdFromMetadataFile(metadataFile); Set<String> entityIdSet = new TreeSet<String>(); Set<String> duplicatesSet = new TreeSet<String>(); if (entityIdList != null && !entityIdList.isEmpty()) { for (String entityId : entityIdList) { if (!entityIdSet.add(entityId)) { duplicatesSet.add(entityId); } } } if (!duplicatesSet.isEmpty()) { validationLog = tr.getValidationLog(); if (validationLog != null) { validationLog = new LinkedList<String>(validationLog); } else { validationLog = new LinkedList<String>(); } validationLog.add("This metadata contains multiple instances of entityId: " + Arrays.toString(duplicatesSet.toArray())); } tr.setValidationLog(validationLog); tr.setGluuEntityId(entityIdSet); tr.setStatus(GluuStatus.ACTIVE); trustService.updateTrustRelationship(tr); result = true; } else if (appConfiguration.isIgnoreValidation() || errorHandler.isInternalError()) { tr.setValidationLog(new ArrayList<String>(new HashSet<String>(errorHandler.getLog()))); tr.setValidationStatus(GluuValidationStatus.FAILED); if (shibboleth3ConfService.renameMetadata(metadataPath, destinationMetadataPath)) { log.error("Failed to move metadata file to location:" + destinationMetadataPath); tr.setStatus(GluuStatus.INACTIVE); } else { tr.setSpMetaDataFN(destinationMetadataName); } boolean federation = shibboleth3ConfService.isFederation(tr); tr.setFederation(federation); String metadataFile = shibboleth3ConfService.getIdpMetadataDir() + tr.getSpMetaDataFN(); List<String> entityIdList = samlMetadataParser.getEntityIdFromMetadataFile(metadataFile); Set<String> duplicatesSet = new TreeSet<String>(); Set<String> entityIdSet = new TreeSet<String>(); for (String entityId : entityIdList) { if (!entityIdSet.add(entityId)) { duplicatesSet.add(entityId); } } tr.setGluuEntityId(entityIdSet); tr.setStatus(GluuStatus.ACTIVE); validationLog = tr.getValidationLog(); if (!duplicatesSet.isEmpty()) { validationLog.add("This metadata contains multiple instances of entityId: " + Arrays.toString(duplicatesSet.toArray())); } if (errorHandler.isInternalError()) { validationLog = tr.getValidationLog(); validationLog.add( "Warning: cannot validate metadata. Check internet connetion ans www.w3.org availability."); // update log with warning for (String warningLogMessage : errorHandler.getLog()) validationLog.add("Warning: " + warningLogMessage); } trustService.updateTrustRelationship(tr); result = true; } else { tr.setValidationLog(new ArrayList<String>(new HashSet<String>(errorHandler.getLog()))); tr.setValidationStatus(GluuValidationStatus.FAILED); tr.setStatus(GluuStatus.INACTIVE); trustService.updateTrustRelationship(tr); } } } return result; } }