diff --git a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java index 3adca2b75..a2d11a1ab 100644 --- a/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java +++ b/backend/src/main/java/edu/internet2/tier/shibboleth/admin/ui/security/service/UserService.java @@ -61,4 +61,13 @@ public User getCurrentUser() { } return user; } + + public Set getUserRoles(String username) { + Optional user = userRepository.findByUsername(username); + HashSet result = new HashSet<>(); + if (user.isPresent() ) { + user.get().getRoles().forEach(role -> result.add(role.getName())); + } + return result; + } } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java index aca51e6fc..f61b94aea 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/AddNewUserFilter.java @@ -9,6 +9,9 @@ import edu.internet2.tier.shibboleth.admin.ui.service.EmailService; import org.apache.commons.lang3.RandomStringUtils; +import org.pac4j.core.context.JEEContext; +import org.pac4j.core.context.session.JEESessionStore; +import org.pac4j.core.matching.matcher.Matcher; import org.pac4j.core.profile.CommonProfile; import org.pac4j.saml.profile.SAML2Profile; import org.slf4j.Logger; @@ -24,6 +27,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; @@ -40,12 +44,14 @@ public class AddNewUserFilter implements Filter { private RoleRepository roleRepository; private Pac4jConfigurationProperties.SAML2ProfileMapping saml2ProfileMapping; private UserRepository userRepository; + private Matcher matcher; - public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserRepository userRepository, RoleRepository roleRepository, Optional emailService) { + public AddNewUserFilter(Pac4jConfigurationProperties pac4jConfigurationProperties, UserRepository userRepository, RoleRepository roleRepository, Matcher matcher, Optional emailService) { this.userRepository = userRepository; this.roleRepository = roleRepository; this.emailService = emailService; this.pac4jConfigurationProperties = pac4jConfigurationProperties; + this.matcher = matcher; saml2ProfileMapping = this.pac4jConfigurationProperties.getSaml2ProfileMapping(); } @@ -73,6 +79,10 @@ public void destroy() { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + JEEContext context = new JEEContext((HttpServletRequest)request, (HttpServletResponse)response); + if (!matcher.matches(context, JEESessionStore.INSTANCE)) { + return; + } Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null) { CommonProfile profile = (CommonProfile) authentication.getPrincipal(); diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java index ac3f159c2..b73d7593d 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/Pac4jConfiguration.java @@ -16,15 +16,20 @@ import org.pac4j.saml.client.SAML2Client; import org.pac4j.saml.config.SAML2Configuration; import org.pac4j.saml.credentials.authenticator.SAML2Authenticator; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigureOrder; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.server.ErrorPage; +import org.springframework.boot.web.server.ErrorPageRegistrar; +import org.springframework.boot.web.server.ErrorPageRegistry; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpStatus; import com.google.common.collect.Lists; import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository; - +import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService; import lombok.extern.slf4j.Slf4j; /** @@ -38,6 +43,9 @@ public class Pac4jConfiguration { public final static String PAC4J_CLIENT_NAME = "shibUIAuthClient"; + @Autowired + private UserService userService; + /** * Custom class that ensures we add the user's roles to the information when doing SAML2 auth */ @@ -52,6 +60,11 @@ public Config config(final Pac4jConfigurationProperties pac4jConfigProps, log.info("**** Configuring PAC4J "); final Config config = new Config(); final Clients clients = new Clients(pac4jConfigProps.getCallbackUrl()); + + // configure the matcher for bypassing auth checks + PathMatcher pm = new PathMatcher(); + pm.setExcludedPaths(Lists.newArrayList("/favicon.ico", "/unsecured/**/*", "/assets/**/*.png", "/static/**/*")); + config.addMatcher("exclude-paths-matcher", pm); // Configure the client switch (pac4jConfigProps.getTypeOfAuth()) { @@ -99,20 +112,25 @@ public void validate(Credentials credentials, WebContext context, SessionStore s final CommonProfile profile = new CommonProfile(); String token = ((TokenCredentials)credentials).getToken(); profile.setId(token); + profile.setRoles(userService.getUserRoles(token)); credentials.setUserProfile(profile); } }); headerClient.setName(PAC4J_CLIENT_NAME); clients.setClients(headerClient); } - } - - // configure the matcher for bypassing auth checks - PathMatcher pm = new PathMatcher(); - pm.setExcludedPaths(Lists.newArrayList("/favicon.ico", "/unsecured/**/*", "/error", "/login", "/")); - config.addMatcher("exclude-paths-matcher", pm); - + } config.setClients(clients); return config; } + + @Bean + public ErrorPageRegistrar errorPageRegistrar() { + return this::registerErrorPages; + } + + private void registerErrorPages(ErrorPageRegistry registry) { + registry.addErrorPages(new ErrorPage(HttpStatus.UNAUTHORIZED, "/unsecured/error.html")); + registry.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/unsecured/error.html")); + } } diff --git a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java index d42206e3f..b16af86ba 100644 --- a/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java +++ b/pac4j-module/src/main/java/net/unicon/shibui/pac4j/WebSecurity.java @@ -7,6 +7,8 @@ import org.apache.commons.lang3.StringUtils; import org.pac4j.core.config.Config; +import org.pac4j.core.matching.matcher.Matcher; +import org.pac4j.core.matching.matcher.PathMatcher; import org.pac4j.springframework.security.web.CallbackFilter; import org.pac4j.springframework.security.web.SecurityFilter; import org.springframework.beans.factory.annotation.Value; @@ -60,23 +62,30 @@ public Pac4jWebSecurityConfigurerAdapter(final Config config, UserRepository use @Override protected void configure(HttpSecurity http) throws Exception { - http.antMatcher("/**"); - http.addFilterBefore(getFilter(config, pac4jConfigurationProperties.getTypeOfAuth()), BasicAuthenticationFilter.class); - http.addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userRepository, roleRepository, emailService), SecurityFilter.class); - http.authorizeRequests().anyRequest().fullyAuthenticated(); + http.authorizeRequests().antMatchers("/unsecured/**/*").permitAll(); + + // add filter based on auth type + http.antMatcher("/**").addFilterBefore(getFilter(config, pac4jConfigurationProperties.getTypeOfAuth()), BasicAuthenticationFilter.class); + + // add the new user filter + http.addFilterAfter(new AddNewUserFilter(pac4jConfigurationProperties, userRepository, roleRepository, getPathMatcher("exclude-paths-matcher") , emailService), SecurityFilter.class); + http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> response.sendRedirect("/unsecured/error.html")); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS); http.csrf().disable(); http.headers().frameOptions().disable(); } + private Matcher getPathMatcher(String name) { + return config.getMatchers().get(name); + } + private Filter getFilter(Config config2, String typeOfAuth) { switch (typeOfAuth) { case "SAML2": return new CallbackFilter(this.config); case "HEADER": - final SecurityFilter securityFilterForHeader = new SecurityFilter(this.config, - Pac4jConfiguration.PAC4J_CLIENT_NAME); + final SecurityFilter securityFilterForHeader = new SecurityFilter(this.config, Pac4jConfiguration.PAC4J_CLIENT_NAME); securityFilterForHeader.setMatchers("exclude-paths-matcher"); return securityFilterForHeader; } @@ -91,6 +100,9 @@ public void configure(org.springframework.security.config.annotation.web.builder firewall.setAllowUrlEncodedSlash(true); firewall.setAllowUrlEncodedDoubleSlash(true); web.httpFirewall(firewall); + + // These don't need to be secured + web.ignoring().antMatchers("/favicon.ico", "/unsecured/**/*", "/assets/**/*.png", "/static/**/*", "/**/*.css"); } } diff --git a/ui/public/unsecured/error.html b/ui/public/unsecured/error.html index 5535dc452..4d200efa8 100644 --- a/ui/public/unsecured/error.html +++ b/ui/public/unsecured/error.html @@ -6,7 +6,7 @@ - +