/**
* 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 .");
}
}