/** * Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2006-2016 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.glite.security.voms.admin.core; import java.io.File; import java.util.EnumSet; import java.util.List; import java.util.Properties; import java.util.concurrent.TimeUnit; import javax.servlet.DispatcherType; import javax.servlet.FilterRegistration; import javax.servlet.ServletContext; import org.apache.commons.lang.Validate; import org.apache.struts2.dispatcher.Dispatcher; import org.apache.velocity.app.Velocity; import org.glite.security.voms.admin.configuration.VOMSConfiguration; import org.glite.security.voms.admin.configuration.VOMSConfigurationConstants; import org.glite.security.voms.admin.configuration.VOMSConfigurationException; import org.glite.security.voms.admin.core.tasks.CancelSignAUPTasksForExpiredUsersTask; import org.glite.security.voms.admin.core.tasks.ExpiredRequestsPurgerTask; import org.glite.security.voms.admin.core.tasks.PermissionCacheStatsLogger; import org.glite.security.voms.admin.core.tasks.PrintX509AAStatsTask; import org.glite.security.voms.admin.core.tasks.SignAUPReminderCheckTask; import org.glite.security.voms.admin.core.tasks.SystemTimeProvider; import org.glite.security.voms.admin.core.tasks.TaskStatusUpdater; import org.glite.security.voms.admin.core.tasks.ThreadUncaughtExceptionHandler; import org.glite.security.voms.admin.core.tasks.UpdateCATask; import org.glite.security.voms.admin.core.tasks.UserStatsTask; import org.glite.security.voms.admin.core.tasks.VOMSExecutorService; import org.glite.security.voms.admin.core.validation.ValidationManager; import org.glite.security.voms.admin.error.VOMSFatalException; import org.glite.security.voms.admin.event.CleanPermissionCacheListener; import org.glite.security.voms.admin.event.DebugEventLogListener; import org.glite.security.voms.admin.event.EventManager; import org.glite.security.voms.admin.event.auditing.AuditLog; import org.glite.security.voms.admin.integration.PluginManager; import org.glite.security.voms.admin.integration.orgdb.OrgDBConfigurator; import org.glite.security.voms.admin.integration.orgdb.servlet.OrgDbHibernateSessionFilter; import org.glite.security.voms.admin.notification.NotificationServiceFactory; import org.glite.security.voms.admin.notification.PersistentNotificationService; import org.glite.security.voms.admin.notification.VOMSNotificationSettings; import org.glite.security.voms.admin.notification.dispatchers.CertificateRequestsNotificationDispatcher; import org.glite.security.voms.admin.notification.dispatchers.DefaultNotificationDispatcher; import org.glite.security.voms.admin.notification.dispatchers.GroupMembershipNotificationDispatcher; import org.glite.security.voms.admin.notification.dispatchers.MembershipRemovalNotificationDispatcher; import org.glite.security.voms.admin.notification.dispatchers.RoleMembershipNotificationDispatcher; import org.glite.security.voms.admin.notification.dispatchers.SignAUPReminderDispatcher; import org.glite.security.voms.admin.notification.dispatchers.UserSuspendedDispatcher; import org.glite.security.voms.admin.notification.dispatchers.VOMembershipNotificationDispatcher; import org.glite.security.voms.admin.operations.DefaultPrincipalProvider; import org.glite.security.voms.admin.persistence.HibernateFactory; import org.glite.security.voms.admin.persistence.SchemaVersion; import org.glite.security.voms.admin.persistence.dao.VOMSVersionDAO; import org.glite.security.voms.admin.persistence.dao.generic.DAOFactory; import org.glite.security.voms.admin.persistence.dao.lookup.LookupPolicyProvider; import org.glite.security.voms.admin.persistence.model.AUP; import org.glite.security.voms.admin.util.validation.x509.VOMSAdminDnValidator; import org.glite.security.voms.admin.view.util.DevModeEnabler; import org.italiangrid.voms.aa.x509.ACGeneratorFactory; import org.opensaml.xml.ConfigurationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.util.StatusPrinter; public final class VOMSService { public static final String ENDPOINTS_KEY = "__voms_endpoints"; static final Logger LOG = LoggerFactory.getLogger(VOMSService.class); protected static void checkDatabaseVersion() { final String detectedDbVersion = VOMSVersionDAO.instance() .getVersion() .getAdminVersion(); int detectedDbVersionInt = -1; try { detectedDbVersionInt = Math.abs(Integer.parseInt(detectedDbVersion)); } catch (NumberFormatException ex) { String msg = String.format( "VOMS DATABASE SCHEMA ERROR: incompatible database. Found '%s' while expecting '%s'." + " Please upgrade the database for this installation using 'voms-db-util upgrade'" + " command.", detectedDbVersion, SchemaVersion.VOMS_ADMIN_DB_VERSION); LOG.error(msg); throw new VOMSFatalException(msg); } if (detectedDbVersionInt < SchemaVersion.VOMS_ADMIN_DB_VERSION_INT) { String msg = String.format( "VOMS DATABASE SCHEMA ERROR: incompatible database. Found '%s' while expecting '%s'." + " Please upgrade the database for this installation using 'voms-db-util upgrade'" + " command.", detectedDbVersion, SchemaVersion.VOMS_ADMIN_DB_VERSION); LOG.error(msg); throw new VOMSFatalException(msg); } } protected static void configureVelocity() { try { Properties p = new Properties(); p.put("resource.loader", "cpath"); p.put("cpath.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); p.put("runtime.log.logsystem.class", "org.glite.security.voms.admin.util.velocity.VelocityLogger"); Velocity.init(p); LOG.debug("Velocity setup ok!"); } catch (Exception e) { LOG.error("Error initializing velocity template engine!"); throw new VOMSFatalException(e); } } protected static void configureEventManager() { EventManager manager = EventManager.instance(); AuditLog.INSTANCE.setPrincipalProvider(new DefaultPrincipalProvider()); AuditLog.INSTANCE.setDaoFactory(DAOFactory.instance()); manager.register(AuditLog.INSTANCE); manager.register(DebugEventLogListener.instance()); manager.register(UserSuspendedDispatcher.instance()); manager.register(DefaultNotificationDispatcher.instance()); manager.register(GroupMembershipNotificationDispatcher.instance()); manager.register(RoleMembershipNotificationDispatcher.instance()); manager.register(VOMembershipNotificationDispatcher.instance()); manager.register(CertificateRequestsNotificationDispatcher.instance()); manager.register(MembershipRemovalNotificationDispatcher.instance()); manager.register(SignAUPReminderDispatcher.instance()); manager.register(CleanPermissionCacheListener.instance()); } protected static void startBackgroundTasks() { VOMSExecutorService es = VOMSExecutorService.instance(); VOMSConfiguration conf = VOMSConfiguration.instance(); List<Integer> aupReminders = conf.getAUPReminderIntervals(); es.startBackgroundTask( new SignAUPReminderCheckTask(DAOFactory.instance(), EventManager.instance(), SystemTimeProvider.INSTANCE, aupReminders, TimeUnit.DAYS), VOMSConfigurationConstants.MEMBERSHIP_CHECK_PERIOD, 300L); es.startBackgroundTask( new CancelSignAUPTasksForExpiredUsersTask(DAOFactory.instance(), EventManager.instance()), VOMSConfigurationConstants.MEMBERSHIP_CHECK_PERIOD, 300L); es.startBackgroundTask(new UpdateCATask(), VOMSConfigurationConstants.TRUST_ANCHORS_REFRESH_PERIOD); es.startBackgroundTask(new TaskStatusUpdater(), 30L); es.startBackgroundTask( new ExpiredRequestsPurgerTask(DAOFactory.instance(), EventManager.instance()), VOMSConfigurationConstants.VO_MEMBERSHIP_EXPIRED_REQ_PURGER_PERIOD, 300L); es.startBackgroundTask(new UserStatsTask(), VOMSConfigurationConstants.MONITORING_USER_STATS_UPDATE_PERIOD, UserStatsTask.DEFAULT_PERIOD_IN_SECONDS); es.scheduleAtFixedRate(new PermissionCacheStatsLogger(true), 1, 60, TimeUnit.SECONDS); } protected static void startNotificationService() { if (!VOMSConfiguration.instance() .getBoolean(VOMSConfigurationConstants.NOTIFICATION_DISABLED, false)) { PersistentNotificationService ns = PersistentNotificationService.INSTANCE; ns.setNotificationSettings( VOMSNotificationSettings.fromVOMSConfiguration()); ns.setDao(DAOFactory.instance() .getNotificationDAO()); ns.start(); } else { LOG.warn("Notification service is DISABLED."); } } protected static void bootstrapAttributeAuthorityServices() { VOMSConfiguration conf = VOMSConfiguration.instance(); try { org.opensaml.DefaultBootstrap.bootstrap(); } catch (ConfigurationException e) { LOG.error("Error initializing OpenSAML:" + e.getMessage()); if (LOG.isDebugEnabled()) LOG.error("Error initializing OpenSAML:" + e.getMessage(), e); LOG.info("SAML endpoint will not be activated."); conf.setProperty( VOMSConfigurationConstants.VOMS_AA_SAML_ACTIVATE_ENDPOINT, false); } boolean x509AcEndpointEnabled = conf.getBoolean( VOMSConfigurationConstants.VOMS_AA_X509_ACTIVATE_ENDPOINT, false); if (x509AcEndpointEnabled) { LOG.info("Bootstrapping VOMS X.509 attribute authority."); ACGeneratorFactory.newACGenerator() .configure(conf.getServiceCredential()); VOMSExecutorService es = VOMSExecutorService.instance(); es.scheduleAtFixedRate(new PrintX509AAStatsTask(), PrintX509AAStatsTask.DEFAULT_PERIOD_IN_SECS, PrintX509AAStatsTask.DEFAULT_PERIOD_IN_SECS, TimeUnit.SECONDS); } else { LOG.info("X.509 attribute authority is disabled."); } } protected static void configureLogging(ServletContext ctxt) { String confDir = ctxt.getInitParameter("CONF_DIR"); String vo = ctxt.getInitParameter("VO_NAME"); String loggingConf = String.format("%s/%s/%s", confDir, vo, "logback.xml"); File f = new File(loggingConf); if (!f.exists()) throw new VOMSFatalException(String.format( "Logging configuration " + "not found at path '%s'", loggingConf)); if (!f.canRead()) throw new VOMSFatalException(String .format("Logging configuration " + "is not readable: %s", loggingConf)); LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); lc.setName(vo); JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); lc.reset(); // We leave this here to avoid runtime errors for people that // update the service, but not the logging configuration // FIXME: to be removed at some point lc.putProperty(VOMSConfigurationConstants.VO_NAME, vo); try { configurator.doConfigure(f); } catch (JoranException e) { throw new VOMSFatalException("Error setting up the logging system", e); } StatusPrinter.printIfErrorsOccured(lc); } public static void bootstrapPersistence(VOMSConfiguration configuration) { Validate.notNull(configuration); HibernateFactory.initialize(configuration.getDatabaseProperties()); } private static void initializeDnValidator() { VOMSAdminDnValidator.INSTANCE.initialize("/etc/grid-security/certificates", true); } private static void configureOrgDbHibernateSessionFitler(ServletContext ctxt){ if (!VOMSConfiguration.instance().getRegistrationType().equals( OrgDBConfigurator.ORGDB_REGISTRATION_TYPE)){ return; } FilterRegistration.Dynamic fr = ctxt.addFilter("orgdb-hibernate-session-filter", OrgDbHibernateSessionFilter.class); fr.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "*"); } private static void setupGlobalApplicationObjects(ServletContext ctxt){ AUP aup = DAOFactory.instance().getAUPDAO().getVOAUP(); ctxt.setAttribute( "registrationEnabled", VOMSConfiguration.instance().getBoolean( VOMSConfigurationConstants.REGISTRATION_SERVICE_ENABLED, true)); ctxt.setAttribute( "readOnlyPI", VOMSConfiguration.instance() .getBoolean( VOMSConfigurationConstants.VOMS_INTERNAL_RO_PERSONAL_INFORMATION, false)); ctxt.setAttribute( "readOnlyMembershipExpiration", VOMSConfiguration.instance().getBoolean( VOMSConfigurationConstants.VOMS_INTERNAL_RO_MEMBERSHIP_EXPIRATION_DATE, false)); ctxt.setAttribute( "disableMembershipEndTime", VOMSConfiguration.instance().getBoolean( VOMSConfigurationConstants.DISABLE_MEMBERSHIP_END_TIME, false)); ctxt.setAttribute("defaultAUP", aup); ctxt.setAttribute("orgdbEnabled", VOMSConfiguration.instance().getRegistrationType().equals( OrgDBConfigurator.ORGDB_REGISTRATION_TYPE)); } public static void start(ServletContext ctxt) { Thread .setDefaultUncaughtExceptionHandler(new ThreadUncaughtExceptionHandler()); configureLogging(ctxt); VOMSConfiguration conf; try { conf = VOMSConfiguration.load(ctxt); } catch (VOMSConfigurationException e) { LOG.error("VOMS-Admin configuration failed!", e); throw new VOMSFatalException(e); } LOG.info("VOMS-Admin starting for VO: " + conf.getVOName()); bootstrapPersistence(conf); setupStrutsDevMode(); checkDatabaseVersion(); configureCertificateLookupPolicy(conf); configureVelocity(); configureEventManager(); startBackgroundTasks(); startNotificationService(); bootstrapAttributeAuthorityServices(); PluginManager.instance() .configurePlugins(); ValidationManager.instance() .startMembershipChecker(); initializeDnValidator(); configureOrgDbHibernateSessionFitler(ctxt); setupGlobalApplicationObjects(ctxt); LOG.info("VOMS-Admin started succesfully."); } private static void configureCertificateLookupPolicy(VOMSConfiguration conf) { boolean skipCaCheck = conf .getBoolean(VOMSConfigurationConstants.SKIP_CA_CHECK, false); if (skipCaCheck) { LOG.info( "CertificateLookupPolicy: VOMS Users, certificates and administrators will be looked up by certificate subject ({} == true)", VOMSConfigurationConstants.SKIP_CA_CHECK); } else { LOG.info( "CertficateLookupPolicy: VOMS Users, certificates and administrators will be looked up by certificate subject AND issuer ({} == false)", VOMSConfigurationConstants.SKIP_CA_CHECK); } LookupPolicyProvider.initialize(skipCaCheck); } private static void setupStrutsDevMode() { if (System.getProperty(VOMSServiceConstants.DEV_MODE_PROPERTY) != null) { Dispatcher.addDispatcherListener(new DevModeEnabler()); } } public static void stop() { VOMSExecutorService.shutdown(); NotificationServiceFactory.getNotificationService() .shutdownNow(); HibernateFactory.shutdown(); LOG.info("VOMS admin stopped ."); } }