/**
* Copyright (C) 2014 EDIT
* European Distributed Institute of Taxonomy
* http://www.e-taxonomy.eu
*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* See LICENSE.TXT at the top of this package for the full license terms.
*/
package eu.etaxonomy.cdm.api.application;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.remoting.httpinvoker.CachingHttpInvokerProxyFactoryBean;
import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.dao.ReflectionSaltSource;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import eu.etaxonomy.cdm.api.service.IAgentService;
import eu.etaxonomy.cdm.api.service.IAnnotationService;
import eu.etaxonomy.cdm.api.service.IClassificationService;
import eu.etaxonomy.cdm.api.service.ICollectionService;
import eu.etaxonomy.cdm.api.service.ICommonService;
import eu.etaxonomy.cdm.api.service.IDatabaseService;
import eu.etaxonomy.cdm.api.service.IDescriptionElementService;
import eu.etaxonomy.cdm.api.service.IDescriptionService;
import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
import eu.etaxonomy.cdm.api.service.IEntityConstraintViolationService;
import eu.etaxonomy.cdm.api.service.IEntityValidationService;
import eu.etaxonomy.cdm.api.service.IEventBaseService;
import eu.etaxonomy.cdm.api.service.IGrantedAuthorityService;
import eu.etaxonomy.cdm.api.service.IGroupService;
import eu.etaxonomy.cdm.api.service.IIdentificationKeyService;
import eu.etaxonomy.cdm.api.service.ILocationService;
import eu.etaxonomy.cdm.api.service.IMediaService;
import eu.etaxonomy.cdm.api.service.IMetadataService;
import eu.etaxonomy.cdm.api.service.INameService;
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
import eu.etaxonomy.cdm.api.service.IPolytomousKeyNodeService;
import eu.etaxonomy.cdm.api.service.IPolytomousKeyService;
import eu.etaxonomy.cdm.api.service.IPreferenceService;
import eu.etaxonomy.cdm.api.service.IProgressMonitorService;
import eu.etaxonomy.cdm.api.service.IReferenceService;
import eu.etaxonomy.cdm.api.service.IRegistrationService;
import eu.etaxonomy.cdm.api.service.IRightsService;
import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
import eu.etaxonomy.cdm.api.service.ITaxonService;
import eu.etaxonomy.cdm.api.service.ITermCollectionService;
import eu.etaxonomy.cdm.api.service.ITermNodeService;
import eu.etaxonomy.cdm.api.service.ITermService;
import eu.etaxonomy.cdm.api.service.ITermTreeService;
import eu.etaxonomy.cdm.api.service.ITestService;
import eu.etaxonomy.cdm.api.service.IUserService;
import eu.etaxonomy.cdm.api.service.IVocabularyService;
import eu.etaxonomy.cdm.api.service.geo.IDistributionService;
import eu.etaxonomy.cdm.api.service.longrunningService.ILongRunningTasksService;
import eu.etaxonomy.cdm.api.service.media.MediaInfoFactory;
import eu.etaxonomy.cdm.api.service.molecular.IAmplificationService;
import eu.etaxonomy.cdm.api.service.molecular.IPrimerService;
import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
import eu.etaxonomy.cdm.api.service.registration.IRegistrationWorkingSetService;
import eu.etaxonomy.cdm.api.service.security.IAccountRegistrationService;
import eu.etaxonomy.cdm.api.service.security.IPasswordResetService;
import eu.etaxonomy.cdm.ext.geo.IEditGeoService;
import eu.etaxonomy.cdm.io.service.IIOService;
import eu.etaxonomy.cdm.persistence.permission.CdmPermissionEvaluator;
import eu.etaxonomy.cdm.persistence.permission.ICdmPermissionEvaluator;
import eu.etaxonomy.cdm.persistence.permission.UnanimousBasedUnrevokable;
import eu.etaxonomy.cdm.persistence.permission.voter.DescriptionBaseVoter;
import eu.etaxonomy.cdm.persistence.permission.voter.DescriptionElementVoter;
import eu.etaxonomy.cdm.persistence.permission.voter.GrantAlwaysVoter;
import eu.etaxonomy.cdm.persistence.permission.voter.TaxonBaseVoter;
import eu.etaxonomy.cdm.persistence.permission.voter.TaxonNodeVoter;
import eu.etaxonomy.taxeditor.remoting.source.CdmRemoteSource;
import eu.etaxonomy.taxeditor.remoting.source.ICdmRemoteSource;
import eu.etaxonomy.taxeditor.service.AuthenticatingHttpInvokerRequestExecutor;
import eu.etaxonomy.taxeditor.service.CachedCommonServiceImpl;
import eu.etaxonomy.taxeditor.service.ICachedCommonService;
import eu.etaxonomy.taxeditor.service.RemoteInvocationTermCacher;
import eu.etaxonomy.taxeditor.session.CdmEntitySessionManager;
import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
/**
* CDM Application Configuration class which manages the configuration for remoting
* clients
*/
@Component
// TODO split into CdmRepository and proper @Configuration class
public class CdmApplicationRemoteConfiguration implements ICdmRepository {
@SuppressWarnings("unused")
private static final Logger logger = LogManager.getLogger();
/**
* Timeout for service lookup etc. This timeout (milliseconds) should be more
* strict than {@link #HTTP_READ_TIMEOUT} to avoid connecting to
* cdm servers when the network quality is too bad.
* Note AM: IMO we should not be to strict, sometimes services do not respond too fast
* during the first call (e.g. test server), therefore I put this up from 1 to 3 sec.
* Better we should inform the user that the connection might be of low quality
* instead of making the connection completely unavailable.
*/
public static final int HTTP_READ_TIMEOUT_MIN = 3000; // 3 seconds
/**
* Timeout for normal operation (milliseconds)
*
* Infinite time out (value = 0) can cause the application to be stuck in
* SocketInputStream.read(), Therefore it makes sense to specify a definite
* timeout which is high enough to allow for longer operations
*
* The application should be usable in networks with low connection quality,
* e.g.: from cuba where the throughput rate is low (<= ISDN speed, 64 kbit)
* and a packet delay of <200 ms.
*
*/
public static final int HTTP_READ_TIMEOUT = 10 * 60 * 1000;
private ICdmRemoteSource remoteSource;
//TODO should be something like Map, IService>, but we have no common service base interface yet
private Map, Object> serviceMap = new HashMap<>();
private ICdmEntitySessionManager cdmEntitySessionManager;
private CdmPermissionEvaluator cdmPermissionEvaluator;
private ProviderManager authenticationManager;
private ICachedCommonService cachedCommonService;
static {
// The Taxeditor is multithreaded, the same authentication should be used
// in all threads
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_GLOBAL);
}
public CdmApplicationRemoteConfiguration() {}
public CdmApplicationRemoteConfiguration(ICdmRemoteSource remoteSource) {
this.remoteSource = remoteSource;
}
public void setRemoteSource(ICdmRemoteSource remoteSource) {
this.remoteSource = remoteSource;
}
private Object getService(Class/* extends IService*/> clazz, String serviceSuffix, HttpComponentsHttpInvokerRequestExecutor executor) {
if(serviceMap.containsKey(clazz)) {
return serviceMap.get(clazz);
}
Object service = getService(clazz, serviceSuffix, remoteSource, executor);
serviceMap.put(clazz, service);
return service;
}
public static Object getService(Class> clazz,
String serviceSuffix,
ICdmRemoteSource remoteSource,
HttpComponentsHttpInvokerRequestExecutor executor) {
String baseUrl;
String protocol = remoteSource.getPort() == 443 ? "https" : "http";
String contextPath = (remoteSource instanceof CdmRemoteSource)? ((CdmRemoteSource)remoteSource).getContextPath() : "";
if(StringUtils.isBlank(contextPath)) {
baseUrl = protocol + "://" + remoteSource.getServer() + ":" + String.valueOf(remoteSource.getPort());
} else {
baseUrl = protocol + "://" + remoteSource.getServer() + ":" + String.valueOf(remoteSource.getPort()) + "/" + contextPath;
}
CachingHttpInvokerProxyFactoryBean proxyFactory = new CachingHttpInvokerProxyFactoryBean();
proxyFactory.setServiceInterface(clazz);
proxyFactory.setServiceUrl(baseUrl + serviceSuffix);
if(executor != null) {
executor.setReadTimeout(HTTP_READ_TIMEOUT);
executor.setConnectionRequestTimeout(HTTP_READ_TIMEOUT);
proxyFactory.setHttpInvokerRequestExecutor(executor);
}
if(ITermService.class.isAssignableFrom(clazz)){
proxyFactory.setRemoteInvocationTermCacher(new RemoteInvocationTermCacher());
}
proxyFactory.afterPropertiesSet();
return proxyFactory.getObject();
}
// ****************************** APPLICATION CONTEXT *************************************************/
public IEditGeoService getEditGeoService() {
return (IEditGeoService) getService(IEditGeoService.class, "/remoting/editgeo.service", new AuthenticatingHttpInvokerRequestExecutor());
}
public ICachedCommonService getCachedCommonService(){
if(cachedCommonService == null) {
cachedCommonService = new CachedCommonServiceImpl();
}
return cachedCommonService;
}
public ICdmEntitySessionManager getCdmEntitySessionManager() {
if(cdmEntitySessionManager == null) {
cdmEntitySessionManager = new CdmEntitySessionManager();
}
return cdmEntitySessionManager;
}
// ****************************** GETTER *************************************************/
@Override
public IAccountRegistrationService getAccountRegistrationService() {
return (IAccountRegistrationService) getService(IAccountRegistrationService.class, "/remoting/accountregistrationservice.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IAgentService getAgentService(){
return (IAgentService) getService(IAgentService.class, "/remoting/agent.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IAnnotationService getAnnotationService(){
return (IAnnotationService) getService(IAnnotationService.class, "/remoting/annotation.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IDatabaseService getDatabaseService(){
return (IDatabaseService) getService(IDatabaseService.class, "/remoting/database.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public INameService getNameService(){
return (INameService) getService(INameService.class, "/remoting/name.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IReferenceService getReferenceService(){
return (IReferenceService) getService(IReferenceService.class, "/remoting/reference.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITaxonService getTaxonService(){
return (ITaxonService) getService(ITaxonService.class, "/remoting/taxon.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IClassificationService getClassificationService(){
return (IClassificationService) getService(IClassificationService.class, "/remoting/classification.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITaxonNodeService getTaxonNodeService(){
return (ITaxonNodeService) getService(ITaxonNodeService.class, "/remoting/taxonnode.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IDescriptionService getDescriptionService(){
return (IDescriptionService) getService(IDescriptionService.class, "/remoting/description.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IDistributionService getDistributionService(){
return (IDistributionService) getService(IDistributionService.class, "/remoting/distribution.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IDescriptionElementService getDescriptionElementService(){
return (IDescriptionElementService) getService(IDescriptionElementService.class, "/remoting/descriptionelement.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IOccurrenceService getOccurrenceService(){
return (IOccurrenceService) getService(IOccurrenceService.class, "/remoting/occurrence.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IPrimerService getPrimerService(){
return (IPrimerService) getService(IPrimerService.class, "/remoting/primer.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IAmplificationService getAmplificationService(){
return (IAmplificationService) getService(IAmplificationService.class, "/remoting/amplification.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ISequenceService getSequenceService(){
return (ISequenceService) getService(ISequenceService.class, "/remoting/sequence.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IEventBaseService getEventBaseService() {
return (IEventBaseService) getService(IEventBaseService.class, "/remoting/eventbase.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IMediaService getMediaService(){
return (IMediaService) getService(IMediaService.class, "/remoting/media.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITermService getTermService(){
return (ITermService) getService(ITermService.class, "/remoting/term.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IRightsService getRightsService(){
return (IRightsService) getService(IRightsService.class, "/remoting/rights.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ICommonService getCommonService(){
return (ICommonService) getService(ICommonService.class, "/remoting/common.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ILocationService getLocationService(){
return (ILocationService) getService(ILocationService.class, "/remoting/location.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IUserService getUserService(){
return (IUserService) getService(IUserService.class, "/remoting-public/user.service", new AuthenticatingHttpInvokerRequestExecutor());
}
public static IUserService getUserService(ICdmRemoteSource remoteSource) {
return (IUserService) getService(IUserService.class, "/remoting-public/user.service", remoteSource, new HttpComponentsHttpInvokerRequestExecutor());
}
@Override
public IMetadataService getMetadataService() {
return (IMetadataService) getService(IMetadataService.class, "/remoting-public/metadata.service", new HttpComponentsHttpInvokerRequestExecutor());
}
public static IMetadataService getMetadataService(ICdmRemoteSource remoteSource) {
return (IMetadataService) getService(IMetadataService.class, "/remoting-public/metadata.service", remoteSource, new HttpComponentsHttpInvokerRequestExecutor());
}
@Override
public IGrantedAuthorityService getGrantedAuthorityService(){
return (IGrantedAuthorityService) getService(IGrantedAuthorityService.class, "/remoting/grantedauthority.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ProviderManager getAuthenticationManager(){
if(authenticationManager == null) {
authenticationManager = getAuthenticationManager(getUserService());
}
return authenticationManager;
}
public static ProviderManager getAuthenticationManager(IUserService userService) {
Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
ReflectionSaltSource saltSource = new ReflectionSaltSource();
saltSource.setUserPropertyToUse("getUsername");
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setUserDetailsService(userService);
daoAuthenticationProvider.setSaltSource(saltSource);
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
return new ProviderManager(Arrays.asList((AuthenticationProvider)daoAuthenticationProvider));
}
@Override
public ICollectionService getCollectionService(){
return (ICollectionService) getService(ICollectionService.class, "/remoting/collection.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITermTreeService getTermTreeService() {
return (ITermTreeService) getService(ITermTreeService.class, "/remoting/termtree.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITermCollectionService getTermCollectionService() {
return (ITermCollectionService) getService(ITermCollectionService.class, "/remoting/termcollection.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ITermNodeService getTermNodeService() {
return (ITermNodeService) getService(ITermNodeService.class, "/remoting/termnode.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IVocabularyService getVocabularyService(){
return (IVocabularyService) getService(IVocabularyService.class, "/remoting/vocabulary.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IIdentificationKeyService getIdentificationKeyService(){
return (IIdentificationKeyService) getService(IIdentificationKeyService.class, "/remoting/identificationkey.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IPolytomousKeyService getPolytomousKeyService(){
return (IPolytomousKeyService) getService(IPolytomousKeyService.class, "/remoting/polytomouskey.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IPolytomousKeyNodeService getPolytomousKeyNodeService(){
return (IPolytomousKeyNodeService) getService(IPolytomousKeyNodeService.class, "/remoting/polytomouskeynode.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IProgressMonitorService getProgressMonitorService() {
return (IProgressMonitorService) getService(IProgressMonitorService.class, "/remoting/progressmonitor.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IDescriptiveDataSetService getDescriptiveDataSetService(){
return (IDescriptiveDataSetService) getService(IDescriptiveDataSetService.class, "/remoting/descriptivedataset.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IRegistrationService getRegistrationService() {
return (IRegistrationService) getService(IRegistrationService.class, "/remoting/registration.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IRegistrationWorkingSetService getRegistrationWorkingSetService() {
return (IRegistrationWorkingSetService) getService(IRegistrationWorkingSetService.class, "/remoting/registrationworkingset.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IGroupService getGroupService(){
return (IGroupService) getService(IGroupService.class, "/remoting/group.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IPreferenceService getPreferenceService(){
return (IPreferenceService) getService(IPreferenceService.class, "/remoting/preference.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IPasswordResetService getPasswordResetService(){
return (IPasswordResetService) getService(IPasswordResetService.class, "/remoting/passwordreset.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IEntityValidationService getEntityValidationService(){
return (IEntityValidationService) getService(IEntityValidationService.class, "/remoting/entityvalidation.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public IEntityConstraintViolationService getEntityConstraintViolationService(){
return (IEntityConstraintViolationService) getService(IEntityConstraintViolationService.class, "/remoting/entityconstraintviolation.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public MediaInfoFactory getMediaInfoFactory(){ // TODO use interface
return (MediaInfoFactory) getService(MediaInfoFactory.class, "/remoting/mediainfofactory.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ICdmPermissionEvaluator getPermissionEvaluator(){
if(cdmPermissionEvaluator != null) {
return cdmPermissionEvaluator;
}
List> decisionVoters = new ArrayList<>();
decisionVoters.add(new GrantAlwaysVoter());
decisionVoters.add(new TaxonNodeVoter());
decisionVoters.add(new TaxonBaseVoter());
decisionVoters.add(new DescriptionBaseVoter());
decisionVoters.add(new DescriptionElementVoter());
UnanimousBasedUnrevokable accessDecisionManager = new UnanimousBasedUnrevokable(decisionVoters);
cdmPermissionEvaluator = new CdmPermissionEvaluator();
cdmPermissionEvaluator.setAccessDecisionManager(accessDecisionManager);
return cdmPermissionEvaluator;
}
@Override
public void authenticate(String username, String password){
UsernamePasswordAuthenticationToken tokenForUser = new UsernamePasswordAuthenticationToken(username, password);
Authentication authentication = this.getAuthenticationManager().authenticate(tokenForUser);
SecurityContext context = SecurityContextHolder.getContext();
context.setAuthentication(authentication);
}
public IIOService getIOService() {
return (IIOService) getService(IIOService.class, "/remoting/io.service", new AuthenticatingHttpInvokerRequestExecutor());
}
@Override
public ILongRunningTasksService getLongRunningTasksService() {
return (ILongRunningTasksService) getService(ILongRunningTasksService.class, "/remoting/longrunningtasks.service", new AuthenticatingHttpInvokerRequestExecutor());
}
public ITestService getTestService() {
return (ITestService) getService(ITestService.class, "/remoting/test.service", new AuthenticatingHttpInvokerRequestExecutor());
}
}