diff --git a/src/main/java/de/thm/arsnova/config/SecurityConfig.java b/src/main/java/de/thm/arsnova/config/SecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..cda4bacf0b0421ed9e025cab4648681548dd604e --- /dev/null +++ b/src/main/java/de/thm/arsnova/config/SecurityConfig.java @@ -0,0 +1,426 @@ +package de.thm.arsnova.config; + +import java.util.ArrayList; +import java.util.List; + +import javax.servlet.ServletContext; + +import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; +import org.scribe.up.provider.impl.FacebookProvider; +import org.scribe.up.provider.impl.Google2Provider; +import org.scribe.up.provider.impl.Google2Provider.Google2Scope; +import org.scribe.up.provider.impl.TwitterProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.security.access.PermissionEvaluator; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; +import org.springframework.security.cas.web.CasAuthenticationFilter; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.authentication.BindAuthenticator; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.authentication.LdapAuthenticator; +import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; +import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.web.context.ServletContextAware; + +import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider; +import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint; +import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter; + +import de.thm.arsnova.CASLogoutSuccessHandler; +import de.thm.arsnova.CasUserDetailsService; +import de.thm.arsnova.LoginAuthenticationFailureHandler; +import de.thm.arsnova.LoginAuthenticationSucessHandler; +import de.thm.arsnova.security.ApplicationPermissionEvaluator; +import de.thm.arsnova.security.DbUserDetailsService; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableWebSecurity +@Profile("!test") +public class SecurityConfig extends WebSecurityConfigurerAdapter implements ServletContextAware { + private final Logger logger = LoggerFactory.getLogger(SecurityConfig.class); + + private ServletContext servletContext; + + @Value("${root-url}") private String rootUrl; + + @Value("${security.user-db.enabled}") private boolean dbAuthEnabled; + + @Value("${security.ldap.enabled}") private boolean ldapEnabled; + @Value("${security.ldap.url}") private String ldapUrl; + @Value("${security.ldap.user-dn-pattern}") private String ldapUserDn; + + @Value("${security.cas.enabled}") private boolean casEnabled; + @Value("${security.cas-server-url}") private String casUrl; + + @Value("${security.facebook.enabled}") private boolean facebookEnabled; + @Value("${security.facebook.key}") private String facebookKey; + @Value("${security.facebook.secret}") private String facebookSecret; + + @Value("${security.twitter.enabled}") private boolean twitterEnabled; + @Value("${security.twitter.key}") private String twitterKey; + @Value("${security.twitter.secret}") private String twitterSecret; + + @Value("${security.google.enabled}") private boolean googleEnabled; + @Value("${security.google.key}") private String googleKey; + @Value("${security.google.secret}") private String googleSecret; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint()); + http.csrf().disable(); + + if (casEnabled) { + http.addFilter(casAuthenticationFilter()); + http.addFilter(casLogoutFilter()); + } + if (googleEnabled) { + http.addFilterAfter(googleFilter(), CasAuthenticationFilter.class); + } + if (facebookEnabled) { + http.addFilterAfter(facebookFilter(), CasAuthenticationFilter.class); + } + if (twitterEnabled) { + http.addFilterAfter(twitterFilter(), CasAuthenticationFilter.class); + } + }; + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + List<String> providers = new ArrayList<>(); + if (dbAuthEnabled) { + providers.add("user-db"); + auth.authenticationProvider(daoAuthenticationProvider()); + } + if (ldapEnabled) { + providers.add("ldap"); + auth.authenticationProvider(ldapAuthenticationProvider()); + } + if (casEnabled) { + providers.add("cas"); + auth.authenticationProvider(casAuthenticationProvider()); + } + if (googleEnabled) { + providers.add("google"); + auth.authenticationProvider(googleAuthProvider()); + } + if (facebookEnabled) { + providers.add("facebook"); + auth.authenticationProvider(facebookAuthProvider()); + } + if (twitterEnabled) { + providers.add("twitter"); + auth.authenticationProvider(twitterAuthProvider()); + } + logger.info("Enabled authentication providers: {}", providers); + }; + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManager(); + } + + @Bean + public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { + final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); + configurer.setLocations(new Resource[] { + new ClassPathResource("arsnova.properties.example"), + new FileSystemResource("file:///etc/arsnova/arsnova.properties"), + }); + configurer.setIgnoreResourceNotFound(true); + configurer.setIgnoreUnresolvablePlaceholders(false); + + return configurer; + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + @Bean + public PermissionEvaluator permissionEvaluator() { + return new ApplicationPermissionEvaluator(); + } + + @Bean + public static AuthenticationEntryPoint restAuthenticationEntryPoint() { + return new Http403ForbiddenEntryPoint(); + } + + @Bean + LoginAuthenticationSucessHandler successHandler() { + final LoginAuthenticationSucessHandler successHandler = new LoginAuthenticationSucessHandler(); + successHandler.setTargetUrl(rootUrl); + + return successHandler; + } + + @Bean + LoginAuthenticationFailureHandler failureHandler() { + final LoginAuthenticationFailureHandler failureHandler = new LoginAuthenticationFailureHandler(); + failureHandler.setDefaultFailureUrl(rootUrl); + + return failureHandler; + } + + // Database Authentication Configuration + + @Bean + public DaoAuthenticationProvider daoAuthenticationProvider() { + final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); + authProvider.setUserDetailsService(dbUserDetailsService()); + authProvider.setPasswordEncoder(passwordEncoder()); + + return authProvider; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public DbUserDetailsService dbUserDetailsService() { + return new DbUserDetailsService(); + } + + @Bean + public SecurityContextLogoutHandler logoutHandler() { + return new SecurityContextLogoutHandler(); + } + + // LDAP Authentication Configuration + + @Bean + public LdapAuthenticationProvider ldapAuthenticationProvider() throws Exception { + return new LdapAuthenticationProvider(ldapAuthenticator(), ldapAuthoritiesPopulator()); + } + + @Bean + public LdapContextSource ldapContextSource() throws Exception { + DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(ldapUrl); + /* TODO: implement support for LDAP bind using manager credentials */ +// contextSource.setUserDn(ldapManagerUserDn); +// contextSource.setPassword(ldapManagerPassword); + + return contextSource; + } + + @Bean + public LdapAuthenticator ldapAuthenticator() throws Exception { + BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource()); + authenticator.setUserDnPatterns(new String[] {ldapUserDn}); + + return authenticator; + } + + @Bean + public LdapAuthoritiesPopulator ldapAuthoritiesPopulator() throws Exception { + return new DefaultLdapAuthoritiesPopulator(ldapContextSource(), null); + } + + // CAS Authentication Configuration + + @Bean + public CasAuthenticationProvider casAuthenticationProvider() { + CasAuthenticationProvider authProvider = new CasAuthenticationProvider(); + authProvider.setAuthenticationUserDetailsService(casUserDetailsService()); + authProvider.setServiceProperties(casServiceProperties()); + authProvider.setTicketValidator(casTicketValidator()); + authProvider.setKey("casAuthProviderKey"); + + return authProvider; + } + + @Bean + public CasUserDetailsService casUserDetailsService() { + return new CasUserDetailsService(); + } + + @Bean + public ServiceProperties casServiceProperties() { + ServiceProperties properties = new ServiceProperties(); + properties.setService(rootUrl + servletContext.getContextPath() + "/j_spring_cas_security_check"); + properties.setSendRenew(false); + + return properties; + } + + @Bean + public Cas20ProxyTicketValidator casTicketValidator() { + return new Cas20ProxyTicketValidator(casUrl); + } + + @Bean + public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { + CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); + entryPoint.setLoginUrl(casUrl + "/login"); + entryPoint.setServiceProperties(casServiceProperties()); + + return entryPoint; + } + + @Bean + public CasAuthenticationFilter casAuthenticationFilter() throws Exception { + CasAuthenticationFilter filter = new CasAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManager()); + filter.setAuthenticationSuccessHandler(successHandler()); + filter.setAuthenticationFailureHandler(failureHandler()); + + return filter; + } + + @Bean + public LogoutFilter casLogoutFilter() { + LogoutFilter filter = new LogoutFilter(casLogoutSuccessHandler(), logoutHandler()); + filter.setLogoutRequestMatcher(new AntPathRequestMatcher("/j_spring_cas_security_logout")); + + return filter; + } + + @Bean + public LogoutSuccessHandler casLogoutSuccessHandler() { + CASLogoutSuccessHandler handler = new CASLogoutSuccessHandler(); + handler.setCasUrl(casUrl); + handler.setDefaultTarget(rootUrl); + + return handler; + } + + // Facebook Authentication Configuration + + @Bean + public OAuthAuthenticationEntryPoint facebookEntryPoint() { + final OAuthAuthenticationEntryPoint entryPoint = new OAuthAuthenticationEntryPoint(); + entryPoint.setProvider(facebookProvider()); + + return entryPoint; + } + + @Bean + public FacebookProvider facebookProvider() { + final FacebookProvider provider = new FacebookProvider(); + provider.setKey(facebookKey); + provider.setSecret(facebookSecret); + provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_facebook_security_check"); + + return provider; + } + + @Bean + public OAuthAuthenticationFilter facebookFilter() throws Exception { + final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_facebook_security_check"); + filter.setProvider(facebookProvider()); + filter.setAuthenticationManager(authenticationManager()); + filter.setAuthenticationFailureHandler(failureHandler()); + filter.setAuthenticationSuccessHandler(successHandler()); + + return filter; + } + + @Bean + public OAuthAuthenticationProvider facebookAuthProvider() { + final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider(); + authProvider.setProvider(facebookProvider()); + + return authProvider; + } + + // Twitter Authentication Configuration + + @Bean + public TwitterProvider twitterProvider() { + final TwitterProvider provider = new TwitterProvider(); + provider.setKey(twitterKey); + provider.setSecret(twitterSecret); + provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_twitter_security_check"); + + return provider; + } + + @Bean + public OAuthAuthenticationFilter twitterFilter() throws Exception { + final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_twitter_security_check"); + filter.setProvider(twitterProvider()); + filter.setAuthenticationManager(authenticationManager()); + filter.setAuthenticationFailureHandler(failureHandler()); + filter.setAuthenticationSuccessHandler(successHandler()); + return filter; + } + + @Bean + public OAuthAuthenticationProvider twitterAuthProvider() { + final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider(); + authProvider.setProvider(twitterProvider()); + + return authProvider; + } + + // Google Authentication Configuration + + @Bean + public Google2Provider googleProvider() { + final Google2Provider provider = new Google2Provider(); + provider.setKey(googleKey); + provider.setSecret(googleSecret); + provider.setCallbackUrl(rootUrl + servletContext.getContextPath() + "/j_spring_google_security_check"); + provider.setScope(Google2Scope.EMAIL); + + return provider; + } + + @Bean + public OAuthAuthenticationFilter googleFilter() throws Exception { + final OAuthAuthenticationFilter filter = new OAuthAuthenticationFilter("/j_spring_google_security_check"); + filter.setProvider(googleProvider()); + filter.setAuthenticationManager(authenticationManager()); + filter.setAuthenticationFailureHandler(failureHandler()); + filter.setAuthenticationSuccessHandler(successHandler()); + + return filter; + } + + @Bean + public OAuthAuthenticationProvider googleAuthProvider() { + final OAuthAuthenticationProvider authProvider = new OAuthAuthenticationProvider(); + authProvider.setProvider(googleProvider()); + + return authProvider; + } + + @Override + public void setServletContext(ServletContext servletContext) { + this.servletContext = servletContext; + } +} diff --git a/src/main/java/de/thm/arsnova/controller/AbstractController.java b/src/main/java/de/thm/arsnova/controller/AbstractController.java index 755a5625b6b01262b89c700902282acbd20491bc..27eb3d61bf83f6931b7371dbb5c6d1d672766958 100644 --- a/src/main/java/de/thm/arsnova/controller/AbstractController.java +++ b/src/main/java/de/thm/arsnova/controller/AbstractController.java @@ -1,67 +1,6 @@ package de.thm.arsnova.controller; -import javax.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpStatus; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; - -import de.thm.arsnova.exceptions.BadRequestException; -import de.thm.arsnova.exceptions.ForbiddenException; -import de.thm.arsnova.exceptions.NoContentException; -import de.thm.arsnova.exceptions.NotFoundException; -import de.thm.arsnova.exceptions.NotImplementedException; -import de.thm.arsnova.exceptions.PreconditionFailedException; -import de.thm.arsnova.exceptions.UnauthorizedException; - public class AbstractController { protected static final String X_DEPRECATED_API = "X-Deprecated-API"; protected static final String X_FORWARDED = "X-Forwarded"; - - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(NotFoundException.class) - public void handleNotFoundException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.UNAUTHORIZED) - @ExceptionHandler(UnauthorizedException.class) - public void handleUnauthorizedException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.UNAUTHORIZED) - @ExceptionHandler(AuthenticationCredentialsNotFoundException.class) - public void handleAuthenticationCredentialsNotFoundException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.FORBIDDEN) - @ExceptionHandler(ForbiddenException.class) - public void handleForbiddenException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.FORBIDDEN) - @ExceptionHandler(AccessDeniedException.class) - public void handleAccessDeniedException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.NO_CONTENT) - @ExceptionHandler(NoContentException.class) - public void handleNoContentException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(BadRequestException.class) - public void handleBadRequestException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.PRECONDITION_FAILED) - @ExceptionHandler(PreconditionFailedException.class) - public void handlePreconditionFailedException(final Exception e, final HttpServletRequest request) { - } - - @ResponseStatus(HttpStatus.NOT_IMPLEMENTED) - @ExceptionHandler(NotImplementedException.class) - public void handleNotImplementedException(final Exception e, final HttpServletRequest request) { - } } diff --git a/src/main/java/de/thm/arsnova/controller/LoginController.java b/src/main/java/de/thm/arsnova/controller/LoginController.java index 6ab3234e05815d722c5a112f00503a0591364b10..4236fdfeda69c08a29ffed2068991ba7a8a79fd3 100644 --- a/src/main/java/de/thm/arsnova/controller/LoginController.java +++ b/src/main/java/de/thm/arsnova/controller/LoginController.java @@ -136,22 +136,22 @@ public class LoginController extends AbstractController { @Value("${security.twitter.enabled}") private String twitterEnabled; - @Autowired + @Autowired(required = false) private DaoAuthenticationProvider daoProvider; - @Autowired + @Autowired(required = false) private TwitterProvider twitterProvider; - @Autowired + @Autowired(required = false) private Google2Provider googleProvider; - @Autowired + @Autowired(required = false) private FacebookProvider facebookProvider; - - @Autowired + + @Autowired(required = false) private LdapAuthenticationProvider ldapAuthenticationProvider; - @Autowired + @Autowired(required = false) private CasAuthenticationEntryPoint casEntryPoint; @Autowired diff --git a/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java b/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java new file mode 100644 index 0000000000000000000000000000000000000000..a21ae905f428feecb84ba19a237595d84f49cfa1 --- /dev/null +++ b/src/main/java/de/thm/arsnova/controller/SecurityExceptionControllerAdvice.java @@ -0,0 +1,84 @@ +package de.thm.arsnova.controller; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; + +import de.thm.arsnova.exceptions.BadRequestException; +import de.thm.arsnova.exceptions.ForbiddenException; +import de.thm.arsnova.exceptions.NoContentException; +import de.thm.arsnova.exceptions.NotFoundException; +import de.thm.arsnova.exceptions.NotImplementedException; +import de.thm.arsnova.exceptions.PreconditionFailedException; +import de.thm.arsnova.exceptions.UnauthorizedException; + +@ControllerAdvice +public class SecurityExceptionControllerAdvice { + + @ResponseStatus(HttpStatus.NOT_FOUND) + @ExceptionHandler(NotFoundException.class) + public void handleNotFoundException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.UNAUTHORIZED) + @ExceptionHandler(UnauthorizedException.class) + public void handleUnauthorizedException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.UNAUTHORIZED) + @ExceptionHandler(AuthenticationCredentialsNotFoundException.class) + public void handleAuthenticationCredentialsNotFoundException(final Exception e, final HttpServletRequest request) { + } + + @ExceptionHandler(AccessDeniedException.class) + public void handleAccessDeniedException( + final Exception e, + final HttpServletRequest request, + final HttpServletResponse response + ) { + final Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if ( + authentication == null + || authentication.getPrincipal() == null + || authentication instanceof AnonymousAuthenticationToken + ) { + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + return; + } + response.setStatus(HttpStatus.FORBIDDEN.value()); + } + + @ResponseStatus(HttpStatus.FORBIDDEN) + @ExceptionHandler(ForbiddenException.class) + public void handleForbiddenException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.NO_CONTENT) + @ExceptionHandler(NoContentException.class) + public void handleNoContentException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(BadRequestException.class) + public void handleBadRequestException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.PRECONDITION_FAILED) + @ExceptionHandler(PreconditionFailedException.class) + public void handlePreconditionFailedException(final Exception e, final HttpServletRequest request) { + } + + @ResponseStatus(HttpStatus.NOT_IMPLEMENTED) + @ExceptionHandler(NotImplementedException.class) + public void handleNotImplementedException(final Exception e, final HttpServletRequest request) { + } +} diff --git a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java index 4e3d4965fe7e9201387b61c3b46908cce53893df..98a98b222467d3123d3cce6e240aabcf45bc7aea 100644 --- a/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java +++ b/src/main/java/de/thm/arsnova/security/ApplicationPermissionEvaluator.java @@ -17,7 +17,6 @@ import de.thm.arsnova.entities.InterposedQuestion; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; import de.thm.arsnova.entities.User; -import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.UnauthorizedException; public class ApplicationPermissionEvaluator implements PermissionEvaluator { @@ -35,11 +34,11 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { if ( targetDomainObject instanceof Session - && !checkSessionPermission(username, ((Session) targetDomainObject).getKeyword(), permission) + && checkSessionPermission(username, ((Session) targetDomainObject).getKeyword(), permission) ) { - throw new ForbiddenException(); + return true; } - return true; + return false; } @Override @@ -51,20 +50,22 @@ public class ApplicationPermissionEvaluator implements PermissionEvaluator { ) { final String username = getUsername(authentication); - if ("session".equals(targetType) && !checkSessionPermission(username, targetId, permission)) { - throw new ForbiddenException(); + if ( + "session".equals(targetType) + && checkSessionPermission(username, targetId, permission)) { + return true; } else if ( "question".equals(targetType) - && !checkQuestionPermission(username, targetId, permission) + && checkQuestionPermission(username, targetId, permission) ) { - throw new ForbiddenException(); + return true; } else if ( "interposedquestion".equals(targetType) - && !checkInterposedQuestionPermission(username, targetId, permission) + && checkInterposedQuestionPermission(username, targetId, permission) ) { - throw new ForbiddenException(); + return true; } - return true; + return false; } private boolean checkSessionPermission( diff --git a/src/main/webapp/arsnova.properties.example b/src/main/resources/arsnova.properties.example similarity index 97% rename from src/main/webapp/arsnova.properties.example rename to src/main/resources/arsnova.properties.example index 0fa0a198ebe3ad3123f24c8a8a8b824f899462bd..7dbef658c609efaad8d36f46b8588cc30615dff0 100644 --- a/src/main/webapp/arsnova.properties.example +++ b/src/main/resources/arsnova.properties.example @@ -100,7 +100,7 @@ security.user-db.reset-password-mail.body=You requested to reset your \ # user-dn-pattern: Pattern used to check user credentials against the LDAP # server. {0} will be replaced with the user ID by ARSnova. # -security.ldap.enabled=true +security.ldap.enabled=false security.ldap.title=LDAP security.ldap.login-dialog-path=login-ldap.html security.ldap.image= @@ -113,7 +113,7 @@ security.ldap.user-dn-pattern=uid={0},ou=arsnova # CAS authentication # -security.cas.enabled=true +security.cas.enabled=false security.cas.title=CAS security.cas.image= security.cas.order=0 @@ -126,21 +126,21 @@ security.cas-server-url=https://example.com/cas # Facebook # -security.facebook.enabled=true +security.facebook.enabled=false security.facebook.order=0 security.facebook.key= security.facebook.secret= # Twitter # -security.twitter.enabled=true +security.twitter.enabled=false security.twitter.order=0 security.twitter.key= security.twitter.secret= # Google # -security.google.enabled=true +security.google.enabled=false security.google.order=0 security.google.key= security.google.secret= diff --git a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml index 1dc289bf8d65a31be351a0f1609a250039b5ae22..9abd13f4d97fbd9e5d3def289c503144b1cabd3e 100644 --- a/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml +++ b/src/main/webapp/WEB-INF/spring/arsnova-servlet.xml @@ -13,16 +13,28 @@ <!-- ARSnova Servlet Context --> <context:component-scan base-package="de.thm.arsnova.controller,de.thm.arsnova.web" /> - <context:property-placeholder location="file:///etc/arsnova/arsnova.properties" file-encoding="UTF-8" /> + <mvc:annotation-driven - content-negotiation-manager="contentNegotiationManager" /> + content-negotiation-manager="mvcContentNegotiationManager" /> <mvc:interceptors> <bean class="de.thm.arsnova.web.CacheControlInterceptorHandler" /> <bean class="de.thm.arsnova.web.DeprecatedApiInterceptorHandler" /> </mvc:interceptors> - <bean id="contentNegotiationManager" + <bean id="propertyPlaceholderConfigurer" + class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" + p:ignoreUnresolvablePlaceholders="false" p:ignoreResourceNotFound="true"> + <property name="locations"> + <list> + <value>classpath:arsnova.properties.example</value> + <value>file:///etc/arsnova/arsnova.properties</value> + </list> + </property> + <property name="fileEncoding" value="UTF-8" /> + </bean> + + <bean id="mvcContentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"> <property name="favorPathExtension" value="false" /> <property name="favorParameter" value="true" /> diff --git a/src/main/webapp/WEB-INF/spring/spring-main.xml b/src/main/webapp/WEB-INF/spring/spring-main.xml index c94ed0de36aafc55edaee52ebc5d97e7b5ab0137..b9bedd607523757fe09fd2554f21851a8eaa0b7f 100644 --- a/src/main/webapp/WEB-INF/spring/spring-main.xml +++ b/src/main/webapp/WEB-INF/spring/spring-main.xml @@ -16,15 +16,13 @@ p:ignoreUnresolvablePlaceholders="false" p:ignoreResourceNotFound="true"> <property name="locations"> <list> - <value>arsnova.properties.example</value> + <value>classpath:arsnova.properties.example</value> <value>file:///etc/arsnova/arsnova.properties</value> </list> </property> <property name="fileEncoding" value="UTF-8" /> </bean> - <import resource="spring-security.xml" /> - <context:component-scan base-package="de.thm.arsnova.dao,de.thm.arsnova.events,de.thm.arsnova.security,de.thm.arsnova.services,de.thm.arsnova.config" /> <context:annotation-config /> diff --git a/src/main/webapp/WEB-INF/spring/spring-security.xml b/src/main/webapp/WEB-INF/spring/spring-security.xml deleted file mode 100644 index 0d843b17bc58c460b7ee4aab2094b2f92ec9b302..0000000000000000000000000000000000000000 --- a/src/main/webapp/WEB-INF/spring/spring-security.xml +++ /dev/null @@ -1,163 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:security="http://www.springframework.org/schema/security" - xmlns:context="http://www.springframework.org/schema/context" - xmlns:p="http://www.springframework.org/schema/p" - xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd - http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> - - <security:authentication-manager alias="authenticationManager"> - <!-- <security:ldap-authentication-provider - user-search-filter="${security.ldap.user-search-filter}" - user-search-base="${security.ldap.user-search-base}" /> - --> - <security:ldap-authentication-provider user-dn-pattern="${security.ldap.user-dn-pattern}" /> - <security:authentication-provider ref="facebookAuthProvider" /> - <security:authentication-provider ref="twitterAuthProvider" /> - <security:authentication-provider ref="googleAuthProvider" /> - <security:authentication-provider ref="casAuthenticationProvider" /> - <security:authentication-provider ref="daoAuthenticationProvider" /> - </security:authentication-manager> - - <security:http entry-point-ref="restLoginEntryPoint"> - <security:custom-filter ref="facebookFilter" before="CAS_FILTER" /> - <security:custom-filter ref="twitterFilter" after="CAS_FILTER" /> - <security:custom-filter ref="googleFilter" before="FORM_LOGIN_FILTER" /> - <security:custom-filter ref="casAuthenticationFilter" position="CAS_FILTER" /> - <security:custom-filter ref="requestSingleLogoutFilter" before="LOGOUT_FILTER" /> - </security:http> - - <!-- ######################### DB auth ############################# --> - <bean id="daoAuthenticationProvider" - class="org.springframework.security.authentication.dao.DaoAuthenticationProvider"> - <property name="userDetailsService" ref="dbUserDetailsService" /> - <property name="passwordEncoder" ref="passwordEncoder" /> - </bean> - - <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> - - <!-- ######################### LDAP ############################# --> - <security:ldap-server url="${security.ldap.url}" /> - <!-- <security:ldap-server ldif="classpath:/test.ldif" root="dc=example,dc=com" /> --> - - <bean id="restLoginEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint" /> - - <bean id="loginUrlAuthenticationEntryPoint" - class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> - <property name="loginFormUrl" value="/login.html" /> - <property name="forceHttps" value="false" /> - </bean> - - <!-- ######################### FACEBOOK ######################### --> - <bean id="facebookEntryPoint" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint" - p:provider-ref="facebookProvider" /> - - <bean id="facebookProvider" class="org.scribe.up.provider.impl.FacebookProvider" - p:key="${security.facebook.key}" - p:secret="${security.facebook.secret}" - p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_facebook_security_check" /> - - <bean id="facebookFilter" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter" - p:filterProcessesUrl="/j_spring_facebook_security_check" - p:provider-ref="facebookProvider" - p:authenticationManager-ref="authenticationManager" - p:authenticationFailureHandler-ref="failureHandler" - p:authenticationSuccessHandler-ref="successHandler" /> - - <bean id="facebookAuthProvider" class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider" - p:provider-ref="facebookProvider" /> - - <!-- ######################### TWITTER ######################### --> - <bean id="twitterProvider" class="org.scribe.up.provider.impl.TwitterProvider" - p:key="${security.twitter.key}" - p:secret="${security.twitter.secret}" - p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_twitter_security_check" /> - - <bean id="twitterFilter" class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter" - p:filterProcessesUrl="/j_spring_twitter_security_check" - p:provider-ref="twitterProvider" - p:authenticationManager-ref="authenticationManager" - p:authenticationFailureHandler-ref="failureHandler" - p:authenticationSuccessHandler-ref="successHandler" /> - - <bean id="twitterAuthProvider" class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider" - p:provider-ref="twitterProvider" /> - - <!-- ######################### GOOGLE ######################### --> - <bean id="googleProvider" class="org.scribe.up.provider.impl.Google2Provider" - p:key="${security.google.key}" - p:secret="${security.google.secret}" - p:scope-ref="googleScope" - p:callbackUrl="${root-url}#{servletContext.contextPath}/j_spring_google_security_check" /> - - <bean id="googleScope" class="org.scribe.up.provider.impl.Google2Provider.Google2Scope" factory-method="valueOf"> - <constructor-arg index="0" value="EMAIL" /> - </bean> - - <bean id="googleFilter" - class="com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter" - p:filterProcessesUrl="/j_spring_google_security_check" p:provider-ref="googleProvider" - p:authenticationManager-ref="authenticationManager" - p:authenticationFailureHandler-ref="failureHandler" - p:authenticationSuccessHandler-ref="successHandler" /> - - <bean id="googleAuthProvider" - class="com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider" - p:provider-ref="googleProvider" /> - - <!-- ######################### CAS ######################### --> - <bean id="casAuthenticationFilter" - class="org.springframework.security.cas.web.CasAuthenticationFilter" - p:authenticationManager-ref="authenticationManager" - p:authenticationFailureHandler-ref="failureHandler" - p:authenticationSuccessHandler-ref="successHandler" /> - - <bean id="casEntryPoint" - class="org.springframework.security.cas.web.CasAuthenticationEntryPoint" - p:loginUrl="${security.cas-server-url}/login" - p:serviceProperties-ref="casServiceProperties" /> - - <bean id="casServiceProperties" - class="org.springframework.security.cas.ServiceProperties" - p:service="${root-url}#{servletContext.contextPath}/j_spring_cas_security_check" - p:sendRenew="false" /> - - <bean id="casAuthenticationProvider" - class="org.springframework.security.cas.authentication.CasAuthenticationProvider" - p:key="casAuthProviderKey" p:serviceProperties-ref="casServiceProperties" - p:authenticationUserDetailsService-ref="casUserDetailsService" - p:ticketValidator-ref="casTicketValidator" /> - - <bean id="casUserDetailsService" class="de.thm.arsnova.CasUserDetailsService" /> - <bean id="casTicketValidator" - class="org.jasig.cas.client.validation.Cas20ProxyTicketValidator"> - <constructor-arg value="${security.cas-server-url}" /> - </bean> - - <bean id="requestSingleLogoutFilter" - class="org.springframework.security.web.authentication.logout.LogoutFilter" - p:filterProcessesUrl="/j_spring_cas_security_logout"> - <constructor-arg ref="casLogoutSuccessHandler" /> - <constructor-arg> - <bean - class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" /> - </constructor-arg> - </bean> - - <bean id="casLogoutSuccessHandler" class="de.thm.arsnova.CASLogoutSuccessHandler" - p:casUrl="${security.cas-server-url}" - p:defaultTarget="${root-url}"/> - - <bean id="successHandler" class="de.thm.arsnova.LoginAuthenticationSucessHandler" - p:targetUrl="${root-url}"/> - - <bean id="failureHandler" class="de.thm.arsnova.LoginAuthenticationFailureHandler" - p:defaultFailureUrl="${root-url}" /> - - <!-- Session Registry --> - <bean id="sessionRegistry" - class="org.springframework.security.core.session.SessionRegistryImpl" /> -</beans> diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index b5d6e63c55228e019cf544417dc958d2c0b19131..9c71a5a4a2233deb0bc848ee71fd5aa8356532dc 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -7,7 +7,6 @@ <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/spring/spring-main.xml - /WEB-INF/spring/spring-security.xml </param-value> </context-param> <context-param> diff --git a/src/test/java/de/thm/arsnova/config/ExtraConfigTest.java b/src/test/java/de/thm/arsnova/config/ExtraConfigTest.java index 8b9ede4ef0bbf60e7e62235b3d78e98e6859a7e0..ee37027f0305a545d8c0e5a2c9d32c32bc062f1f 100644 --- a/src/test/java/de/thm/arsnova/config/ExtraConfigTest.java +++ b/src/test/java/de/thm/arsnova/config/ExtraConfigTest.java @@ -5,6 +5,7 @@ import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -16,10 +17,10 @@ import de.thm.arsnova.connector.client.ConnectorClient; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class ExtraConfigTest { @Autowired(required = false) diff --git a/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java b/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..012c40b7b69a43e698b6d9e3cb6711f487003831 --- /dev/null +++ b/src/test/java/de/thm/arsnova/config/TestSecurityConfig.java @@ -0,0 +1,138 @@ +package de.thm.arsnova.config; + +import org.jasig.cas.client.validation.Cas20ProxyTicketValidator; +import org.scribe.up.provider.impl.FacebookProvider; +import org.scribe.up.provider.impl.Google2Provider; +import org.scribe.up.provider.impl.TwitterProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; +import org.springframework.security.cas.web.CasAuthenticationFilter; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.core.session.SessionRegistry; +import org.springframework.security.core.session.SessionRegistryImpl; + +import com.github.leleuj.ss.oauth.client.authentication.OAuthAuthenticationProvider; +import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationEntryPoint; +import com.github.leleuj.ss.oauth.client.web.OAuthAuthenticationFilter; + +import de.thm.arsnova.CasUserDetailsService; + +@Configuration +@EnableGlobalMethodSecurity(prePostEnabled = true) +@EnableWebSecurity +@Profile("test") +public class TestSecurityConfig extends SecurityConfig { + @Override + protected void configure(HttpSecurity http) {}; + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + auth.inMemoryAuthentication() + .withUser("ptsr00") + .password("secret") + .authorities("ROLE_USER") + ; + } + + @Bean + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManager(); + } + + @Bean + public SessionRegistry sessionRegistry() { + return new SessionRegistryImpl(); + } + + + /* Override for test unnecessary Beans with null */ + + @Override + public CasAuthenticationProvider casAuthenticationProvider() { + return null; + } + + @Override + public CasUserDetailsService casUserDetailsService() { + return null; + } + + @Override + public ServiceProperties casServiceProperties() { + return null; + } + + @Override + public Cas20ProxyTicketValidator casTicketValidator() { + return null; + } + + @Override + public CasAuthenticationEntryPoint casAuthenticationEntryPoint() { + return null; + } + + @Override + public CasAuthenticationFilter casAuthenticationFilter() { + return null; + } + + @Override + public FacebookProvider facebookProvider() { + return null; + } + + @Override + public OAuthAuthenticationFilter facebookFilter() { + return null; + } + + @Override + public OAuthAuthenticationProvider facebookAuthProvider() { + return null; + } + + @Override + public OAuthAuthenticationEntryPoint facebookEntryPoint() { + return null; + } + + @Override + public Google2Provider googleProvider() { + return null; + } + + @Override + public OAuthAuthenticationFilter googleFilter() { + return null; + } + + @Override + public OAuthAuthenticationProvider googleAuthProvider() { + return null; + } + + @Override + public TwitterProvider twitterProvider() { + return null; + } + + @Override + public OAuthAuthenticationFilter twitterFilter() { + return null; + } + + @Override + public OAuthAuthenticationProvider twitterAuthProvider() { + return null; + } +} diff --git a/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java b/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..626eaf31fddb9b3bfc713f1f375fc8a589340695 --- /dev/null +++ b/src/test/java/de/thm/arsnova/controller/AbstractControllerTest.java @@ -0,0 +1,39 @@ +package de.thm.arsnova.controller; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; + +import de.thm.arsnova.services.StubUserService; + +public abstract class AbstractControllerTest { + + @Autowired protected StubUserService userService; + + public AbstractControllerTest() { + super(); + } + + protected void setAuthenticated(final boolean isAuthenticated, final String username) { + final List<GrantedAuthority> ga = new ArrayList<GrantedAuthority>(); + if (isAuthenticated) { + final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, "secret", ga); + SecurityContextHolder.getContext().setAuthentication(token); + userService.setUserAuthenticated(isAuthenticated, username); + } else { + userService.setUserAuthenticated(isAuthenticated); + } + } + + @After + public final void cleanup() { + SecurityContextHolder.clearContext(); + userService.setUserAuthenticated(false); + } + +} \ No newline at end of file diff --git a/src/test/java/de/thm/arsnova/controller/CourseControllerTest.java b/src/test/java/de/thm/arsnova/controller/CourseControllerTest.java index 7b122982ca65b02e078cdf85140ceea5efca3dcc..5a2a79081352c81c6bfe993280e01c6172b45fa3 100644 --- a/src/test/java/de/thm/arsnova/controller/CourseControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/CourseControllerTest.java @@ -16,6 +16,7 @@ import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -31,10 +32,11 @@ import de.thm.arsnova.services.StubUserService; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) + +@ActiveProfiles("test") public class CourseControllerTest { private MockMvc mockMvc; diff --git a/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java b/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java index a9fc7a1613f52ad71b6d74eabc4bd2b9a54354e1..610fbca29dfdbab59f496f96d3cc23761925616e 100644 --- a/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/FeedbackControllerTest.java @@ -11,6 +11,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -25,10 +26,10 @@ import de.thm.arsnova.services.StubUserService; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class FeedbackControllerTest { @Autowired diff --git a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java b/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java index bccdc773ce1462219c49fa00ce6cd54dcae27076..d6477d7e0739a3c75b62ecb2a9b535415f1330ec 100644 --- a/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/LecturerQuestionControllerTest.java @@ -17,6 +17,7 @@ import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -31,10 +32,10 @@ import de.thm.arsnova.services.StubUserService; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class LecturerQuestionControllerTest { @Autowired diff --git a/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java b/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java index c2e27a6bbd119c6b1700e5467f2898319b5e96ac..dafb87efc7bf71d301f06b2069b5b6f1a04a1d16 100644 --- a/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/LoginControllerTest.java @@ -33,6 +33,7 @@ import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -47,10 +48,10 @@ import de.thm.arsnova.services.StubUserService; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class LoginControllerTest { @Autowired @@ -71,8 +72,7 @@ public class LoginControllerTest { mockMvc.perform( get("/doLogin") .param("type", "guest") - ).andExpect(status().isMovedTemporarily()) - .andExpect(redirectedUrl("/#auth/checkLogin")); + ).andExpect(status().isOk()); } @Test @@ -80,8 +80,7 @@ public class LoginControllerTest { mockMvc.perform( get("/doLogin") .param("type", "guest").param("user","Guest1234567890") - ).andExpect(status().isMovedTemporarily()) - .andExpect(redirectedUrl("/#auth/checkLogin")); + ).andExpect(status().isOk()); final Authentication auth = SecurityContextHolder.getContext() .getAuthentication(); diff --git a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java b/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java index 9918233f24ddcfa745d0b39e8df8718a4d082e22..4860c0746b8a68a0dea2f49d99e1a53371b4c1dc 100644 --- a/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/SessionControllerTest.java @@ -1,7 +1,9 @@ package de.thm.arsnova.controller; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -18,6 +20,7 @@ import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -32,10 +35,10 @@ import de.thm.arsnova.services.StubUserService; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class SessionControllerTest { @Autowired @@ -143,4 +146,28 @@ public class SessionControllerTest { .andExpect(status().isOk()) .andExpect(header().string(AbstractController.X_DEPRECATED_API, "1")); } + + @Test + public void testShouldEndInUnauthorizedResult() throws Exception { + setAuthenticated(false, "ptsr00"); + + mockMvc.perform(post("/session/12345678/online").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isUnauthorized()); + } + + @Test + public void testShouldEndInForbidden() throws Exception { + setAuthenticated(true, "ptsr00"); + + mockMvc.perform( + put("/session/12345678") + .content("{\"keyword\":\"12345678\", \"name\":\"Testsession\"}, \"shortName\":\"TS\", \"creator\":\"ptsr00\", \"active\":true") + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + + setAuthenticated(true, "other"); + + mockMvc.perform(delete("/session/12345678")).andExpect(status().isForbidden()); + } } diff --git a/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java b/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java index a23f9132519aa94643d059d841ea7fff2c673a46..3fac5873cd1f4bca907937b579954b5935b7e985 100644 --- a/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java +++ b/src/test/java/de/thm/arsnova/controller/StatisticsControllerTest.java @@ -11,6 +11,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; @@ -23,10 +24,10 @@ import org.springframework.web.context.WebApplicationContext; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml", "file:src/test/resources/test-socketioconfig.xml" }) +@ActiveProfiles("test") public class StatisticsControllerTest { @Autowired @@ -46,13 +47,14 @@ public class StatisticsControllerTest { public final void testShouldGetCurrentOnlineUsers() throws Exception { mockMvc.perform(get("/statistics/activeusercount").accept(MediaType.TEXT_PLAIN)) .andExpect(status().isOk()) - .andExpect(content().string("0")); + .andExpect(content().contentType("text/plain")); } @Test public final void testShouldSendXDeprecatedApiForGetCurrentOnlineUsers() throws Exception { mockMvc.perform(get("/statistics/activeusercount").accept(MediaType.TEXT_PLAIN)) .andExpect(status().isOk()) + .andExpect(content().contentType("text/plain")) .andExpect(header().string(AbstractController.X_DEPRECATED_API,"1")); } @@ -60,6 +62,7 @@ public class StatisticsControllerTest { public final void testShouldGetSessionCount() throws Exception { mockMvc.perform(get("/statistics/sessioncount").accept(MediaType.TEXT_PLAIN)) .andExpect(status().isOk()) + .andExpect(content().contentType("text/plain")) .andExpect(content().string("3")); } @@ -67,6 +70,7 @@ public class StatisticsControllerTest { public final void testShouldSendXDeprecatedApiForGetSessionCount() throws Exception { mockMvc.perform(get("/statistics/sessioncount").accept(MediaType.TEXT_PLAIN)) .andExpect(status().isOk()) + .andExpect(content().contentType("text/plain")) .andExpect(header().string(AbstractController.X_DEPRECATED_API,"1")); } @@ -79,7 +83,7 @@ public class StatisticsControllerTest { .andExpect(jsonPath("$.questions").value(0)) .andExpect(jsonPath("$.openSessions").value(3)) .andExpect(jsonPath("$.closedSessions").value(0)) - .andExpect(jsonPath("$.activeUsers").value(0)); + .andExpect(jsonPath("$.activeUsers").exists()); } @Test diff --git a/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java b/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java index 671a7898c8ec41cb51ebfaef8e636db592d24e22..e399e8417c74988c7b346e94591c12c79566094b 100644 --- a/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java +++ b/src/test/java/de/thm/arsnova/services/FeedbackServiceTest.java @@ -40,7 +40,6 @@ import de.thm.arsnova.exceptions.NotFoundException; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml" }) @ActiveProfiles("test") diff --git a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java b/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java index 1e94d025dafa1ba483568856ce1aad9e00a87895..c8b86acdb8e7f3bc047ebf6633d45610746eb41b 100644 --- a/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java +++ b/src/test/java/de/thm/arsnova/services/QuestionServiceTest.java @@ -30,6 +30,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; @@ -41,14 +42,12 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import de.thm.arsnova.dao.StubDatabaseDao; import de.thm.arsnova.entities.InterposedQuestion; import de.thm.arsnova.entities.Question; -import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NotFoundException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml" }) @ActiveProfiles("test") @@ -130,7 +129,7 @@ public class QuestionServiceTest { assertFalse(theQ.isRead()); } - @Test(expected = ForbiddenException.class) + @Test(expected = AccessDeniedException.class) public void testShouldSaveQuestion() throws Exception{ setAuthenticated(true, "regular user"); final Question question = new Question(); @@ -139,13 +138,13 @@ public class QuestionServiceTest { questionService.saveQuestion(question); } - @Test(expected = ForbiddenException.class) + @Test(expected = AccessDeniedException.class) public void testShouldNotDeleteQuestion() throws Exception{ setAuthenticated(true, "otheruser"); questionService.deleteQuestion("a1a2a3a4a5a6a7a8a9a"); } - @Test(expected = ForbiddenException.class) + @Test(expected = AccessDeniedException.class) public void testShouldNotDeleteInterposedQuestion() throws Exception{ setAuthenticated(true, "otheruser"); questionService.deleteInterposedQuestion("a1a2a3a4a5a6a7a8a9a"); diff --git a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java b/src/test/java/de/thm/arsnova/services/SessionServiceTest.java index 9a2694032da8a431971f3bf5a34933037cbe4e94..09c07f18580afea478ee913eed35b2b2efb68cd4 100644 --- a/src/test/java/de/thm/arsnova/services/SessionServiceTest.java +++ b/src/test/java/de/thm/arsnova/services/SessionServiceTest.java @@ -38,6 +38,7 @@ import org.junit.runner.RunWith; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority; @@ -51,14 +52,12 @@ import de.thm.arsnova.dao.IDatabaseDao; import de.thm.arsnova.dao.StubDatabaseDao; import de.thm.arsnova.entities.Question; import de.thm.arsnova.entities.Session; -import de.thm.arsnova.exceptions.ForbiddenException; import de.thm.arsnova.exceptions.NotFoundException; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml" }) @ActiveProfiles("test") @@ -144,7 +143,7 @@ public class SessionServiceTest { assertNotNull(sessionService.joinSession("11111111")); } - @Test(expected = ForbiddenException.class) + @Test(expected = AccessDeniedException.class) public void testShouldUpdateSession() { setAuthenticated(true, "ptsr00"); @@ -193,7 +192,7 @@ public class SessionServiceTest { sessionService.deleteSession("12345678"); } - @Test(expected = ForbiddenException.class) + @Test(expected = AccessDeniedException.class) public void testShouldNotDeleteSessionIfNotOwner() { setAuthenticated(true, "anybody"); sessionService.deleteSession("12345678"); diff --git a/src/test/java/de/thm/arsnova/services/StatisticsServiceTest.java b/src/test/java/de/thm/arsnova/services/StatisticsServiceTest.java index 0ed3179fb0f020b38c6a4e281532efc0a8a84f0f..6b78ad029342a34d411b4736d120efe55b03e056 100644 --- a/src/test/java/de/thm/arsnova/services/StatisticsServiceTest.java +++ b/src/test/java/de/thm/arsnova/services/StatisticsServiceTest.java @@ -22,7 +22,6 @@ import de.thm.arsnova.entities.Statistics; @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/arsnova-servlet.xml", "file:src/main/webapp/WEB-INF/spring/spring-main.xml", - "file:src/main/webapp/WEB-INF/spring/spring-security.xml", "file:src/test/resources/test-config.xml" }) @ActiveProfiles("test") @@ -34,11 +33,14 @@ public class StatisticsServiceTest { @Mock private IDatabaseDao databaseDao; + @Mock + private IUserService userService; + @Before public final void startup() { MockitoAnnotations.initMocks(this); + when(userService.loggedInUsers()).thenReturn(42); when(databaseDao.countQuestions()).thenReturn(123); - when(databaseDao.countActiveUsers(anyInt())).thenReturn(42); when(databaseDao.countOpenSessions()).thenReturn(1978); when(databaseDao.countClosedSessions()).thenReturn(1984); when(databaseDao.countAnswers()).thenReturn(2014); diff --git a/src/test/resources/arsnova.properties.example b/src/test/resources/arsnova.properties.example index 0fa0a198ebe3ad3123f24c8a8a8b824f899462bd..7dbef658c609efaad8d36f46b8588cc30615dff0 100644 --- a/src/test/resources/arsnova.properties.example +++ b/src/test/resources/arsnova.properties.example @@ -100,7 +100,7 @@ security.user-db.reset-password-mail.body=You requested to reset your \ # user-dn-pattern: Pattern used to check user credentials against the LDAP # server. {0} will be replaced with the user ID by ARSnova. # -security.ldap.enabled=true +security.ldap.enabled=false security.ldap.title=LDAP security.ldap.login-dialog-path=login-ldap.html security.ldap.image= @@ -113,7 +113,7 @@ security.ldap.user-dn-pattern=uid={0},ou=arsnova # CAS authentication # -security.cas.enabled=true +security.cas.enabled=false security.cas.title=CAS security.cas.image= security.cas.order=0 @@ -126,21 +126,21 @@ security.cas-server-url=https://example.com/cas # Facebook # -security.facebook.enabled=true +security.facebook.enabled=false security.facebook.order=0 security.facebook.key= security.facebook.secret= # Twitter # -security.twitter.enabled=true +security.twitter.enabled=false security.twitter.order=0 security.twitter.key= security.twitter.secret= # Google # -security.google.enabled=true +security.google.enabled=false security.google.order=0 security.google.key= security.google.secret= diff --git a/src/test/resources/test-config.xml b/src/test/resources/test-config.xml index 1da5cc9d310d3894a29db0740ee8842e93a2d000..efaf6fd87ae7868e0ed09c598117fb2895b5172d 100644 --- a/src/test/resources/test-config.xml +++ b/src/test/resources/test-config.xml @@ -1,12 +1,7 @@ <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" - xmlns:websocket="http://www.springframework.org/schema/websocket" - xmlns:security="http://www.springframework.org/schema/security" - xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd - http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd - http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="servletContext" class="org.springframework.mock.web.MockServletContext"> <property name="contextPath" value="/" /> @@ -30,13 +25,4 @@ </map> </property> </bean> - - <security:authentication-manager> - <security:authentication-provider> - <security:user-service> - <security:user name="ptsr00" password="secret" - authorities="" /> - </security:user-service> - </security:authentication-provider> - </security:authentication-manager> </beans> diff --git a/src/test/resources/test-socketioconfig.xml b/src/test/resources/test-socketioconfig.xml index 844695c967c031e2b3900ce737c2ea0a5811944c..27d9112451ecdf6050c019e1e8d77cac279c2715 100644 --- a/src/test/resources/test-socketioconfig.xml +++ b/src/test/resources/test-socketioconfig.xml @@ -1,10 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" - xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="socketServer" class="de.thm.arsnova.socket.ARSnovaSocketIOServer" init-method="startServer" destroy-method="stopServer" scope="singleton"