diff --git a/gui/admin-gui/pom.xml b/gui/admin-gui/pom.xml index 559156372b3..1fa667be097 100644 --- a/gui/admin-gui/pom.xml +++ b/gui/admin-gui/pom.xml @@ -220,6 +220,16 @@ 2.5.3 runtime + + org.springframework.security + spring-security-cas + ${spring.security.version} + + + org.jasig.cas.client + cas-client-core + 3.5.0 + org.webjars webjars-locator-core diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/CasSecurityConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/CasSecurityConfig.java index 5bacbbfe681..707715d4568 100644 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/CasSecurityConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/CasSecurityConfig.java @@ -16,8 +16,20 @@ package com.evolveum.midpoint.web.boot; +import org.apache.commons.lang3.StringUtils; +import org.jasig.cas.client.validation.TicketValidator; +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.security.authentication.AuthenticationProvider; +import org.springframework.security.cas.ServiceProperties; +import org.springframework.security.cas.authentication.CasAuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationEntryPoint; +import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper; +import org.springframework.security.core.userdetails.UserDetailsService; + +import java.lang.reflect.Constructor; /** * Created by Viliam Repan (lazyman). @@ -26,5 +38,51 @@ @Configuration public class CasSecurityConfig { - // TODO move configuration from ctx-web-security-cas.xml here + @Value("${auth.cas.midpoint.url}") + private String casMidpointUrl; + @Value("${auth.cas.server.url}") + private String casServerUrl; + @Value("${auth.cas.ticketValidator}") + private String ticketValidator; + + @Bean + public ServiceProperties serviceProperties() { + ServiceProperties properties = new ServiceProperties(); + properties.setService(casMidpointUrl + "/login/cas"); + properties.setSendRenew(false); + + return properties; + } + + @Bean + public CasAuthenticationEntryPoint authenticationEntryPoint() { + CasAuthenticationEntryPoint entryPoint = new CasAuthenticationEntryPoint(); + entryPoint.setLoginUrl(casServerUrl + "/login"); + entryPoint.setServiceProperties(serviceProperties()); + + return entryPoint; + } + + @Profile("cas") + @Bean + public AuthenticationProvider midPointAuthenticationProvider(UserDetailsService userDetailsService) throws Exception { + CasAuthenticationProvider provider = new CasAuthenticationProvider(); + provider.setAuthenticationUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService)); + provider.setServiceProperties(serviceProperties()); + provider.setTicketValidator(createTicketValidatorInstance()); + provider.setKey("CAS_ID"); + + return provider; + } + + private TicketValidator createTicketValidatorInstance() throws Exception { + if (!StringUtils.contains(ticketValidator, "\\.")) { + ticketValidator = "org.jasig.cas.client.validation." + ticketValidator; + } + + Class type = (Class) Class.forName(ticketValidator); + Constructor c = type.getConstructor(String.class); + + return c.newInstance(casServerUrl); + } } diff --git a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebSecurityConfig.java b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebSecurityConfig.java index fa207c7d5e6..d2e12f5a3f5 100755 --- a/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebSecurityConfig.java +++ b/gui/admin-gui/src/main/java/com/evolveum/midpoint/web/boot/WebSecurityConfig.java @@ -23,44 +23,58 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.security.SecurityProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; +import org.springframework.context.annotation.*; import org.springframework.core.annotation.Order; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.cas.web.CasAuthenticationFilter; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter; +import java.util.Arrays; + /** * Created by Viliam Repan (lazyman). */ @Order(SecurityProperties.BASIC_AUTH_ORDER - 1) @Configuration -//TODO -//@EnableGlobalMethodSecurity(securedEnabled = true) @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Autowired + private Environment environment; @Autowired private AuthenticationProvider authenticationProvider; + @Autowired + private AuthenticationManager authenticationManager; + @Autowired private MidPointGuiAuthorizationEvaluator accessDecisionManager; + @Value("${auth.sso.header:SM_USER}") + private String principalRequestHeader; + + @Value("${auth.cas.server.url:}") + private String casServerUrl; + @Value("${security.enable-csrf:true}") private boolean csrfEnabled; @Value("${auth.logout.url:/}") private String authLogoutUrl; - @Value("${auth.sso.header:SM_USER}") - private String principalRequestHeader; - + + @Profile("!cas") @Bean - public WicketLoginUrlAuthenticationEntryPoint wicketAuthenticationEntryPoint() { + public AuthenticationEntryPoint authenticationEntryPoint() { return new WicketLoginUrlAuthenticationEntryPoint("/login"); } @@ -71,27 +85,15 @@ public MidPointGuiAuthorizationEvaluator accessDecisionManager(SecurityEnforcer return new MidPointGuiAuthorizationEvaluator(securityEnforcer, securityContextManager, taskManager); } - @Profile("sso") - @Bean - public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() throws Exception { - RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); - filter.setPrincipalRequestHeader(principalRequestHeader); - filter.setAuthenticationManager(authenticationManager()); - - getHttp().addFilterBefore(filter, LogoutFilter.class); - - return filter; - } - @Override public void configure(WebSecurity web) throws Exception { - // Web (SOAP) services + // Web (SOAP) services web.ignoring().antMatchers("/model/**"); web.ignoring().antMatchers("/ws/**"); // REST service web.ignoring().antMatchers("/rest/**"); - + // Special intra-cluster service to download and delete report outputs web.ignoring().antMatchers("/report"); @@ -139,7 +141,7 @@ protected void configure(HttpSecurity http) throws Exception { .successHandler(authenticationSuccessHandler()).permitAll(); http.exceptionHandling() - .authenticationEntryPoint(wicketAuthenticationEntryPoint()) + .authenticationEntryPoint(authenticationEntryPoint()) .accessDeniedHandler(accessDeniedHandler()); if (!csrfEnabled) { @@ -147,6 +149,22 @@ protected void configure(HttpSecurity http) throws Exception { } http.headers().disable(); + + if (Arrays.stream(environment.getActiveProfiles()).anyMatch(p -> p.equalsIgnoreCase("cas"))) { + http.addFilterAt(casFilter(), CasAuthenticationFilter.class); + http.addFilterBefore(requestSingleLogoutFilter(), LogoutFilter.class); +// http.addFilterBefore(singleSignOutFilter(), CasAuthenticationFilter.class); + } + + if (Arrays.stream(environment.getActiveProfiles()).anyMatch(p -> p.equalsIgnoreCase("sso"))) { + http.addFilterBefore(requestHeaderAuthenticationFilter(), LogoutFilter.class); + } + } + + @Bean + @Override + protected AuthenticationManager authenticationManager() throws Exception { + return super.authenticationManager(); } @Bean @@ -154,9 +172,10 @@ public MidPointAccessDeniedHandler accessDeniedHandler() { return new MidPointAccessDeniedHandler(); } - @Profile({"!ldap", "!cas"}) + @Profile("default") + @Conditional(DefaultProfileOnlyCondition.class) @Bean - public AuthenticationProvider authenticationProvider() { + public AuthenticationProvider midPointAuthenticationProvider() throws Exception { return new MidPointAuthenticationProvider(); } @@ -181,5 +200,57 @@ public AuditedLogoutHandler logoutHandler() { return handler; } + + @Profile("sso") + @Bean + public RequestHeaderAuthenticationFilter requestHeaderAuthenticationFilter() { + RequestHeaderAuthenticationFilter filter = new RequestHeaderAuthenticationFilter(); + filter.setPrincipalRequestHeader(principalRequestHeader); + filter.setAuthenticationManager(authenticationManager); + + return filter; + } + + @Profile("cas") + @Bean + public CasAuthenticationFilter casFilter() { + CasAuthenticationFilter filter = new CasAuthenticationFilter(); + filter.setAuthenticationManager(authenticationManager); + + return filter; + } + + @Profile("cas") + @Bean + public LogoutFilter requestSingleLogoutFilter() { + LogoutFilter filter = new LogoutFilter(casServerUrl + "/logout", new SecurityContextLogoutHandler()); + filter.setFilterProcessesUrl("/j_spring_cas_security_logout"); + + return filter; + } + +// @Profile("cas") +// @Bean +// public SingleSignOutFilter singleSignOutFilter() { +// SingleSignOutFilter filter = new SingleSignOutFilter(); +// filter.setCasServerUrlPrefix(casServerUrl); +// +// return filter; +// } + + private static class DefaultProfileOnlyCondition implements Condition { + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + + if (context.getEnvironment() == null) { + return true; + } + + String[] activeProfiles = context.getEnvironment().getActiveProfiles(); + + return !Arrays.stream(activeProfiles).anyMatch(p -> p.equalsIgnoreCase("cas") || p.equalsIgnoreCase("ldap")); + } + } } diff --git a/gui/admin-gui/src/main/resources/application-cas.yml b/gui/admin-gui/src/main/resources/application-cas.yml deleted file mode 100644 index f232c5f74bc..00000000000 --- a/gui/admin-gui/src/main/resources/application-cas.yml +++ /dev/null @@ -1,8 +0,0 @@ -#auth: -# cas: -# midpoint: -# host: http://localhost:8080/midpoint -# send: -# renew: false -# server: -# host: http://localhost:9090/ \ No newline at end of file diff --git a/gui/admin-gui/src/main/resources/application-ldap.yml b/gui/admin-gui/src/main/resources/application-ldap.yml deleted file mode 100644 index 52f6a26e2f2..00000000000 --- a/gui/admin-gui/src/main/resources/application-ldap.yml +++ /dev/null @@ -1,11 +0,0 @@ -#auth: -# ldap: -# host: ldap://localhost:389/dc=example,dc=com -# manager: cn=admin,dc=example,dc=com -# password: secret -# dn: -# pattern: uid={0},ou=people -# -# search: -# pattern: (uid={0}) -# subtree: true \ No newline at end of file diff --git a/gui/admin-gui/src/main/resources/application.yml b/gui/admin-gui/src/main/resources/application.yml index d398a995880..03cf6fdb5c5 100644 --- a/gui/admin-gui/src/main/resources/application.yml +++ b/gui/admin-gui/src/main/resources/application.yml @@ -20,7 +20,27 @@ server: auth: logout: url: / # NOTE: This URL is relative to application root - +## Example SSO header authentication configuration +# sso: +# header: SM_USER +## Example CAS SSO configuration +# cas: +# midpoint: +# url: http://localhost:38080/midpoint +# server: +# url: http://localhost:8080/cas +# ticketValidator: Cas30ServiceTicketValidator +## Example LDAP authentication configuration +# ldap: +# host: ldap://localhost:389/dc=example,dc=com +# manager: cn=admin,dc=example,dc=com +# password: secret +# dn: +# pattern: uid={0},ou=people +# +# search: +# pattern: (uid={0}) +# subtree: true #security: # enable-csrf: false # default for midpoint is true