diff --git a/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java b/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java index e88d15ba45848bc0f9db1244a01339fd0d4acdc8..30b6785406ae1a527c369b805ac817d65c8e0600 100644 --- a/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java +++ b/src/main/java/org/nrg/pipeline/XnatPipelineLauncher.java @@ -32,6 +32,7 @@ import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; +import java.nio.file.Paths; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -190,7 +191,7 @@ public class XnatPipelineLauncher { */ public boolean launch() { - return launch(XDAT.getSiteConfigPreferences().getPipelinePath() + "bin" + File.separator + SCHEDULE); + return launch(Paths.get(XDAT.getSiteConfigPreferences().getPipelinePath(), "bin", SCHEDULE).toString()); } /* @@ -303,7 +304,7 @@ public class XnatPipelineLauncher { command = ""; } - command += XDAT.getSiteConfigPreferences().getAdminEmail() + "bin" + File.separator + "XnatPipelineLauncher"; + command += Paths.get(XDAT.getSiteConfigPreferences().getPipelinePath(), "bin", "XnatPipelineLauncher").toString(); if (System.getProperty("os.name").toUpperCase().startsWith("WINDOWS")) { command += ".bat"; @@ -330,7 +331,7 @@ public class XnatPipelineLauncher { private List<String> getPipelineConfigurationArguments() { List<String> arguments = new ArrayList<>(); try { - String pipelinePath = new File(XDAT.getSiteConfigPreferences().getAdminEmail()).getCanonicalPath(); + String pipelinePath = new File(XDAT.getSiteConfigPreferences().getPipelinePath()).getCanonicalPath(); boolean requiresQuotes = pipelinePath.contains(" "); arguments.add("-config"); String configPath = pipelinePath + File.separator + "pipeline.config"; diff --git a/src/main/java/org/nrg/pipeline/utils/PipelineFileUtils.java b/src/main/java/org/nrg/pipeline/utils/PipelineFileUtils.java index 4165fa6172bfbf241f497af456af81c1eb0b972b..a6c647efe8b82f6e5278b90acb3445435dcab12c 100644 --- a/src/main/java/org/nrg/pipeline/utils/PipelineFileUtils.java +++ b/src/main/java/org/nrg/pipeline/utils/PipelineFileUtils.java @@ -92,7 +92,7 @@ public class PipelineFileUtils { public static String getBuildDir(String project, boolean postfixTimestamp) { ArcProject arcProject = ArcSpecManager.GetFreshInstance().getProjectArc(project); - String buildPath = XDAT.getSiteConfigPreferences().getAdminEmail() ; + String buildPath = XDAT.getSiteConfigPreferences().getBuildPath() ; if (arcProject != null) { buildPath = arcProject.getPaths().getBuildpath(); } diff --git a/src/main/java/org/nrg/xnat/archive/DicomZipImporter.java b/src/main/java/org/nrg/xnat/archive/DicomZipImporter.java index b2267f4daba7e4e24acb41108842c813381d0758..32621fb5337f54cee03b31e606a5fcad64925dbb 100644 --- a/src/main/java/org/nrg/xnat/archive/DicomZipImporter.java +++ b/src/main/java/org/nrg/xnat/archive/DicomZipImporter.java @@ -32,7 +32,7 @@ import org.nrg.xnat.restlet.util.FileWriterWrapperI; import com.google.common.collect.Lists; import com.google.common.collect.Sets; -@ImporterHandler(handler = "DICOM-zip", allowCallsWithoutFiles = false) +@ImporterHandler(handler = ImporterHandlerA.DICOM_ZIP_IMPORTER) public final class DicomZipImporter extends ImporterHandlerA { private final InputStream in; private final Object listenerControl; diff --git a/src/main/java/org/nrg/xnat/archive/GradualDicomImporter.java b/src/main/java/org/nrg/xnat/archive/GradualDicomImporter.java index ffa912bfcf1ec1ec0c0670886e663a05ef1d9f26..236379e3a87f060a03d53f7e6fed6754044ec497 100644 --- a/src/main/java/org/nrg/xnat/archive/GradualDicomImporter.java +++ b/src/main/java/org/nrg/xnat/archive/GradualDicomImporter.java @@ -64,7 +64,7 @@ import java.util.concurrent.Callable; @SuppressWarnings("ThrowFromFinallyBlock") @Service -@ImporterHandler(handler = "DICOM-zip", allowCallsWithoutFiles = false) +@ImporterHandler(handler = ImporterHandlerA.GRADUAL_DICOM_IMPORTER) public class GradualDicomImporter extends ImporterHandlerA { public static final String SENDER_AE_TITLE_PARAM = "Sender-AE-Title"; public static final String SENDER_ID_PARAM = "Sender-ID"; diff --git a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java index 540fee3185bed97d169f4659a442c05f070545c7..36d36973c68278dc3d2f6cf52aa4a8dc0613d6cc 100644 --- a/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java +++ b/src/main/java/org/nrg/xnat/configuration/SchedulerConfig.java @@ -1,8 +1,11 @@ package org.nrg.xnat.configuration; import org.nrg.config.exceptions.SiteConfigurationException; +import org.nrg.framework.exceptions.NrgServiceError; +import org.nrg.framework.exceptions.NrgServiceRuntimeException; import org.nrg.mail.services.EmailRequestLogService; import org.nrg.xdat.preferences.InitializerSiteConfiguration; +import org.nrg.xdat.preferences.SiteConfigPreferences; import org.nrg.xnat.helpers.prearchive.SessionXMLRebuilder; import org.nrg.xnat.security.DisableInactiveUsers; import org.nrg.xnat.security.ResetEmailRequests; @@ -24,6 +27,7 @@ import org.springframework.scheduling.support.CronTrigger; import org.springframework.scheduling.support.PeriodicTrigger; import javax.inject.Inject; +import java.sql.SQLException; import java.util.List; @Configuration @@ -31,7 +35,13 @@ import java.util.List; public class SchedulerConfig implements SchedulingConfigurer { @Bean public TriggerTask disableInactiveUsers() throws SiteConfigurationException { - return new TriggerTask(new DisableInactiveUsers(_preferences.getInactivityBeforeLockout()), new CronTrigger(_preferences.getInactivityBeforeLockoutSchedule())); + try { + final DisableInactiveUsers task = new DisableInactiveUsers(_preferences.getInactivityBeforeLockout(), (int) SiteConfigPreferences.convertPGIntervalToSeconds(_preferences.getMaxFailedLoginsLockoutDuration())); + return new TriggerTask(task, new CronTrigger(_preferences.getInactivityBeforeLockoutSchedule())); + } catch (SQLException e) { + // This isn't a real thing: PGInterval doesn't actually access the database. But just to make everyone happy... + throw new NrgServiceRuntimeException(NrgServiceError.Unknown, "This really shouldn't happen.", e); + } } @Bean diff --git a/src/main/java/org/nrg/xnat/initialization/PropertiesConfig.java b/src/main/java/org/nrg/xnat/initialization/PropertiesConfig.java index af431c4d4b811fcb4b303a706a3f8f99cb9ee664..c6a5fd961bfa9e6a510d9f983d9f2ec0b930eee2 100644 --- a/src/main/java/org/nrg/xnat/initialization/PropertiesConfig.java +++ b/src/main/java/org/nrg/xnat/initialization/PropertiesConfig.java @@ -160,7 +160,7 @@ public class PropertiesConfig { // Then cool, just return that. return candidate; } else { - // If it's a file, then the parent is probably a config folder, so if this is services.properties (the default) or the specific file identified by xnat.config... + // If it's a file, then the parent is probably a config folder, so if this is xnat-conf.properties (the default) or the specific file identified by xnat.config... if (file.getName().equals(XNAT_CONF_FILE) || StringUtils.equals(candidate.toString(), environment.getProperty(JAVA_XNAT_CONFIG))) { // So its parent is a config folder, QED. return candidate.getParent(); diff --git a/src/main/java/org/nrg/xnat/initialization/RootConfig.java b/src/main/java/org/nrg/xnat/initialization/RootConfig.java index 79de8d9aa243ea03af84f5dfb2cfe267a4cba09d..9ac4540a0f4318b3cfd13eeef944bbec57900d0a 100644 --- a/src/main/java/org/nrg/xnat/initialization/RootConfig.java +++ b/src/main/java/org/nrg/xnat/initialization/RootConfig.java @@ -20,7 +20,6 @@ import org.nrg.xnat.services.XnatAppInfo; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.context.annotation.ImportResource; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.http.converter.xml.MarshallingHttpMessageConverter; @@ -29,10 +28,8 @@ import org.springframework.oxm.jaxb.Jaxb2Marshaller; import javax.servlet.ServletContext; import javax.xml.bind.Marshaller; import java.io.IOException; -import java.io.InputStream; import java.util.HashMap; import java.util.Map; -import java.util.jar.Manifest; /** * Configuration for the XNAT root application context. This contains all of the basic infrastructure for initializing @@ -43,8 +40,7 @@ import java.util.jar.Manifest; * for standard XNAT components should be added in the {@link ApplicationConfig application configuration class}. */ @Configuration -@Import({PropertiesConfig.class, DatabaseConfig.class}) -@ImportResource("WEB-INF/conf/xnat-security.xml") +@Import({PropertiesConfig.class, DatabaseConfig.class, SecurityConfig.class}) public class RootConfig { @Bean public XnatAppInfo appInfo(final ServletContext context) throws IOException { diff --git a/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..771b24c2d94d61e046ae40d1c9f6c301c7c684aa --- /dev/null +++ b/src/main/java/org/nrg/xnat/initialization/SecurityConfig.java @@ -0,0 +1,225 @@ +package org.nrg.xnat.initialization; + +import com.fasterxml.jackson.core.type.TypeReference; +import org.nrg.config.exceptions.SiteConfigurationException; +import org.nrg.framework.services.SerializerService; +import org.nrg.xdat.preferences.InitializerSiteConfiguration; +import org.nrg.xnat.security.*; +import org.nrg.xnat.security.alias.AliasTokenAuthenticationProvider; +import org.nrg.xnat.security.config.AuthenticationProviderAggregator; +import org.nrg.xnat.security.config.AuthenticationProviderConfigurator; +import org.nrg.xnat.security.config.DatabaseAuthenticationProviderConfigurator; +import org.nrg.xnat.security.config.LdapAuthenticationProviderConfigurator; +import org.nrg.xnat.security.userdetailsservices.XnatDatabaseUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Lazy; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.security.access.AccessDecisionVoter; +import org.springframework.security.access.vote.AuthenticatedVoter; +import org.springframework.security.access.vote.RoleVoter; +import org.springframework.security.access.vote.UnanimousBased; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.access.channel.ChannelDecisionManagerImpl; +import org.springframework.security.web.access.channel.InsecureChannelProcessor; +import org.springframework.security.web.access.channel.SecureChannelProcessor; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.security.web.session.ConcurrentSessionFilter; + +import javax.sql.DataSource; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +@Configuration +@ImportResource("WEB-INF/conf/xnat-security.xml") +public class SecurityConfig { + @Bean + public UnanimousBased unanimousBased() { + final RoleVoter voter = new RoleVoter(); + voter.setRolePrefix("ROLE_"); + final List<AccessDecisionVoter<?>> voters = new ArrayList<>(); + voters.add(voter); + voters.add(new AuthenticatedVoter()); + return new UnanimousBased(voters); + } + + @Bean + public OnXnatLogin logUserLogin() { + return new OnXnatLogin(); + } + + @Bean + public AuthenticationFailureHandler authFailure() { + return new XnatUrlAuthenticationFailureHandler("/app/template/Login.vm?failed=true", "/app/template/PostRegister.vm"); + } + + @Bean + public XnatAuthenticationEntryPoint loginUrlAuthenticationEntryPoint() { + final XnatAuthenticationEntryPoint entryPoint = new XnatAuthenticationEntryPoint("/app/template/Login.vm"); + entryPoint.setDataPaths(Arrays.asList("/data/**", "/REST/**", "/fs/**")); + entryPoint.setInteractiveAgents(Arrays.asList(".*MSIE.*", ".*Mozilla.*", ".*AppleWebKit.*", ".*Opera.*")); + return entryPoint; + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + @Bean + public ConcurrentSessionFilter concurrencyFilter(final SessionRegistry sessionRegistry) { + return new ConcurrentSessionFilter(sessionRegistry, "/app/template/Login.vm"); + } + + @Bean + public ConcurrentSessionControlAuthenticationStrategy sas(final SessionRegistry sessionRegistry) throws SiteConfigurationException { + final ConcurrentSessionControlAuthenticationStrategy strategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry); + strategy.setMaximumSessions(_configuration.getConcurrentMaxSessions()); + strategy.setExceptionIfMaximumExceeded(true); + return strategy; + } + + @Bean + public LogoutFilter logoutFilter() { + final XnatLogoutSuccessHandler logoutSuccessHandler = new XnatLogoutSuccessHandler(); + logoutSuccessHandler.setOpenXnatLogoutSuccessUrl("/"); + logoutSuccessHandler.setSecuredXnatLogoutSuccessUrl("/app/template/Login.vm"); + final SecurityContextLogoutHandler securityContextLogoutHandler = new SecurityContextLogoutHandler(); + securityContextLogoutHandler.setInvalidateHttpSession(true); + final XnatLogoutHandler xnatLogoutHandler = new XnatLogoutHandler(); + final LogoutFilter filter = new LogoutFilter(logoutSuccessHandler, securityContextLogoutHandler, xnatLogoutHandler); + filter.setFilterProcessesUrl("/app/action/LogoutUser"); + return filter; + } + + @Bean + public FilterSecurityInterceptorBeanPostProcessor filterSecurityInterceptorBeanPostProcessor() throws IOException { + final Resource resource = RESOURCE_LOADER.getResource("classpath:META-INF/xnat/security/configured-urls.yaml"); + try (final InputStream inputStream = resource.getInputStream()) { + final HashMap<String, ArrayList<String>> urlMap = _serializer.deserializeYaml(inputStream, TYPE_REFERENCE); + final FilterSecurityInterceptorBeanPostProcessor postProcessor = new FilterSecurityInterceptorBeanPostProcessor(); + postProcessor.setOpenUrls(urlMap.get("openUrls")); + postProcessor.setAdminUrls(urlMap.get("adminUrls")); + return postProcessor; + } + } + + @Bean + public TranslatingChannelProcessingFilter channelProcessingFilter() throws SiteConfigurationException { + final ChannelDecisionManagerImpl decisionManager = new ChannelDecisionManagerImpl(); + decisionManager.setChannelProcessors(Arrays.asList(new SecureChannelProcessor(), new InsecureChannelProcessor())); + final TranslatingChannelProcessingFilter filter = new TranslatingChannelProcessingFilter(); + filter.setChannelDecisionManager(decisionManager); + filter.setRequiredChannel(_configuration.getSecurityChannel()); + return filter; + } + + @Bean + public AliasTokenAuthenticationProvider aliasTokenAuthenticationProvider() { + return new AliasTokenAuthenticationProvider(); + } + + @Bean + public DatabaseAuthenticationProviderConfigurator dbConfigurator() { + return new DatabaseAuthenticationProviderConfigurator(); + } + + @Bean + public LdapAuthenticationProviderConfigurator ldapConfigurator() { + return new LdapAuthenticationProviderConfigurator(); + } + + @Bean + public AuthenticationProviderAggregator providerAggregator(final List<AuthenticationProvider> providers, final List<AuthenticationProviderConfigurator> configurators) { + final Map<String, AuthenticationProviderConfigurator> configuratorMap = new HashMap<>(); + for (final AuthenticationProviderConfigurator configurator : configurators) { + configuratorMap.put(configurator.getConfiguratorId(), configurator); + } + + return new AuthenticationProviderAggregator(providers, configuratorMap); + } + + @Bean(name = {"org.springframework.security.authenticationManager", "customAuthenticationManager"}) + public XnatProviderManager customAuthenticationManager(final AuthenticationProviderAggregator aggregator) { + return new XnatProviderManager(aggregator); + } + + @Bean + public XnatAuthenticationFilter customAuthenticationFilter(final XnatProviderManager providerManager, + final AuthenticationSuccessHandler successHandler, + final AuthenticationFailureHandler failureHandler, + final SessionAuthenticationStrategy sas) { + final XnatAuthenticationFilter filter = new XnatAuthenticationFilter(); + filter.setAuthenticationManager(providerManager); + filter.setAuthenticationSuccessHandler(successHandler); + filter.setAuthenticationFailureHandler(failureHandler); + filter.setSessionAuthenticationStrategy(sas); + return filter; + } + + @Bean + public XnatBasicAuthenticationFilter customBasicAuthenticationFilter(final XnatProviderManager providerManager, + final AuthenticationEntryPoint entryPoint, + final SessionAuthenticationStrategy sas) { + final XnatBasicAuthenticationFilter filter = new XnatBasicAuthenticationFilter(providerManager, entryPoint); + filter.setSessionAuthenticationStrategy(sas); + return filter; + } + + @Bean + public XnatExpiredPasswordFilter expiredPasswordFilter() { + final XnatExpiredPasswordFilter filter = new XnatExpiredPasswordFilter(); + filter.setChangePasswordPath("/app/template/XDATScreen_UpdateUser.vm"); + filter.setChangePasswordDestination("/app/action/ModifyPassword"); + filter.setLogoutDestination("/app/action/LogoutUser"); + filter.setLoginPath("/app/template/Login.vm"); + filter.setLoginDestination("/app/action/XDATLoginUser"); + filter.setInactiveAccountPath("/app/template/InactiveAccount.vm"); + filter.setInactiveAccountDestination("/app/action/XnatInactiveAccount"); + filter.setEmailVerificationPath("/app/template/VerifyEmail.vm"); + filter.setEmailVerificationDestination("/data/services/sendEmailVerification"); + return filter; + } + + @Bean + public XnatInitCheckFilter xnatInitCheckFilter() { + final XnatInitCheckFilter filter = new XnatInitCheckFilter(); + filter.setInitializationPaths(Arrays.asList("/xapi/siteConfig/batch", "/xapi/notifications/smtp")); + filter.setConfigurationPath("/setup"); + filter.setNonAdminErrorPath("/app/template/Unconfigured.vm"); + filter.setExemptedPaths(Arrays.asList("/app/template/XDATScreen_UpdateUser.vm", "/app/action/ModifyPassword", "/app/template/Login.vm", "/style/app.css", "/login")); + return filter; + } + + @Bean + public XnatDatabaseUserDetailsService customDatabaseService(final DataSource dataSource) { + final XnatDatabaseUserDetailsService service = new XnatDatabaseUserDetailsService(); + service.setDataSource(dataSource); + return service; + } + + private static final ResourceLoader RESOURCE_LOADER = new DefaultResourceLoader(); + private static final TypeReference<HashMap<String, ArrayList<String>>> TYPE_REFERENCE = new TypeReference<HashMap<String, ArrayList<String>>>() { + }; + + @Autowired + @Lazy + private InitializerSiteConfiguration _configuration; + + @Autowired + @Lazy + private SerializerService _serializer; +} \ No newline at end of file diff --git a/src/main/java/org/nrg/xnat/restlet/actions/SessionImporter.java b/src/main/java/org/nrg/xnat/restlet/actions/SessionImporter.java index 415d80201bd8795fe7a4097fb37a2190ab7e4128..adb4a73788ae5e46376e7060b936c8ff39cb9e54 100644 --- a/src/main/java/org/nrg/xnat/restlet/actions/SessionImporter.java +++ b/src/main/java/org/nrg/xnat/restlet/actions/SessionImporter.java @@ -46,7 +46,7 @@ import org.nrg.xnat.turbine.utils.XNATSessionPopulater; import org.restlet.data.Status; import org.xml.sax.SAXException; -@ImporterHandler(handler = "SI", allowCallsWithoutFiles = false) +@ImporterHandler(handler = ImporterHandlerA.SESSION_IMPORTER) public class SessionImporter extends ImporterHandlerA implements Callable<List<String>> { static Logger logger = Logger.getLogger(SessionImporter.class); diff --git a/src/main/java/org/nrg/xnat/restlet/actions/XarImporter.java b/src/main/java/org/nrg/xnat/restlet/actions/XarImporter.java index 33cc7c759f0c7e948e836a012a3bdc71e067edbc..dde32a013556040efae1496a8a442435767beadf 100644 --- a/src/main/java/org/nrg/xnat/restlet/actions/XarImporter.java +++ b/src/main/java/org/nrg/xnat/restlet/actions/XarImporter.java @@ -52,12 +52,12 @@ import org.nrg.xnat.turbine.utils.ArcSpecManager; import org.nrg.xnat.utils.WorkflowUtils; import org.xml.sax.SAXException; -@ImporterHandler(handler = "XAR", allowCallsWithoutFiles = false) +@ImporterHandler(handler = ImporterHandlerA.XAR_IMPORTER) public class XarImporter extends ImporterHandlerA implements Callable<List<String>> { - static final String[] zipExtensions={".zip",".jar",".rar",".ear",".gar",".xar"}; + private static final String[] zipExtensions={".zip",".jar",".rar",".ear",".gar",".xar"}; - static Logger logger = Logger.getLogger(XarImporter.class); + private static final Logger logger = Logger.getLogger(XarImporter.class); private final FileWriterWrapperI fw; private final UserI user; @@ -66,19 +66,19 @@ public class XarImporter extends ImporterHandlerA implements Callable<List<Strin /** * - * @param listenerControl - * @param u - * @param session - * @param overwrite: 'append' means overwrite, but preserve un-modified content (don't delete anything) - * 'delete' means delete the pre-existing content. - * @param additionalValues: should include project, subject_ID and label (if session is null) + * @param listenerControl The listener. + * @param u The user. + * @param fw The file writer wrapper. + * @param params Parameters to the operation, including overwrite, 'append' means overwrite, but + * preserve un-modified content (don't delete anything), 'delete' means delete the + * pre-existing content. Should include project, subject_ID and label if session is null. */ public XarImporter(final Object listenerControl, final UserI u, final FileWriterWrapperI fw, final Map<String,Object> params) { super(listenerControl, u, fw, params); this.user=u; this.fw=fw; this.params=params; - this.urlList=new ArrayList<String>(); + this.urlList= new ArrayList<>(); } //@SuppressWarnings("serial") diff --git a/src/main/java/org/nrg/xnat/restlet/actions/importer/ImporterHandlerA.java b/src/main/java/org/nrg/xnat/restlet/actions/importer/ImporterHandlerA.java index e84358d0321293205465d50341d80025182e6a35..5b3237b4f84d2cff377995d75f5e71c2d0a5e4ac 100644 --- a/src/main/java/org/nrg/xnat/restlet/actions/importer/ImporterHandlerA.java +++ b/src/main/java/org/nrg/xnat/restlet/actions/importer/ImporterHandlerA.java @@ -47,18 +47,18 @@ public abstract class ImporterHandlerA extends StatusProducer implements Callab public abstract List<String> call() throws ClientException, ServerException; - static Logger logger = Logger.getLogger(ImporterHandlerA.class); + private static final Logger logger = Logger.getLogger(ImporterHandlerA.class); public static final String IMPORT_HANDLER_ATTR = "import-handler"; - public static String SESSION_IMPORTER="SI"; - public static String XAR_IMPORTER="XAR"; - public static String GRADUAL_DICOM_IMPORTER="gradual-DICOM"; - public static String DICOM_ZIP_IMPORTER="DICOM-zip"; - public static String BLANK_PREARCHIVE_ENTRY="blank"; + public static final String SESSION_IMPORTER="SI"; + public static final String XAR_IMPORTER="XAR"; + public static final String GRADUAL_DICOM_IMPORTER="gradual-DICOM"; + public static final String DICOM_ZIP_IMPORTER="DICOM-zip"; + public static final String BLANK_PREARCHIVE_ENTRY="blank"; static String DEFAULT_HANDLER=SESSION_IMPORTER; - final static Map<String,Class<? extends ImporterHandlerA>> IMPORTERS=new HashMap<String,Class<? extends ImporterHandlerA>>(); + final static Map<String,Class<? extends ImporterHandlerA>> IMPORTERS= new HashMap<>(); private static final String PROP_OBJECT_IDENTIFIER = "org.nrg.import.handler.impl"; private static final String IMPORTER_PROPERTIES = "importer.properties"; diff --git a/src/main/java/org/nrg/xnat/security/DisableInactiveUsers.java b/src/main/java/org/nrg/xnat/security/DisableInactiveUsers.java index 33132ad86f2c5f3b645913075df9b277794f4dda..d3fa49b87e03b5ee355ae3688453d139619c1189 100644 --- a/src/main/java/org/nrg/xnat/security/DisableInactiveUsers.java +++ b/src/main/java/org/nrg/xnat/security/DisableInactiveUsers.java @@ -18,7 +18,6 @@ import org.nrg.xft.XFTTable; import org.nrg.xft.event.EventUtils; import org.nrg.xft.exception.DBPoolException; import org.nrg.xft.security.UserI; -import org.nrg.xft.utils.AuthUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,8 +28,9 @@ import java.util.GregorianCalendar; public class DisableInactiveUsers implements Runnable { - public DisableInactiveUsers(final int inactivityBeforeLockout) { + public DisableInactiveUsers(final int inactivityBeforeLockout, final int lockoutDuration) { _inactivityBeforeLockout = inactivityBeforeLockout; + _lockoutDuration = lockoutDuration; } /** @@ -62,7 +62,7 @@ public class DisableInactiveUsers implements Runnable { u.setVerified("0"); Users.save(u, u, false, EventUtils.newEventInstance(EventUtils.CATEGORY.SIDE_ADMIN, EventUtils.TYPE.PROCESS, "Disabled due to inactivity")); - String expiration = TurbineUtils.getDateTimeFormatter().format(DateUtils.addMilliseconds(GregorianCalendar.getInstance().getTime(), -(AuthUtils.LOCKOUT_DURATION))); + String expiration = TurbineUtils.getDateTimeFormatter().format(DateUtils.addMilliseconds(GregorianCalendar.getInstance().getTime(), _lockoutDuration)); System.out.println("Locked out " + u.getLogin() + " user account until " + expiration); AdminUtils.sendAdminEmail(u.getLogin() + " account disabled due to inactivity.", "User " + u.getLogin() + " has been automatically disabled due to inactivity."); } @@ -98,6 +98,7 @@ public class DisableInactiveUsers implements Runnable { private static final Logger logger = LoggerFactory.getLogger(DisableInactiveUsers.class); - private int _inactivityBeforeLockout; + private final int _inactivityBeforeLockout; + private final int _lockoutDuration; } diff --git a/src/main/java/org/nrg/xnat/security/XnatAuthenticationFilter.java b/src/main/java/org/nrg/xnat/security/XnatAuthenticationFilter.java index dfb1c279d4fcf688fb55e839c0a243016625f35c..dad3fd4f943d0b9ccfcc879b888f5c84b250a0e6 100644 --- a/src/main/java/org/nrg/xnat/security/XnatAuthenticationFilter.java +++ b/src/main/java/org/nrg/xnat/security/XnatAuthenticationFilter.java @@ -27,7 +27,6 @@ import org.springframework.security.crypto.codec.Base64; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.inject.Inject; -import javax.inject.Named; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.UnsupportedEncodingException; @@ -135,6 +134,5 @@ public class XnatAuthenticationFilter extends UsernamePasswordAuthenticationFilt private static final Map<String, Integer> checked = Maps.newHashMap(); @Inject - @Named("customAuthenticationManager") private XnatProviderManager _providerManager; } diff --git a/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java b/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java index 2dbbd4764751b1176cadf75154d5e6b34ad0ef07..94b242ea479f8ad9c92d820505d045cef51c6366 100644 --- a/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java +++ b/src/main/java/org/nrg/xnat/security/XnatExpiredPasswordFilter.java @@ -1,340 +1,364 @@ -/* - * org.nrg.xnat.security.XnatExpiredPasswordFilter - * XNAT http://www.xnat.org - * Copyright (c) 2014, Washington University School of Medicine - * All Rights Reserved - * - * Released under the Simplified BSD. - * - * Last modified 12/11/13 3:33 PM - */ -package org.nrg.xnat.security; - -import org.apache.commons.lang3.StringUtils; -import org.nrg.config.exceptions.SiteConfigurationException; -import org.nrg.xdat.XDAT; -import org.nrg.xdat.entities.AliasToken; -import org.nrg.xdat.entities.UserRole; -import org.nrg.xdat.om.ArcArchivespecification; -import org.nrg.xdat.preferences.InitializerSiteConfiguration; -import org.nrg.xdat.security.helpers.Roles; -import org.nrg.xdat.services.AliasTokenService; -import org.nrg.xdat.services.XdatUserAuthService; -import org.nrg.xdat.turbine.utils.TurbineUtils; -import org.nrg.xft.security.UserI; -import org.nrg.xnat.turbine.utils.ArcSpecManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.security.crypto.codec.Base64; -import org.springframework.web.filter.GenericFilterBean; - -import javax.inject.Inject; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.sql.DataSource; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; - -@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) -public class XnatExpiredPasswordFilter extends GenericFilterBean { - private String changePasswordPath = ""; - private String changePasswordDestination = ""; - private String logoutDestination = ""; - private String loginPath = ""; - private String loginDestination = ""; - private String inactiveAccountPath; - private String inactiveAccountDestination; - private String emailVerificationDestination; - private String emailVerificationPath; - private boolean passwordExpirationDirtied = true; - private boolean passwordExpirationDisabled; - private boolean passwordExpirationInterval; - private String passwordExpirationSetting; - - @Override - public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { - final HttpServletRequest request = (HttpServletRequest) req; - final HttpServletResponse response = (HttpServletResponse) res; - UserI user = XDAT.getUserDetails(); - Object passwordExpired = request.getSession().getAttribute("expired"); - // MIGRATION: Need to remove arcspec. - ArcArchivespecification _arcSpec = ArcSpecManager.GetInstance(); - final String referer = request.getHeader("Referer"); - if (request.getSession() != null && request.getSession().getAttribute("forcePasswordChange") != null && (Boolean) request.getSession().getAttribute("forcePasswordChange")) { - try { - String refererPath = null; - String uri = new URI(request.getRequestURI()).getPath(); - if (!StringUtils.isBlank(referer)) { - refererPath = new URI(referer).getPath(); - } - if (uri.endsWith(changePasswordPath) || uri.endsWith(changePasswordDestination) || uri.endsWith(logoutDestination) || uri.endsWith(loginPath) || uri.endsWith(loginDestination)) { - //If you're already on the change password page, continue on without redirect. - chain.doFilter(req, res); - } else if (!StringUtils.isBlank(refererPath) && (changePasswordPath.equals(refererPath) || changePasswordDestination.equals(refererPath) || logoutDestination.equals(refererPath))) { - //If you're on a request within the change password page, continue on without redirect. - chain.doFilter(req, res); - } else { - response.sendRedirect(TurbineUtils.GetFullServerPath() + changePasswordPath); - } - } catch (URISyntaxException ignored) { - // - } - } else if (passwordExpired != null && !(Boolean) passwordExpired) { - //If the date of password change was checked earlier in the session and found to be not expired, do not send them to the expired password page. - chain.doFilter(request, response); - } else if (_arcSpec == null || !_arcSpec.isComplete()) { - //If the arc spec has not yet been set, have the user configure the arc spec before changing their password. This prevents a negative interaction with the arc spec filter. - chain.doFilter(request, response); - } else { - if (user == null) { - //If the user is not logged in, do not send them to the expired password page. - - String header = request.getHeader("Authorization"); - if (header != null && header.startsWith("Basic ")) { - //For users that authenticated using basic authentication, check whether their password is expired, and if so give them a 403 and a message that they need to change their password. - - String token = new String(Base64.decode(header.substring(6).getBytes("UTF-8")), "UTF-8"); - String username = ""; - int delim = token.indexOf(":"); - if (delim != -1) { - username = token.substring(0, delim); - } - if (AliasToken.isAliasFormat(username)) { - AliasToken alias = _aliasTokenService.locateToken(username); - if (alias == null) { - response.sendError(HttpServletResponse.SC_FORBIDDEN, "Your security token has expired. Please try again after updating your session."); - return; - } - username = alias.getXdatUserId(); - } - // Check whether the user is connected to an active role for non_expiring. - try { - List<Integer> roles = (new JdbcTemplate(_dataSource)).query("SELECT COUNT(*) FROM xhbm_user_role where username = ? and role = ? and enabled = 't'", new String[]{username, UserRole.ROLE_NON_EXPIRING}, new RowMapper<Integer>() { - public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { - return rs.getInt(1); - } - }); - if (roles.get(0) > 0) { - chain.doFilter(request, response); - } - } catch (Exception e) { - logger.error(e); - } - - try { - passwordExpirationDisabled = isPasswordExpirationDisabled(); - } catch (SiteConfigurationException e) { - logger.error("An error occurred trying to check for expired passwords, continuing processing.", e); - } - if (passwordExpirationDisabled) { - chain.doFilter(request, response); - } else { - final boolean isExpired = checkForExpiredPassword(username); - request.getSession().setAttribute("expired", isExpired); - if (username != null && isExpired && !username.equals("guest")) { - response.sendError(HttpServletResponse.SC_FORBIDDEN, "Your password has expired. Please try again after changing your password."); - } else { - chain.doFilter(request, response); - } - } - } else { - checkUserChangePassword(request, response); - //User is not authenticated through basic authentication either. - chain.doFilter(req, res); - } - } else { - String uri = request.getRequestURI(); - - if (user.isGuest()) { - //If you're a guest and you try to access the change password page, you get sent to the login page since there's no password on the guest account to change. - checkUserChangePassword(request, response); - } - if (user.isGuest() || - //If you're logging in or out, or going to the login page itself - (uri.endsWith(logoutDestination) || uri.endsWith(loginPath) || uri.endsWith(loginDestination)) || - //If you're already on the change password page, continue on without redirect. - (user.isEnabled() && (uri.endsWith(changePasswordPath) || uri.endsWith(changePasswordDestination))) || - //If you're already on the inactive account page or reactivating an account, continue on without redirect. - (!user.isEnabled() && (uri.endsWith(inactiveAccountPath) || uri.endsWith(inactiveAccountDestination) || - uri.endsWith(emailVerificationPath) || uri.endsWith(emailVerificationDestination) || - (referer != null && (referer.endsWith(inactiveAccountPath) || referer.endsWith(inactiveAccountDestination))))) || - //If you're on a request within the change password page, continue on without redirect. - (referer != null && (referer.endsWith(changePasswordPath) || referer.endsWith(changePasswordDestination) || - referer.endsWith(logoutDestination)))) { - chain.doFilter(req, res); - } else { - if ( - user.getAuthorization() != null && user.getAuthorization().getAuthMethod().equals(XdatUserAuthService.LDAP) - ) { - // Shouldn't check for a localdb expired password if user is coming in through LDAP - chain.doFilter(req, res); - } else if (user.isEnabled()) { - boolean isExpired = checkForExpiredPassword(user); - - if ((!isUserNonExpiring(user) && isExpired) || (_preferences.getRequireSaltedPasswords() && user.getSalt() == null)) { - request.getSession().setAttribute("expired", isExpired); - response.sendRedirect(TurbineUtils.GetFullServerPath() + changePasswordPath); - } else { - chain.doFilter(request, response); - } - } else { - response.sendRedirect(TurbineUtils.GetFullServerPath() + inactiveAccountPath); - } - } - } - } - } - - public void setChangePasswordPath(String path) { - this.changePasswordPath = path; - } - - public void setChangePasswordDestination(String path) { - this.changePasswordDestination = path; - } - - public void setLogoutDestination(String path) { - this.logoutDestination = path; - } - - public void setLoginPath(String path) { - this.loginPath = path; - } - - public void setLoginDestination(String loginDestination) { - this.loginDestination = loginDestination; - } - - public void setInactiveAccountPath(String inactiveAccountPath) { - this.inactiveAccountPath = inactiveAccountPath; - } - - public String getInactiveAccountPath() { - return inactiveAccountPath; - } - - public void setInactiveAccountDestination(String inactiveAccountDestination) { - this.inactiveAccountDestination = inactiveAccountDestination; - } - - public String getInactiveAccountDestination() { - return inactiveAccountDestination; - } - - public void setEmailVerificationDestination(String emailVerificationDestination) { - this.emailVerificationDestination = emailVerificationDestination; - } - - public String getEmailVerificationDestination() { - return emailVerificationDestination; - } - - public void setEmailVerificationPath(String emailVerificationPath) { - this.emailVerificationPath = emailVerificationPath; - } - - public String getEmailVerificationPath() { - return emailVerificationPath; - } - - public void setPasswordExpirationDirtied(final boolean passwordExpirationDirtied) { - this.passwordExpirationDirtied = passwordExpirationDirtied; - } - - private boolean checkForExpiredPassword(final UserI user) { - return checkForExpiredPassword(user.getUsername()); - } - - private boolean checkForExpiredPassword(final String username) { - try { - if (isPasswordExpirationDisabled()) { - return false; - } - if (isPasswordExpirationInterval()) { - List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT ((now()-password_updated)> (Interval '" + passwordExpirationSetting + " days')) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[]{username}, new RowMapper<Boolean>() { - public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { - return rs.getBoolean(1); - } - }); - return expired.get(0); - } else { - List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT (to_date('" + new SimpleDateFormat("MM/dd/yyyy").format(new Date(Long.parseLong(passwordExpirationSetting))) + "', 'MM/DD/YYYY') BETWEEN password_updated AND now()) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[]{username}, new RowMapper<Boolean>() { - public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { - return rs.getBoolean(1); - } - }); - return expired.get(0); - } - } catch (Throwable e) { // ldap authentication can throw an exception during these queriess - logger.error(e.getMessage(), e); - } - return false; - } - - private boolean isPasswordExpirationDisabled() throws SiteConfigurationException { - if (!passwordExpirationDirtied) { - return passwordExpirationDisabled; - } - final String type = _preferences.getPasswordExpirationType(); - if (StringUtils.isBlank(type)) { - passwordExpirationDisabled = true; - } else if (type.equals("Interval")) { - passwordExpirationInterval = true; - passwordExpirationSetting = Integer.toString(_preferences.getPasswordExpirationInterval()); - passwordExpirationDisabled = passwordExpirationSetting.equals("0"); - } else if (type.equals("Date")) { - passwordExpirationInterval = false; - passwordExpirationSetting = Long.toString(_preferences.getPasswordExpirationDate().getTime()); - passwordExpirationDisabled = passwordExpirationSetting.equals("0"); - } else { - passwordExpirationDisabled = true; - } - passwordExpirationDirtied = false; - return passwordExpirationDisabled; - } - - private boolean isPasswordExpirationInterval() { - return passwordExpirationInterval; - } - - private boolean isUserNonExpiring(UserI user) { - try { - return Roles.checkRole(user, UserRole.ROLE_NON_EXPIRING); - } catch (Exception e) { - return false; - } - } - - private void checkUserChangePassword(HttpServletRequest request, HttpServletResponse response) throws IOException { - try { - String uri = new URI(request.getRequestURI()).getPath(); - if (uri.endsWith("XDATScreen_UpdateUser.vm") && request.getParameterMap().isEmpty()) { - response.sendRedirect(TurbineUtils.GetFullServerPath() + "/app/template/Login.vm"); - } - } catch (URISyntaxException ignored) { - } - } - - @Autowired - @Lazy - private InitializerSiteConfiguration _preferences; - - @Autowired - @Lazy - private AliasTokenService _aliasTokenService; - - @Inject - private DataSource _dataSource; -} +/* + * org.nrg.xnat.security.XnatExpiredPasswordFilter + * XNAT http://www.xnat.org + * Copyright (c) 2014, Washington University School of Medicine + * All Rights Reserved + * + * Released under the Simplified BSD. + * + * Last modified 12/11/13 3:33 PM + */ +package org.nrg.xnat.security; + +import org.apache.commons.lang3.StringUtils; +import org.nrg.config.exceptions.SiteConfigurationException; +import org.nrg.framework.services.ContextService; +import org.nrg.xdat.XDAT; +import org.nrg.xdat.entities.AliasToken; +import org.nrg.xdat.entities.UserRole; +import org.nrg.xdat.om.ArcArchivespecification; +import org.nrg.xdat.preferences.InitializerSiteConfiguration; +import org.nrg.xdat.security.helpers.Roles; +import org.nrg.xdat.services.AliasTokenService; +import org.nrg.xdat.services.XdatUserAuthService; +import org.nrg.xdat.turbine.utils.TurbineUtils; +import org.nrg.xft.security.UserI; +import org.nrg.xnat.turbine.utils.ArcSpecManager; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Lazy; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.security.crypto.codec.Base64; +import org.springframework.web.filter.GenericFilterBean; + +import javax.inject.Inject; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.sql.DataSource; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +@SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) +public class XnatExpiredPasswordFilter extends GenericFilterBean implements ApplicationContextAware { + private String changePasswordPath = ""; + private String changePasswordDestination = ""; + private String logoutDestination = ""; + private String loginPath = ""; + private String loginDestination = ""; + private String inactiveAccountPath; + private String inactiveAccountDestination; + private String emailVerificationDestination; + private String emailVerificationPath; + private boolean passwordExpirationDirtied = true; + private boolean passwordExpirationDisabled; + private boolean passwordExpirationInterval; + private String passwordExpirationSetting; + + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { + final HttpServletRequest request = (HttpServletRequest) req; + final HttpServletResponse response = (HttpServletResponse) res; + UserI user = XDAT.getUserDetails(); + Object passwordExpired = request.getSession().getAttribute("expired"); + // MIGRATION: Need to remove arcspec. + ArcArchivespecification _arcSpec = ArcSpecManager.GetInstance(); + final String referer = request.getHeader("Referer"); + if (request.getSession() != null && request.getSession().getAttribute("forcePasswordChange") != null && (Boolean) request.getSession().getAttribute("forcePasswordChange")) { + try { + String refererPath = null; + String uri = new URI(request.getRequestURI()).getPath(); + if (!StringUtils.isBlank(referer)) { + refererPath = new URI(referer).getPath(); + } + if (uri.endsWith(changePasswordPath) || uri.endsWith(changePasswordDestination) || uri.endsWith(logoutDestination) || uri.endsWith(loginPath) || uri.endsWith(loginDestination)) { + //If you're already on the change password page, continue on without redirect. + chain.doFilter(req, res); + } else if (!StringUtils.isBlank(refererPath) && (changePasswordPath.equals(refererPath) || changePasswordDestination.equals(refererPath) || logoutDestination.equals(refererPath))) { + //If you're on a request within the change password page, continue on without redirect. + chain.doFilter(req, res); + } else { + response.sendRedirect(TurbineUtils.GetFullServerPath() + changePasswordPath); + } + } catch (URISyntaxException ignored) { + // + } + } else if (passwordExpired != null && !(Boolean) passwordExpired) { + //If the date of password change was checked earlier in the session and found to be not expired, do not send them to the expired password page. + chain.doFilter(request, response); + } else if (_arcSpec == null || !_arcSpec.isComplete()) { + //If the arc spec has not yet been set, have the user configure the arc spec before changing their password. This prevents a negative interaction with the arc spec filter. + chain.doFilter(request, response); + } else { + if (user == null) { + //If the user is not logged in, do not send them to the expired password page. + + String header = request.getHeader("Authorization"); + if (header != null && header.startsWith("Basic ")) { + //For users that authenticated using basic authentication, check whether their password is expired, and if so give them a 403 and a message that they need to change their password. + + String token = new String(Base64.decode(header.substring(6).getBytes("UTF-8")), "UTF-8"); + String username = ""; + int delim = token.indexOf(":"); + if (delim != -1) { + username = token.substring(0, delim); + } + if (AliasToken.isAliasFormat(username)) { + AliasToken alias = getAliasTokenService().locateToken(username); + if (alias == null) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Your security token has expired. Please try again after updating your session."); + return; + } + username = alias.getXdatUserId(); + } + // Check whether the user is connected to an active role for non_expiring. + try { + List<Integer> roles = (new JdbcTemplate(_dataSource)).query("SELECT COUNT(*) FROM xhbm_user_role where username = ? and role = ? and enabled = 't'", new String[] {username, UserRole.ROLE_NON_EXPIRING}, new RowMapper<Integer>() { + public Integer mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getInt(1); + } + }); + if (roles.get(0) > 0) { + chain.doFilter(request, response); + } + } catch (Exception e) { + logger.error(e); + } + + try { + passwordExpirationDisabled = isPasswordExpirationDisabled(); + } catch (SiteConfigurationException e) { + logger.error("An error occurred trying to check for expired passwords, continuing processing.", e); + } + if (passwordExpirationDisabled) { + chain.doFilter(request, response); + } else { + final boolean isExpired = checkForExpiredPassword(username); + request.getSession().setAttribute("expired", isExpired); + if (username != null && isExpired && !username.equals("guest")) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "Your password has expired. Please try again after changing your password."); + } else { + chain.doFilter(request, response); + } + } + } else { + checkUserChangePassword(request, response); + //User is not authenticated through basic authentication either. + chain.doFilter(req, res); + } + } else { + String uri = request.getRequestURI(); + + if (user.isGuest()) { + //If you're a guest and you try to access the change password page, you get sent to the login page since there's no password on the guest account to change. + checkUserChangePassword(request, response); + } + if (user.isGuest() || + //If you're logging in or out, or going to the login page itself + (uri.endsWith(logoutDestination) || uri.endsWith(loginPath) || uri.endsWith(loginDestination)) || + //If you're already on the change password page, continue on without redirect. + (user.isEnabled() && (uri.endsWith(changePasswordPath) || uri.endsWith(changePasswordDestination))) || + //If you're already on the inactive account page or reactivating an account, continue on without redirect. + (!user.isEnabled() && (uri.endsWith(inactiveAccountPath) || uri.endsWith(inactiveAccountDestination) || + uri.endsWith(emailVerificationPath) || uri.endsWith(emailVerificationDestination) || + (referer != null && (referer.endsWith(inactiveAccountPath) || referer.endsWith(inactiveAccountDestination))))) || + //If you're on a request within the change password page, continue on without redirect. + (referer != null && (referer.endsWith(changePasswordPath) || referer.endsWith(changePasswordDestination) || + referer.endsWith(logoutDestination)))) { + chain.doFilter(req, res); + } else { + if ( + user.getAuthorization() != null && user.getAuthorization().getAuthMethod().equals(XdatUserAuthService.LDAP) + ) { + // Shouldn't check for a localdb expired password if user is coming in through LDAP + chain.doFilter(req, res); + } else if (user.isEnabled()) { + boolean isExpired = checkForExpiredPassword(user); + + if ((!isUserNonExpiring(user) && isExpired) || (_preferences.getRequireSaltedPasswords() && user.getSalt() == null)) { + request.getSession().setAttribute("expired", isExpired); + response.sendRedirect(TurbineUtils.GetFullServerPath() + changePasswordPath); + } else { + chain.doFilter(request, response); + } + } else { + response.sendRedirect(TurbineUtils.GetFullServerPath() + inactiveAccountPath); + } + } + } + } + } + + public void setChangePasswordPath(String path) { + this.changePasswordPath = path; + } + + public void setChangePasswordDestination(String path) { + this.changePasswordDestination = path; + } + + public void setLogoutDestination(String path) { + this.logoutDestination = path; + } + + public void setLoginPath(String path) { + this.loginPath = path; + } + + public void setLoginDestination(String loginDestination) { + this.loginDestination = loginDestination; + } + + public void setInactiveAccountPath(String inactiveAccountPath) { + this.inactiveAccountPath = inactiveAccountPath; + } + + public String getInactiveAccountPath() { + return inactiveAccountPath; + } + + public void setInactiveAccountDestination(String inactiveAccountDestination) { + this.inactiveAccountDestination = inactiveAccountDestination; + } + + public String getInactiveAccountDestination() { + return inactiveAccountDestination; + } + + public void setEmailVerificationDestination(String emailVerificationDestination) { + this.emailVerificationDestination = emailVerificationDestination; + } + + public String getEmailVerificationDestination() { + return emailVerificationDestination; + } + + public void setEmailVerificationPath(String emailVerificationPath) { + this.emailVerificationPath = emailVerificationPath; + } + + public String getEmailVerificationPath() { + return emailVerificationPath; + } + + public void setPasswordExpirationDirtied(final boolean passwordExpirationDirtied) { + this.passwordExpirationDirtied = passwordExpirationDirtied; + } + + /** + * {@inheritDoc} + */ + @Override + public void setApplicationContext(final ApplicationContext context) throws BeansException { + _context = context; + } + + private boolean checkForExpiredPassword(final UserI user) { + return checkForExpiredPassword(user.getUsername()); + } + + private boolean checkForExpiredPassword(final String username) { + try { + if (isPasswordExpirationDisabled()) { + return false; + } + if (isPasswordExpirationInterval()) { + List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT ((now()-password_updated)> (Interval '" + passwordExpirationSetting + " days')) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[] {username}, new RowMapper<Boolean>() { + public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getBoolean(1); + } + }); + return expired.get(0); + } else { + List<Boolean> expired = (new JdbcTemplate(_dataSource)).query("SELECT (to_date('" + new SimpleDateFormat("MM/dd/yyyy").format(new Date(Long.parseLong(passwordExpirationSetting))) + "', 'MM/DD/YYYY') BETWEEN password_updated AND now()) AS expired FROM xhbm_xdat_user_auth WHERE auth_user = ? AND auth_method = 'localdb'", new String[] {username}, new RowMapper<Boolean>() { + public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getBoolean(1); + } + }); + return expired.get(0); + } + } catch (Throwable e) { // ldap authentication can throw an exception during these queriess + logger.error(e.getMessage(), e); + } + return false; + } + + private boolean isPasswordExpirationDisabled() throws SiteConfigurationException { + if (!passwordExpirationDirtied) { + return passwordExpirationDisabled; + } + final String type = _preferences.getPasswordExpirationType(); + if (StringUtils.isBlank(type)) { + passwordExpirationDisabled = true; + } else if (type.equals("Interval")) { + passwordExpirationInterval = true; + passwordExpirationSetting = Integer.toString(_preferences.getPasswordExpirationInterval()); + passwordExpirationDisabled = passwordExpirationSetting.equals("0"); + } else if (type.equals("Date")) { + passwordExpirationInterval = false; + passwordExpirationSetting = Long.toString(_preferences.getPasswordExpirationDate().getTime()); + passwordExpirationDisabled = passwordExpirationSetting.equals("0"); + } else { + passwordExpirationDisabled = true; + } + passwordExpirationDirtied = false; + return passwordExpirationDisabled; + } + + private boolean isPasswordExpirationInterval() { + return passwordExpirationInterval; + } + + private boolean isUserNonExpiring(UserI user) { + try { + return Roles.checkRole(user, UserRole.ROLE_NON_EXPIRING); + } catch (Exception e) { + return false; + } + } + + private void checkUserChangePassword(HttpServletRequest request, HttpServletResponse response) throws IOException { + try { + String uri = new URI(request.getRequestURI()).getPath(); + if (uri.endsWith("XDATScreen_UpdateUser.vm") && request.getParameterMap().isEmpty()) { + response.sendRedirect(TurbineUtils.GetFullServerPath() + "/app/template/Login.vm"); + } + } catch (URISyntaxException ignored) { + } + } + + // We have to do this because the filter must be created in the root context, but the token service must be created + // in the application context. We use the context service to cheat across these boundaries. + private AliasTokenService getAliasTokenService() { + if (_aliasTokenService == null) { + _aliasTokenService = _contextService.getBean(AliasTokenService.class); + } + return _aliasTokenService; + } + + @Autowired + @Lazy + private InitializerSiteConfiguration _preferences; + + @Autowired + @Lazy + private ContextService _contextService; + + @Inject + private DataSource _dataSource; + + private ApplicationContext _context; + private AliasTokenService _aliasTokenService; +} diff --git a/src/main/java/org/nrg/xnat/security/XnatProviderManager.java b/src/main/java/org/nrg/xnat/security/XnatProviderManager.java index 6e1e149f311b4cbbbeb9f1497d978b26eefb99ab..a762ed38c82c8f42c952299f7e83012e7b0a073c 100644 --- a/src/main/java/org/nrg/xnat/security/XnatProviderManager.java +++ b/src/main/java/org/nrg/xnat/security/XnatProviderManager.java @@ -1,429 +1,408 @@ -/* - * org.nrg.xnat.security.XnatProviderManager - * XNAT http://www.xnat.org - * Copyright (c) 2014, Washington University School of Medicine - * All Rights Reserved - * - * Released under the Simplified BSD. - * - * Last modified 12/13/13 2:19 PM - */ -package org.nrg.xnat.security; - -import com.google.common.collect.Maps; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.time.DateUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.velocity.VelocityContext; -import org.hibernate.exception.DataException; -import org.nrg.framework.services.ContextService; -import org.nrg.xdat.XDAT; -import org.nrg.xdat.entities.AliasToken; -import org.nrg.xdat.entities.UserAuthI; -import org.nrg.xdat.entities.XdatUserAuth; -import org.nrg.xdat.security.helpers.Users; -import org.nrg.xdat.services.XdatUserAuthService; -import org.nrg.xdat.turbine.utils.AdminUtils; -import org.nrg.xdat.turbine.utils.TurbineUtils; -import org.nrg.xft.security.UserI; -import org.nrg.xft.utils.AuthUtils; -import org.nrg.xnat.security.alias.AliasTokenAuthenticationProvider; -import org.nrg.xnat.security.alias.AliasTokenAuthenticationToken; -import org.nrg.xnat.security.provider.XnatAuthenticationProvider; -import org.nrg.xnat.security.provider.XnatDatabaseAuthenticationProvider; -import org.nrg.xnat.security.provider.XnatLdapAuthenticationProvider; -import org.nrg.xnat.security.tokens.XnatDatabaseUsernamePasswordAuthenticationToken; -import org.nrg.xnat.security.tokens.XnatLdapUsernamePasswordAuthenticationToken; -import org.nrg.xnat.security.userdetailsservices.XnatDatabaseUserDetailsService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.support.MessageSourceAccessor; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.security.authentication.*; -import org.springframework.security.core.*; -import org.springframework.security.core.authority.SimpleGrantedAuthority; - -import javax.inject.Inject; -import javax.sql.DataSource; -import java.io.IOException; -import java.net.URL; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.*; - -public class XnatProviderManager extends ProviderManager { - - public XnatProviderManager(final List<AuthenticationProvider> providers, final Properties properties) { - - super(providers); - - if (properties == null) { - throw new IllegalArgumentException("The list of authentication providers cannot be set to null."); - } - - _properties = properties; - - if(!StringUtils.isBlank(properties.getProperty(SECURITY_MAX_FAILED_LOGINS_PROPERTY))) { - AuthUtils.MAX_FAILED_LOGIN_ATTEMPTS=Integer.valueOf(properties.getProperty(SECURITY_MAX_FAILED_LOGINS_PROPERTY)); - } - - if(!StringUtils.isBlank(properties.getProperty(SECURITY_MAX_FAILED_LOGINS_LOCKOUT_DURATION_PROPERTY))) { - AuthUtils.LOCKOUT_DURATION=Integer.valueOf(properties.getProperty(SECURITY_MAX_FAILED_LOGINS_LOCKOUT_DURATION_PROPERTY)); - if(AuthUtils.LOCKOUT_DURATION>0)AuthUtils.LOCKOUT_DURATION=-(AuthUtils.LOCKOUT_DURATION); //LOCKOUT must be negative for date comparison to work - } - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Class<? extends Authentication> toTest = authentication.getClass(); - AuthenticationException lastException = null; - Authentication result = null; - List<AuthenticationProvider> providers = new ArrayList<>(); - - // HACK: This is a hack to work around open XNAT auth issue. If this is a bare un/pw auth token, use anon auth. - if (authentication.getClass() == UsernamePasswordAuthenticationToken.class && authentication.getName().equalsIgnoreCase("guest")) { - providers.add(getAnonymousAuthenticationProvider()); - authentication = new AnonymousAuthenticationToken(getAnonymousAuthenticationProvider().getKey(), authentication.getPrincipal(), Collections.<GrantedAuthority>singletonList(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))); - } else { - for (AuthenticationProvider candidate : getProviders()) { - if (!candidate.supports(toTest)) { - continue; - } - if(authentication instanceof XnatLdapUsernamePasswordAuthenticationToken){ - if (!(candidate instanceof XnatLdapAuthenticationProvider)) { - continue; - } - XnatLdapAuthenticationProvider ldapCandidate = (XnatLdapAuthenticationProvider) candidate; - if (!((XnatLdapUsernamePasswordAuthenticationToken) authentication).getProviderId().equalsIgnoreCase(ldapCandidate.getProviderId())) { - //This is a different LDAP provider than the one that was selected. - continue; - } - } - try { - if (((XnatDatabaseAuthenticationProvider)candidate).isPlainText()) { - String username = authentication.getPrincipal().toString(); - Boolean encrypted = new JdbcTemplate(_dataSource).query("SELECT primary_password_encrypt<>0 OR (primary_password_encrypt IS NULL AND CHAR_LENGTH(primary_password)=64) FROM xdat_user WHERE login=? LIMIT 1", new String[] {username}, new RowMapper<Boolean>() { - public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { - return rs.getBoolean(1); - } - }).get(0); - - if (encrypted) continue; - } - } catch (Exception e) {/*casting exceptions can be safely ignored*/} - providers.add(candidate); - } - } - - assert providers.size() > 0: "No provider found for authentication of type " + authentication.getClass().getSimpleName(); - - for (AuthenticationProvider provider : providers) { - _log.debug("Authentication attempt using " + provider.getClass().getName()); - - try { - result = provider.authenticate(authentication); - if (result != null) { - if (_log.isDebugEnabled()) { - _log.debug("Found a provider that worked for " + authentication.getName() + ": " + provider.getClass().getSimpleName()); - } - - copyDetails(authentication, result); - break; - } - } catch (AccountStatusException exception) { - _log.warn("Error occurred authenticating login request", exception); - lastException = exception; - } catch(NewLdapAccountNotAutoEnabledException e) { - try { - AdminUtils.sendNewUserNotification(e.getUserDetails(), "", "", "", new VelocityContext()); - } catch (Exception exception) { - _log.error("Error occurred sending new user request email", exception); - } - lastException = e; - - } catch (AuthenticationException e) { - lastException = e; - } - } - - if (result != null) { - boolean eraseCredentialsAfterAuthentication = false; - if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { - // Authentication is complete. Remove credentials and other secret data from authentication - ((CredentialsContainer)result).eraseCredentials(); - } - - eventPublisher.publishAuthenticationSuccess(authentication); - - return result; - - } else { - // Parent was null, or didn't authenticate (or throw an exception). - if (lastException == null) { - lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound", - new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}")); - } - - eventPublisher.publishAuthenticationFailure(lastException, authentication); - throw lastException; - } - } - - private AnonymousAuthenticationProvider getAnonymousAuthenticationProvider() { - return _contextService.getBean(AnonymousAuthenticationProvider.class); - } - - public void setProperties(List<String> fileNames) { - _properties = new Properties(); - for (String filename : fileNames) { - String path = "../../../../../../" + filename; - URL url = getClass().getResource(path); - if (url != null) { - try { - _properties.load(url.openStream()); - } catch (IOException e) { - _log.error(e); - } - } - } - } - - public XdatUserAuth getUserByAuth(Authentication authentication) { - if(authentication==null){ - return null; - } - - final String u; - if(authentication.getPrincipal() instanceof String){ - u=(String)authentication.getPrincipal(); - }else{ - u=((UserI)authentication.getPrincipal()).getLogin(); - } - final String method; - final String provider; - if(authentication instanceof XnatLdapUsernamePasswordAuthenticationToken){ - provider=((XnatLdapUsernamePasswordAuthenticationToken)authentication).getProviderId(); - method=XdatUserAuthService.LDAP; - }else{ - provider=XnatDatabaseUserDetailsService.DB_PROVIDER; - method=XdatUserAuthService.LOCALDB; - } - - try { - return getUserAuthService().getUserByNameAndAuth(u, method, provider); - } catch (DataException exception) { - _log.error("An error occurred trying to retrieve the auth method", exception); - throw new RuntimeException("An error occurred trying to validate the given information. Please check your username and password. If this problem persists, please contact your system administrator."); - } - } - - public UsernamePasswordAuthenticationToken buildUPTokenForAuthMethod(String authMethod, String username, String password){ - XnatAuthenticationProvider chosenProvider = findAuthenticationProviderByAuthMethod(authMethod); - return buildUPToken(chosenProvider, username, password); - } - - public UsernamePasswordAuthenticationToken buildUPTokenForProviderName(String providerName, String username, String password){ - XnatAuthenticationProvider chosenProvider = findAuthenticationProviderByProviderName(providerName); - return buildUPToken(chosenProvider, username, password); - } - - public String retrieveAuthMethod(final String username) { - String auth = cached_methods.get(username); - if (auth == null) { - try { - List<XdatUserAuth> userAuths = getUserAuthService().getUsersByName(username); - if (userAuths.size() == 1) { - auth = userAuths.get(0).getAuthMethod(); - cached_methods.put(username.intern(), auth.intern()); - // The list may contain localdb auth method even when password is empty and LDAP authentication is used (MRH) - } else if (userAuths.size() > 1) { - for (UserAuthI userAuth : userAuths) { - auth = userAuth.getAuthMethod(); - cached_methods.put(username.intern(), auth.intern()); - if (!userAuth.getAuthMethod().equalsIgnoreCase(XdatUserAuthService.LOCALDB)) { - break; - } - } - } else if (AliasToken.isAliasFormat(username)) { - auth = XdatUserAuthService.TOKEN; - cached_methods.put(username.intern(), auth.intern()); - } - } catch (DataException exception) { - _log.error("An error occurred trying to retrieve the auth method", exception); - throw new RuntimeException("An error occurred trying to validate the given information. Please check your username and password. If this problem persists, please contact your system administrator."); - } - } - return auth; - } - - private XdatUserAuthService getUserAuthService() { - if (_userAuthService == null) { - _userAuthService = _contextService.getBean(XdatUserAuthService.class); - } - return _userAuthService; - } - - private static UsernamePasswordAuthenticationToken buildUPToken(XnatAuthenticationProvider provider, String username, String password){ - if (provider instanceof XnatLdapAuthenticationProvider) { - return new XnatLdapUsernamePasswordAuthenticationToken(username, password, provider.getProviderId()); - } else if (provider instanceof AliasTokenAuthenticationProvider) { - return new AliasTokenAuthenticationToken(username, Long.parseLong(password)); - } else { - return new XnatDatabaseUsernamePasswordAuthenticationToken(username, password); - } - } - - private XnatAuthenticationProvider findAuthenticationProviderByAuthMethod(final String authMethod){ - return findAuthenticationProvider(new XnatAuthenticationProviderMatcher() { - @Override - public boolean matches(XnatAuthenticationProvider provider) { - return provider.getAuthMethod().equalsIgnoreCase(authMethod); - } - }); - } - - private XnatAuthenticationProvider findAuthenticationProviderByProviderName(final String providerName){ - return findAuthenticationProvider(new XnatAuthenticationProviderMatcher() { - @Override - public boolean matches(XnatAuthenticationProvider provider) { - return provider.getName().equalsIgnoreCase(providerName); - } - }); - } - - private XnatAuthenticationProvider findAuthenticationProvider(XnatAuthenticationProviderMatcher matcher){ - List<AuthenticationProvider> prov = getProviders(); - for(AuthenticationProvider ap : prov){ - XnatAuthenticationProvider xap = (XnatAuthenticationProvider) ap; - if(matcher.matches(xap)){ - return xap; - } - } - return null; - } - - private void copyDetails(Authentication source, Authentication destination) { - if ((destination instanceof AbstractAuthenticationToken) && (destination.getDetails() == null)) { - AbstractAuthenticationToken token = (AbstractAuthenticationToken) destination; - - token.setDetails(source.getDetails()); -} - } - - private static final class AuthenticationAttemptEventPublisher implements AuthenticationEventPublisher { - - private final FailedAttemptsManager failedAttemptsManager; - private final LastSuccessfulLoginManager lastSuccessfulLoginManager; - - private AuthenticationAttemptEventPublisher(final XnatProviderManager manager) { - failedAttemptsManager = new FailedAttemptsManager(manager); - lastSuccessfulLoginManager = new LastSuccessfulLoginManager(manager); - } - - public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { - //increment failed login attempt - failedAttemptsManager.addFailedLoginAttempt(authentication); - } - - public void publishAuthenticationSuccess(Authentication authentication) { - failedAttemptsManager.clearCount(authentication); - lastSuccessfulLoginManager.updateLastSuccessfulLogin(authentication); - } - } - - private static final class LastSuccessfulLoginManager { - private final XnatProviderManager _manager; - - public LastSuccessfulLoginManager(final XnatProviderManager manager) { - _manager = manager; - } - - private void updateLastSuccessfulLogin(Authentication auth) { - XdatUserAuth ua = _manager.getUserByAuth(auth); - if (ua != null) { - Date now = java.util.Calendar.getInstance(TimeZone.getDefault()).getTime(); - ua.setLastSuccessfulLogin(now); - ua.setLastLoginAttempt(now); - XDAT.getXdatUserAuthService().update(ua); - } - } - } - - private static final class FailedAttemptsManager { - private final XnatProviderManager _manager; - - public FailedAttemptsManager(final XnatProviderManager manager) { - _manager = manager; - } - - /** - * Increments failed Login count - * - * @param auth The authentication that failed. - */ - private synchronized void addFailedLoginAttempt(final Authentication auth) { - XdatUserAuth ua = _manager.getUserByAuth(auth); - if (ua != null) { - if (AuthUtils.MAX_FAILED_LOGIN_ATTEMPTS > 0) { - ua.setFailedLoginAttempts(ua.getFailedLoginAttempts() + 1); - ua.setLastLoginAttempt(new Date()); - XDAT.getXdatUserAuthService().update(ua); - } - - if (StringUtils.isNotEmpty(ua.getXdatUsername())) { - Integer uid = Users.getUserid(ua.getXdatUsername()); - if (uid != null) { - try { - if (ua.getFailedLoginAttempts().equals(AuthUtils.MAX_FAILED_LOGIN_ATTEMPTS)) { - String expiration = TurbineUtils.getDateTimeFormatter().format(DateUtils.addMilliseconds(GregorianCalendar.getInstance().getTime(), -(AuthUtils.LOCKOUT_DURATION))); - System.out.println("Locked out " + ua.getXdatUsername() + " user account until " + expiration); - AdminUtils.sendAdminEmail(ua.getXdatUsername() + " account temporarily disabled.", "User " + ua.getXdatUsername() + " has been temporarily disabled due to excessive failed login attempts. The user's account will be automatically enabled at " + expiration + "."); - } - } catch (Exception e) { - //ignore - } - } - } - } - } - - public void clearCount(final Authentication auth) { - if (AuthUtils.MAX_FAILED_LOGIN_ATTEMPTS > 0) { - XdatUserAuth ua = _manager.getUserByAuth(auth); - if (ua != null) { - ua.setFailedLoginAttempts(0); - XDAT.getXdatUserAuthService().update(ua); - } - } - } - } - - private interface XnatAuthenticationProviderMatcher { - boolean matches(XnatAuthenticationProvider provider); - } - - private static final String SECURITY_MAX_FAILED_LOGINS_LOCKOUT_DURATION_PROPERTY = "security.max_failed_logins_lockout_duration"; - private static final String SECURITY_MAX_FAILED_LOGINS_PROPERTY = "security.max_failed_logins"; - - private static final Log _log = LogFactory.getLog(XnatProviderManager.class); - - private static Map<String,String> cached_methods= Maps.newConcurrentMap();//this will prevent 20,000 curl scripts from hitting the db everytime - - protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); - - @Inject - private DataSource _dataSource; - - @Autowired - @Qualifier("rootContextService") - @Lazy - private ContextService _contextService; - - private XdatUserAuthService _userAuthService; - - private final AuthenticationEventPublisher eventPublisher = new AuthenticationAttemptEventPublisher(this); - private Properties _properties; -} +/* + * org.nrg.xnat.security.XnatProviderManager + * XNAT http://www.xnat.org + * Copyright (c) 2014, Washington University School of Medicine + * All Rights Reserved + * + * Released under the Simplified BSD. + * + * Last modified 12/13/13 2:19 PM + */ +package org.nrg.xnat.security; + +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.velocity.VelocityContext; +import org.hibernate.exception.DataException; +import org.nrg.config.exceptions.SiteConfigurationException; +import org.nrg.framework.services.ContextService; +import org.nrg.xdat.XDAT; +import org.nrg.xdat.entities.AliasToken; +import org.nrg.xdat.entities.UserAuthI; +import org.nrg.xdat.entities.XdatUserAuth; +import org.nrg.xdat.preferences.InitializerSiteConfiguration; +import org.nrg.xdat.preferences.SiteConfigPreferences; +import org.nrg.xdat.security.helpers.Users; +import org.nrg.xdat.services.XdatUserAuthService; +import org.nrg.xdat.turbine.utils.AdminUtils; +import org.nrg.xdat.turbine.utils.TurbineUtils; +import org.nrg.xft.security.UserI; +import org.nrg.xnat.security.alias.AliasTokenAuthenticationProvider; +import org.nrg.xnat.security.alias.AliasTokenAuthenticationToken; +import org.nrg.xnat.security.provider.XnatAuthenticationProvider; +import org.nrg.xnat.security.provider.XnatDatabaseAuthenticationProvider; +import org.nrg.xnat.security.provider.XnatLdapAuthenticationProvider; +import org.nrg.xnat.security.tokens.XnatDatabaseUsernamePasswordAuthenticationToken; +import org.nrg.xnat.security.tokens.XnatLdapUsernamePasswordAuthenticationToken; +import org.nrg.xnat.security.userdetailsservices.XnatDatabaseUserDetailsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.support.MessageSourceAccessor; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.security.authentication.*; +import org.springframework.security.core.*; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +import javax.inject.Inject; +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; + +public class XnatProviderManager extends ProviderManager { + public XnatProviderManager(final List<AuthenticationProvider> providers) { + super(providers); + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Class<? extends Authentication> toTest = authentication.getClass(); + AuthenticationException lastException = null; + Authentication result = null; + List<AuthenticationProvider> providers = new ArrayList<>(); + + // HACK: This is a hack to work around open XNAT auth issue. If this is a bare un/pw auth token, use anon auth. + if (authentication.getClass() == UsernamePasswordAuthenticationToken.class && authentication.getName().equalsIgnoreCase("guest")) { + providers.add(getAnonymousAuthenticationProvider()); + authentication = new AnonymousAuthenticationToken(getAnonymousAuthenticationProvider().getKey(), authentication.getPrincipal(), Collections.<GrantedAuthority> singletonList(new SimpleGrantedAuthority("ROLE_ANONYMOUS"))); + } else { + for (AuthenticationProvider candidate : getProviders()) { + if (!candidate.supports(toTest)) { + continue; + } + if (authentication instanceof XnatLdapUsernamePasswordAuthenticationToken) { + if (!(candidate instanceof XnatLdapAuthenticationProvider)) { + continue; + } + XnatLdapAuthenticationProvider ldapCandidate = (XnatLdapAuthenticationProvider) candidate; + if (!((XnatLdapUsernamePasswordAuthenticationToken) authentication).getProviderId().equalsIgnoreCase(ldapCandidate.getProviderId())) { + //This is a different LDAP provider than the one that was selected. + continue; + } + } + try { + if (((XnatDatabaseAuthenticationProvider) candidate).isPlainText()) { + String username = authentication.getPrincipal().toString(); + @SuppressWarnings({"SqlDialectInspection", "SqlNoDataSourceInspection"}) + final Boolean encrypted = new JdbcTemplate(_dataSource).query("SELECT primary_password_encrypt<>0 OR (primary_password_encrypt IS NULL AND CHAR_LENGTH(primary_password)=64) FROM xdat_user WHERE login=? LIMIT 1", new String[] {username}, new RowMapper<Boolean>() { + public Boolean mapRow(ResultSet rs, int rowNum) throws SQLException { + return rs.getBoolean(1); + } + }).get(0); + + if (encrypted) { + continue; + } + } + } catch (Exception e) {/*casting exceptions can be safely ignored*/} + providers.add(candidate); + } + } + + assert providers.size() > 0 : "No provider found for authentication of type " + authentication.getClass().getSimpleName(); + + for (AuthenticationProvider provider : providers) { + _log.debug("Authentication attempt using " + provider.getClass().getName()); + + try { + result = provider.authenticate(authentication); + if (result != null) { + if (_log.isDebugEnabled()) { + _log.debug("Found a provider that worked for " + authentication.getName() + ": " + provider.getClass().getSimpleName()); + } + + copyDetails(authentication, result); + break; + } + } catch (AccountStatusException exception) { + _log.warn("Error occurred authenticating login request", exception); + lastException = exception; + } catch (NewLdapAccountNotAutoEnabledException e) { + try { + AdminUtils.sendNewUserNotification(e.getUserDetails(), "", "", "", new VelocityContext()); + } catch (Exception exception) { + _log.error("Error occurred sending new user request email", exception); + } + lastException = e; + + } catch (AuthenticationException e) { + lastException = e; + } + } + + if (result != null) { + boolean eraseCredentialsAfterAuthentication = false; + if (eraseCredentialsAfterAuthentication && (result instanceof CredentialsContainer)) { + // Authentication is complete. Remove credentials and other secret data from authentication + ((CredentialsContainer) result).eraseCredentials(); + } + + eventPublisher.publishAuthenticationSuccess(authentication); + + return result; + + } else { + // Parent was null, or didn't authenticate (or throw an exception). + if (lastException == null) { + lastException = new ProviderNotFoundException(messages.getMessage("ProviderManager.providerNotFound", + new Object[] {toTest.getName()}, "No AuthenticationProvider found for {0}")); + } + + eventPublisher.publishAuthenticationFailure(lastException, authentication); + throw lastException; + } + } + + private AnonymousAuthenticationProvider getAnonymousAuthenticationProvider() { + return _contextService.getBean(AnonymousAuthenticationProvider.class); + } + + public XdatUserAuth getUserByAuth(Authentication authentication) { + if (authentication == null) { + return null; + } + + final String u; + if (authentication.getPrincipal() instanceof String) { + u = (String) authentication.getPrincipal(); + } else { + u = ((UserI) authentication.getPrincipal()).getLogin(); + } + final String method; + final String provider; + if (authentication instanceof XnatLdapUsernamePasswordAuthenticationToken) { + provider = ((XnatLdapUsernamePasswordAuthenticationToken) authentication).getProviderId(); + method = XdatUserAuthService.LDAP; + } else { + provider = XnatDatabaseUserDetailsService.DB_PROVIDER; + method = XdatUserAuthService.LOCALDB; + } + + try { + return getUserAuthService().getUserByNameAndAuth(u, method, provider); + } catch (DataException exception) { + _log.error("An error occurred trying to retrieve the auth method", exception); + throw new RuntimeException("An error occurred trying to validate the given information. Please check your username and password. If this problem persists, please contact your system administrator."); + } + } + + public UsernamePasswordAuthenticationToken buildUPTokenForAuthMethod(String authMethod, String username, String password) { + XnatAuthenticationProvider chosenProvider = findAuthenticationProviderByAuthMethod(authMethod); + return buildUPToken(chosenProvider, username, password); + } + + public UsernamePasswordAuthenticationToken buildUPTokenForProviderName(String providerName, String username, String password) { + XnatAuthenticationProvider chosenProvider = findAuthenticationProviderByProviderName(providerName); + return buildUPToken(chosenProvider, username, password); + } + + public String retrieveAuthMethod(final String username) { + String auth = cached_methods.get(username); + if (auth == null) { + try { + List<XdatUserAuth> userAuths = getUserAuthService().getUsersByName(username); + if (userAuths.size() == 1) { + auth = userAuths.get(0).getAuthMethod(); + cached_methods.put(username.intern(), auth.intern()); + // The list may contain localdb auth method even when password is empty and LDAP authentication is used (MRH) + } else if (userAuths.size() > 1) { + for (UserAuthI userAuth : userAuths) { + auth = userAuth.getAuthMethod(); + cached_methods.put(username.intern(), auth.intern()); + if (!userAuth.getAuthMethod().equalsIgnoreCase(XdatUserAuthService.LOCALDB)) { + break; + } + } + } else if (AliasToken.isAliasFormat(username)) { + auth = XdatUserAuthService.TOKEN; + cached_methods.put(username.intern(), auth.intern()); + } + } catch (DataException exception) { + _log.error("An error occurred trying to retrieve the auth method", exception); + throw new RuntimeException("An error occurred trying to validate the given information. Please check your username and password. If this problem persists, please contact your system administrator."); + } + } + return auth; + } + + private XdatUserAuthService getUserAuthService() { + if (_userAuthService == null) { + _userAuthService = _contextService.getBean(XdatUserAuthService.class); + } + return _userAuthService; + } + + private static UsernamePasswordAuthenticationToken buildUPToken(XnatAuthenticationProvider provider, String username, String password) { + if (provider instanceof XnatLdapAuthenticationProvider) { + return new XnatLdapUsernamePasswordAuthenticationToken(username, password, provider.getProviderId()); + } else if (provider instanceof AliasTokenAuthenticationProvider) { + return new AliasTokenAuthenticationToken(username, password); + } else { + return new XnatDatabaseUsernamePasswordAuthenticationToken(username, password); + } + } + + private XnatAuthenticationProvider findAuthenticationProviderByAuthMethod(final String authMethod) { + return findAuthenticationProvider(new XnatAuthenticationProviderMatcher() { + @Override + public boolean matches(XnatAuthenticationProvider provider) { + return provider.getAuthMethod().equalsIgnoreCase(authMethod); + } + }); + } + + private XnatAuthenticationProvider findAuthenticationProviderByProviderName(final String providerName) { + return findAuthenticationProvider(new XnatAuthenticationProviderMatcher() { + @Override + public boolean matches(XnatAuthenticationProvider provider) { + return provider.getName().equalsIgnoreCase(providerName); + } + }); + } + + private XnatAuthenticationProvider findAuthenticationProvider(XnatAuthenticationProviderMatcher matcher) { + List<AuthenticationProvider> prov = getProviders(); + for (AuthenticationProvider ap : prov) { + XnatAuthenticationProvider xap = (XnatAuthenticationProvider) ap; + if (matcher.matches(xap)) { + return xap; + } + } + return null; + } + + private void copyDetails(Authentication source, Authentication destination) { + if ((destination instanceof AbstractAuthenticationToken) && (destination.getDetails() == null)) { + AbstractAuthenticationToken token = (AbstractAuthenticationToken) destination; + + token.setDetails(source.getDetails()); + } + } + + private static final class AuthenticationAttemptEventPublisher implements AuthenticationEventPublisher { + + private final FailedAttemptsManager failedAttemptsManager; + private final LastSuccessfulLoginManager lastSuccessfulLoginManager; + + private AuthenticationAttemptEventPublisher(final XnatProviderManager manager) { + failedAttemptsManager = new FailedAttemptsManager(manager); + lastSuccessfulLoginManager = new LastSuccessfulLoginManager(manager); + } + + public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { + //increment failed login attempt + try { + failedAttemptsManager.addFailedLoginAttempt(authentication); + } catch (SiteConfigurationException e) { + _log.error("An error occurred accessing the site configuration", e); + } + } + + public void publishAuthenticationSuccess(Authentication authentication) { + try { + failedAttemptsManager.clearCount(authentication); + lastSuccessfulLoginManager.updateLastSuccessfulLogin(authentication); + } catch (SiteConfigurationException e) { + _log.error("An error occurred accessing the site configuration", e); + } + } + } + + private static final class LastSuccessfulLoginManager { + private final XnatProviderManager _manager; + + public LastSuccessfulLoginManager(final XnatProviderManager manager) { + _manager = manager; + } + + private void updateLastSuccessfulLogin(Authentication auth) { + XdatUserAuth ua = _manager.getUserByAuth(auth); + if (ua != null) { + Date now = java.util.Calendar.getInstance(TimeZone.getDefault()).getTime(); + ua.setLastSuccessfulLogin(now); + ua.setLastLoginAttempt(now); + XDAT.getXdatUserAuthService().update(ua); + } + } + } + + private static final class FailedAttemptsManager { + private final XnatProviderManager _manager; + + public FailedAttemptsManager(final XnatProviderManager manager) { + _manager = manager; + } + + /** + * Increments failed Login count + * + * @param auth The authentication that failed. + */ + private synchronized void addFailedLoginAttempt(final Authentication auth) throws SiteConfigurationException { + XdatUserAuth ua = _manager.getUserByAuth(auth); + if (ua != null) { + if (_manager._preferences.getMaxFailedLogins() > 0) { + ua.setFailedLoginAttempts(ua.getFailedLoginAttempts() + 1); + ua.setLastLoginAttempt(new Date()); + XDAT.getXdatUserAuthService().update(ua); + } + + if (StringUtils.isNotEmpty(ua.getXdatUsername())) { + Integer uid = Users.getUserid(ua.getXdatUsername()); + if (uid != null) { + try { + if (ua.getFailedLoginAttempts().equals(_manager._preferences.getMaxFailedLogins())) { + String expiration = TurbineUtils.getDateTimeFormatter().format(DateUtils.addMilliseconds(GregorianCalendar.getInstance().getTime(), (int) SiteConfigPreferences.convertPGIntervalToSeconds(_manager._preferences.getMaxFailedLoginsLockoutDuration()))); + _log.info("Locked out " + ua.getXdatUsername() + " user account until " + expiration); + AdminUtils.sendAdminEmail(ua.getXdatUsername() + " account temporarily disabled.", "User " + ua.getXdatUsername() + " has been temporarily disabled due to excessive failed login attempts. The user's account will be automatically enabled at " + expiration + "."); + } + } catch (Exception e) { + //ignore + } + } + } + } + } + + public void clearCount(final Authentication auth) throws SiteConfigurationException { + if (_manager._preferences.getMaxFailedLogins() > 0) { + XdatUserAuth ua = _manager.getUserByAuth(auth); + if (ua != null) { + ua.setFailedLoginAttempts(0); + XDAT.getXdatUserAuthService().update(ua); + } + } + } + } + + private interface XnatAuthenticationProviderMatcher { + boolean matches(XnatAuthenticationProvider provider); + } + + private static final Log _log = LogFactory.getLog(XnatProviderManager.class); + + private static Map<String, String> cached_methods = Maps.newConcurrentMap();//this will prevent 20,000 curl scripts from hitting the db everytime + + protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor(); + + @Inject + private DataSource _dataSource; + + @Autowired + @Qualifier("rootContextService") + @Lazy + private ContextService _contextService; + + @Autowired + @Lazy + private InitializerSiteConfiguration _preferences; + + private XdatUserAuthService _userAuthService; + + private final AuthenticationEventPublisher eventPublisher = new AuthenticationAttemptEventPublisher(this); +} diff --git a/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java b/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java index 39672b61f57e608ea37b3198b06e87c7af0f9e5e..e8a9a051dfac89496088429d4da8aeab0f8a7550 100644 --- a/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java +++ b/src/main/java/org/nrg/xnat/security/alias/AliasTokenAuthenticationProvider.java @@ -33,6 +33,14 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException; public class AliasTokenAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider implements XnatAuthenticationProvider { + public AliasTokenAuthenticationProvider() { + this("token"); + } + + public AliasTokenAuthenticationProvider(final String name) { + _name = name; + } + /** * Performs authentication with the same contract as {@link * org.springframework.security.authentication.AuthenticationManager#authenticate(org.springframework.security.core.Authentication)}. @@ -94,10 +102,6 @@ public class AliasTokenAuthenticationProvider extends AbstractUserDetailsAuthent return StringUtils.isBlank(_name) ? getClass().toString() : _name; } - public void setName(final String name) { - _name = name; - } - @Override public String getProviderId() { return _providerId; @@ -202,6 +206,6 @@ public class AliasTokenAuthenticationProvider extends AbstractUserDetailsAuthent private XdatUserAuthService _userAuthService; - private String _name; + private final String _name; private String _providerId; } diff --git a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java index abda2cd2e0878d5d94960bdecd5a4b6dae111b2a..6990368e825463d43f11210b3534c5b69b015cc4 100644 --- a/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java +++ b/src/main/java/org/nrg/xnat/security/config/AuthenticationProviderAggregator.java @@ -4,80 +4,48 @@ import org.apache.commons.lang3.StringUtils; import org.nrg.framework.utilities.BasicXnatResourceLocator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.core.io.Resource; import java.util.*; public class AuthenticationProviderAggregator extends ArrayList<AuthenticationProvider> { - public AuthenticationProviderAggregator(List<AuthenticationProvider> standaloneProviders, Map<String, AuthenticationProviderConfigurator> configurators, Properties properties) { - if (properties == null) { - throw new IllegalArgumentException("The list of authentication providers cannot be set to null."); - } - -// String commaDelineatedProviders = properties.getProperty("provider.providers.enabled"); -// assert !StringUtils.isBlank(commaDelineatedProviders) : "You must specify at least one authentication provider configuration."; -// ArrayList<String> providerArray=new ArrayList<String>(Arrays.asList(commaDelineatedProviders.split("[\\s,]+"))); -// HashMap<String, HashMap<String, String>> providerMap = new HashMap<>(); -// for(String prov : providerArray){ -// providerMap.put(prov, new HashMap<String, String>()); -// } - - ArrayList<String> providerArray=new ArrayList<String>(); - String dbName = "Database"; - String dbId = "localdb"; - String dbType = "db"; + public AuthenticationProviderAggregator(List<AuthenticationProvider> standaloneProviders, Map<String, AuthenticationProviderConfigurator> configurators) { + ArrayList<String> providerArray = new ArrayList<>(); + String dbName = "Database"; + String dbId = "localdb"; + String dbType = "db"; providerArray.add(dbType); HashMap<String, HashMap<String, String>> providerMap = new HashMap<>(); providerMap.put(dbType, new HashMap<String, String>()); - providerMap.get(dbType).put("name",dbName); - providerMap.get(dbType).put("id",dbId); - providerMap.get(dbType).put("type",dbType); + providerMap.get(dbType).put("name", dbName); + providerMap.get(dbType).put("id", dbId); + providerMap.get(dbType).put("type", dbType); // Populate map of properties try { - String filnameEnd = "-provider.properties"; - final List<Resource> resources = BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/auth/**/*" + filnameEnd); + String filenameEnd = "-provider.properties"; + final List<Resource> resources = BasicXnatResourceLocator.getResources("classpath*:META-INF/xnat/auth/**/*" + filenameEnd); for (final Resource resource : resources) { String filename = resource.getFilename(); - String id = filename.substring(0, (filename.length() - filnameEnd.length())); + String id = filename.substring(0, (filename.length() - filenameEnd.length())); providerMap.put(id, new HashMap<String, String>()); providerArray.add(id); final Properties provider = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<Object, Object> providerProperty : provider.entrySet()) { providerMap.get(id).put(providerProperty.getKey().toString(), providerProperty.getValue().toString()); } - } - }catch(Exception e){ - _log.error("",e); + } catch (Exception e) { + _log.error("", e); } -// -// for(Map.Entry<Object, Object> entry : properties.entrySet()) { -// String key = (String) entry.getKey(); -// StringTokenizer st = new StringTokenizer(key, "."); -// String provider = st.nextToken(); -// if (provider.equals("provider")) { -// String name = st.nextToken(); -// if(providerMap.containsKey(name)) { -// StringBuilder providerProperty = new StringBuilder(); -// while (st.hasMoreTokens()) { -// if (providerProperty.length() > 0) { -// providerProperty.append("."); -// } -// providerProperty.append(st.nextToken()); -// } -// providerMap.get(name).put(providerProperty.toString(), (String) entry.getValue()); -// } -// } -// } // Create providers - for(String prov: providerArray){ + for (String prov : providerArray) { String name = providerMap.get(prov).get("name"); - String id = providerMap.get(prov).get("id"); + String id = providerMap.get(prov).get("id"); String type = providerMap.get(prov).get("type"); assert !StringUtils.isBlank(name) : "You must provide a name for all authentication provider configurations"; diff --git a/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java b/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java index 532d085649bb3fba094c28cf9ac72a7c779ae191..0857caa31dba50c47e834af3cc7d217a7c3ab4d1 100644 --- a/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java +++ b/src/main/java/org/nrg/xnat/security/config/DatabaseAuthenticationProviderConfigurator.java @@ -24,6 +24,10 @@ import java.util.List; import java.util.Map; public class DatabaseAuthenticationProviderConfigurator extends AbstractAuthenticationProviderConfigurator { + public DatabaseAuthenticationProviderConfigurator() { + setConfiguratorId("db"); + } + @Override public List<AuthenticationProvider> getAuthenticationProviders(String id, String name) { List<AuthenticationProvider> providers = new ArrayList<>(); diff --git a/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java b/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java index b0e535b1de65b3993de6782497ecf6687c5ab890..a23300a67337716d01073f5c1c0cc419cc5799ab 100644 --- a/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java +++ b/src/main/java/org/nrg/xnat/security/config/LdapAuthenticationProviderConfigurator.java @@ -30,6 +30,10 @@ import java.util.List; import java.util.Map; public class LdapAuthenticationProviderConfigurator extends AbstractAuthenticationProviderConfigurator { + public LdapAuthenticationProviderConfigurator() { + setConfiguratorId("ldap"); + } + @Override public List<AuthenticationProvider> getAuthenticationProviders(String id, String name) { throw new NotImplementedException("You must provide LDAP properties in order to configure an LDAP connection."); diff --git a/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java b/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java index 17c17ad8b2c50559b95059beefd1d7256f1dfbd8..77aa42932f5e5539c8a07d2e791d2513b2cbcfe3 100644 --- a/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java +++ b/src/main/java/org/nrg/xnat/turbine/utils/ArcSpecManager.java @@ -239,7 +239,8 @@ public class ArcSpecManager { arcSpec.setEnableNewRegistrations(preferences.getUserRegistration()); arcSpec.setRequireLogin(preferences.getRequireLogin()); - if (StringUtils.isNotBlank(preferences.getAdminEmail())) { + + if (StringUtils.isNotBlank(preferences.getPipelinePath())) { arcSpec.setProperty("globalPaths/pipelinePath", preferences.getPipelinePath()); } diff --git a/src/main/resources/META-INF/xnat/security/configured-urls.yaml b/src/main/resources/META-INF/xnat/security/configured-urls.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b968bd4b3b05964b88e7aaa0a12b41bb34f49786 --- /dev/null +++ b/src/main/resources/META-INF/xnat/security/configured-urls.yaml @@ -0,0 +1,51 @@ +openUrls: + - /app/action/AcceptProjectAccess/par/* + - /app/action/ModifyPassword* + - /app/action/XDATForgotLogin* + - /app/action/XDATRegisterUser* + - /app/action/InactiveAccountEmail* + - /app/template/XDATScreen_UpdateUser.vm* + - /app/template/Error.vm* + - /app/template/ForgotLogin.vm* + - /app/template/Login.vm* + - /app/template/PostRegister.vm* + - /app/template/InactiveAccount.vm* + - /app/template/Register.vm* + - /app/template/ResendEmail.vm* + - /app/template/ResendVerification.vm* + - /app/template/VerificationSent.vm* + - /app/template/VerifyEmail.vm* + - /favicon.ico + - /data/JSESSION + - /REST/JSESSION + - /data/services/auth* + - /REST/services/auth* + - /data/services/sendemailverification* + - /REST/services/sendemailverification* + - /images/** + - /scripts/** + - /style/** + - /themes/** + - /files/** + - /pages/** + - /page/** + - /applet/** + +adminUrls: + - /app/template/AdminSummary.vm* + - /app/template/XDATScreen_EditScript.vm/user/Test* + - /app/template/XDATScreen_active_sessions.vm* + - /app/template/XDATScreen_admin.vm* + - /app/template/XDATScreen_admin_options.vm* + - /app/template/XDATScreen_admin_projectAccess.vm* + - /app/template/XDATScreen_bundles.vm* + - /app/template/XDATScreen_dataTypes.vm* + - /app/template/XDATScreen_email.vm* + - /app/template/XDATScreen_emailSpecifications.vm* + - /app/template/XDATScreen_groups.vm* + - /app/template/XDATScreen_manage_info.vm* + - /app/template/XDATScreen_manage_news.vm* + - /app/template/XDATScreen_manage_pipeline.vm* + - /app/template/XDATScreen_roles.vm* + - /monitoring* + - /setup/**