Repository: kylin Updated Branches: refs/heads/2.0-rc e01e4b602 -> e8de0dc50
KYLIN-1219 support SSO with Spring SAML Project: http://git-wip-us.apache.org/repos/asf/kylin/repo Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/e8de0dc5 Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/e8de0dc5 Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/e8de0dc5 Branch: refs/heads/2.0-rc Commit: e8de0dc5098b81d466691078c3c3192f463eb538 Parents: e01e4b6 Author: shaofengshi <[email protected]> Authored: Fri Dec 11 17:32:18 2015 +0800 Committer: shaofengshi <[email protected]> Committed: Fri Dec 11 17:34:50 2015 +0800 ---------------------------------------------------------------------- build/bin/kylin.sh | 9 +- build/conf/kylin.properties | 37 +- .../test_case_data/localmeta/kylin.properties | 4 +- .../test_case_data/sandbox/kylin.properties | 4 +- pom.xml | 2 + server/pom.xml | 28 ++ .../security/KylinAuthenticationProvider.java | 101 +++++ .../kylin/rest/security/LdapProvider.java | 1 + .../rest/security/SAMLUserDetailsService.java | 32 ++ .../src/main/resources/applicationContext.xml | 6 +- .../resources/kylin-server-log4j.properties | 5 + server/src/main/resources/kylinSecurity.xml | 452 ++++++++++++++++--- 12 files changed, 586 insertions(+), 95 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/build/bin/kylin.sh ---------------------------------------------------------------------- diff --git a/build/bin/kylin.sh b/build/bin/kylin.sh index 9e91131..50c5ce2 100644 --- a/build/bin/kylin.sh +++ b/build/bin/kylin.sh @@ -49,11 +49,7 @@ then - useSandbox=`sh ${dir}/get-properties.sh kylin.sandbox` - spring_profile="default" - if [ "$useSandbox" = "true" ] - then spring_profile="sandbox" - fi + spring_profile=`sh ${dir}/get-properties.sh kylin.security.profile` #retrive $hive_dependency and $hbase_dependency source ${dir}/find-hive-dependency.sh @@ -64,7 +60,8 @@ then fi export HBASE_CLASSPATH_PREFIX=${tomcat_root}/bin/bootstrap.jar:${tomcat_root}/bin/tomcat-juli.jar:${tomcat_root}/lib/*:$HBASE_CLASSPATH_PREFIX - export HBASE_CLASSPATH=$hive_dependency:${HBASE_CLASSPATH} + mkdir -p ${KYLIN_HOME}/ext + export HBASE_CLASSPATH=$hive_dependency:${KYLIN_HOME}/lib/*:${KYLIN_HOME}/ext/*:${HBASE_CLASSPATH} #debug if encounter NoClassDefError #hbase classpath http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/build/conf/kylin.properties ---------------------------------------------------------------------- diff --git a/build/conf/kylin.properties b/build/conf/kylin.properties index cc91824..36f34a7 100644 --- a/build/conf/kylin.properties +++ b/build/conf/kylin.properties @@ -60,19 +60,42 @@ kylin.hbase.region.cut.small=5 kylin.hbase.region.cut.medium=10 kylin.hbase.region.cut.large=50 -## Config for Restful APP ## -# database connection settings: -ldap.server= + +## kylin security configurations + +# spring security profile, options: testing, ldap, saml +# with "testing" profile, user can use pre-defined name/pwd like KYLIN/ADMIN to login +kylin.security.profile=testing + +# default roles and admin roles in LDAP, for ldap and saml +acl.defaultRole=ROLE_ANALYST,ROLE_MODELER +acl.adminRole=ROLE_ADMIN + +#LDAP authentication configuration +ldap.server=ldap://ldap_server:389 ldap.username= ldap.password= + +#LDAP user account directory; ldap.user.searchBase= ldap.user.searchPattern= ldap.user.groupSearchBase= -ldap.service.searchBase=OU= + +#LDAP service account directory +ldap.service.searchBase= ldap.service.searchPattern= ldap.service.groupSearchBase= -acl.adminRole= -acl.defaultRole= + +#SAML configurations for SSO +# SAML IDP metadata file location +saml.metadata.file=classpath:sso_metadata.xml +saml.metadata.entityBaseURL=https://hostname/kylin +saml.context.scheme=https +saml.context.serverName=hostname +saml.context.serverPort=443 +saml.context.contextPath=/kylin + + ganglia.group= ganglia.port=8664 @@ -105,5 +128,5 @@ kylin.web.contact_mail= #env DEV|QA|PROD deploy.env=DEV -###########################config info for sandbox####################### +###########################deprecated configs####################### kylin.sandbox=true http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/examples/test_case_data/localmeta/kylin.properties ---------------------------------------------------------------------- diff --git a/examples/test_case_data/localmeta/kylin.properties b/examples/test_case_data/localmeta/kylin.properties index 48f01f5..a008494 100644 --- a/examples/test_case_data/localmeta/kylin.properties +++ b/examples/test_case_data/localmeta/kylin.properties @@ -40,7 +40,7 @@ kylin.job.yarn.app.rest.check.interval.seconds=10 kylin.hbase.default.compression.codec=gzip - +kylin.security.profile=testing ## Config for Restful APP ## # database connection settings: ldap.server= @@ -57,7 +57,5 @@ acl.defaultRole= ganglia.group= ganglia.port=8664 -###########################config info for sandbox####################### -kylin.sandbox=true http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/examples/test_case_data/sandbox/kylin.properties ---------------------------------------------------------------------- diff --git a/examples/test_case_data/sandbox/kylin.properties b/examples/test_case_data/sandbox/kylin.properties index 35e2927..a12bc40 100644 --- a/examples/test_case_data/sandbox/kylin.properties +++ b/examples/test_case_data/sandbox/kylin.properties @@ -51,6 +51,8 @@ kylin.job.yarn.app.rest.check.interval.seconds=10 #default compression codec for htable,snappy,lzo,gzip,lz4 kylin.hbase.default.compression.codec=gzip +kylin.security.profile=testing + ## Config for Restful APP ## # database connection settings: ldap.server= @@ -96,6 +98,4 @@ kylin.web.contact_mail= #env DEV|QA|PROD deploy.env=DEV -###########################config info for sandbox####################### -kylin.sandbox=true http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 31ab924..b91b8d9 100644 --- a/pom.xml +++ b/pom.xml @@ -92,6 +92,8 @@ <!-- REST Service --> <spring.framework.version>3.1.2.RELEASE</spring.framework.version> + <spring.framework.security.extensions.version>1.0.1.RELEASE</spring.framework.security.extensions.version> + <opensaml.version>2.6.1</opensaml.version> <spring.boot.version>1.2.7.RELEASE</spring.boot.version> <!-- Calcite Version --> http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/pom.xml ---------------------------------------------------------------------- diff --git a/server/pom.xml b/server/pom.xml index f2f9e32..515305d 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -204,6 +204,34 @@ <version>${spring.framework.version}</version> </dependency> <dependency> + <groupId>org.springframework.security.extensions</groupId> + <artifactId>spring-security-saml2-core</artifactId> + <version>${spring.framework.security.extensions.version}</version> + </dependency> + <dependency> + <groupId>org.opensaml</groupId> + <artifactId>opensaml</artifactId> + <version>${opensaml.version}</version> + <exclusions> + <exclusion> + <artifactId>xml-apis</artifactId> + <groupId>org.apache.xerces</groupId> + </exclusion> + <exclusion> + <artifactId>jcl-over-slf4j</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + <exclusion> + <artifactId>serializer</artifactId> + <groupId>org.apache.xerces</groupId> + </exclusion> + <exclusion> + <artifactId>log4j-over-slf4j</artifactId> + <groupId>org.slf4j</groupId> + </exclusion> + </exclusions> + </dependency> + <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.8.1</version> http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java b/server/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java new file mode 100644 index 0000000..be28bdd --- /dev/null +++ b/server/src/main/java/org/apache/kylin/rest/security/KylinAuthenticationProvider.java @@ -0,0 +1,101 @@ +package org.apache.kylin.rest.security; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; +import org.apache.kylin.rest.service.UserService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.saml.SAMLAuthenticationProvider; +import org.springframework.util.Assert; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * A wrapper class for the authentication provider; Will do something more for Kylin. + */ +public class KylinAuthenticationProvider implements AuthenticationProvider { + + private static final Logger logger = LoggerFactory.getLogger(KylinAuthenticationProvider.class); + + @Autowired + UserService userService; + + @Autowired + private CacheManager cacheManager; + + //Embedded authentication provider + private AuthenticationProvider authenticationProvider; + + MessageDigest md = null; + + public KylinAuthenticationProvider(AuthenticationProvider authenticationProvider) { + super(); + Assert.notNull(authenticationProvider, "The embedded authenticationProvider should not be null."); + this.authenticationProvider = authenticationProvider; + try { + md = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Failed to init Message Digest ", e); + } + } + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Authentication authed = null; + Cache userCache = cacheManager.getCache("UserCache"); + md.reset(); + byte[] hashKey = md.digest((authentication.getName() + authentication.getCredentials()).getBytes()); + String userKey = Arrays.toString(hashKey); + + Element authedUser = userCache.get(userKey); + if (null != authedUser) { + authed = (Authentication) authedUser.getObjectValue(); + SecurityContextHolder.getContext().setAuthentication(authed); + } else { + try { + authed = authenticationProvider.authenticate(authentication); + userCache.put(new Element(userKey, authed)); + } catch (AuthenticationException e) { + logger.error("Failed to auth user: " + authentication.getName(), e); + throw e; + } + + logger.debug("Authenticated user " + authed.toString()); + + UserDetails user = (UserDetails)authed.getDetails(); + Assert.notNull(user, "The UserDetail is null."); + + logger.debug("User authorities :" + user.getAuthorities()); + if (!userService.userExists(user.getUsername())) { + userService.createUser(user); + } else { + userService.updateUser(user); + } + } + + return authed; + } + + @Override + public boolean supports(Class<?> authentication) { + return authenticationProvider.supports(authentication); + } + + public AuthenticationProvider getAuthenticationProvider() { + return authenticationProvider; + } + + public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) { + this.authenticationProvider = authenticationProvider; + } + +} http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/java/org/apache/kylin/rest/security/LdapProvider.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/security/LdapProvider.java b/server/src/main/java/org/apache/kylin/rest/security/LdapProvider.java index b34a2bd..2dae90b 100644 --- a/server/src/main/java/org/apache/kylin/rest/security/LdapProvider.java +++ b/server/src/main/java/org/apache/kylin/rest/security/LdapProvider.java @@ -41,6 +41,7 @@ import org.springframework.security.ldap.userdetails.LdapAuthoritiesPopulator; /** * @author xduo + * @deprecated replaced by KylinAuthenticationProvider * */ public class LdapProvider extends LdapAuthenticationProvider { http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/java/org/apache/kylin/rest/security/SAMLUserDetailsService.java ---------------------------------------------------------------------- diff --git a/server/src/main/java/org/apache/kylin/rest/security/SAMLUserDetailsService.java b/server/src/main/java/org/apache/kylin/rest/security/SAMLUserDetailsService.java new file mode 100644 index 0000000..8d13805 --- /dev/null +++ b/server/src/main/java/org/apache/kylin/rest/security/SAMLUserDetailsService.java @@ -0,0 +1,32 @@ +package org.apache.kylin.rest.security; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.ldap.userdetails.LdapUserDetailsService; +import org.springframework.security.saml.SAMLCredential; + +/** + * An implementation of SAMLUserDetailsService by delegating the query to LdapUserDetailsService. + */ +public class SAMLUserDetailsService implements org.springframework.security.saml.userdetails.SAMLUserDetailsService { + + private static final Logger logger = LoggerFactory.getLogger(SAMLUserDetailsService.class); + private LdapUserDetailsService ldapUserDetailsService; + + public SAMLUserDetailsService(LdapUserDetailsService ldapUserDetailsService) { + this.ldapUserDetailsService = ldapUserDetailsService; + } + + @Override + public Object loadUserBySAML(SAMLCredential samlCredential) throws UsernameNotFoundException { + final String userEmail = samlCredential.getAttributeAsString("email"); + logger.debug("samlCredential.email:" + userEmail); + final String userName = userEmail.substring(0, userEmail.indexOf("@")); + + UserDetails userDetails = ldapUserDetailsService.loadUserByUsername(userName); + logger.debug("userDeail by search ldap with '" + userName + "' is: " + userDetails); + return userDetails; + } +} http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/resources/applicationContext.xml ---------------------------------------------------------------------- diff --git a/server/src/main/resources/applicationContext.xml b/server/src/main/resources/applicationContext.xml index a103b56..dd66070 100644 --- a/server/src/main/resources/applicationContext.xml +++ b/server/src/main/resources/applicationContext.xml @@ -85,19 +85,19 @@ <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cacheManager-ref="ehcache"/> - <beans profile="default"> + <beans profile="ldap,saml"> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml" p:shared="true"/> </beans> - <beans profile="sandbox,testing"> + <beans profile="testing"> <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache-test.xml" p:shared="true"/> </beans> <!-- hbase storage/global lock Config --> - <beans profile="default,sandbox"> + <beans profile="ldap,saml"> <bean id="aclHBaseStorage" class="org.apache.kylin.rest.security.RealAclHBaseStorage"/> <bean id="jobLock" class="org.apache.kylin.storage.hbase.util.ZookeeperJobLock"/> </beans> http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/resources/kylin-server-log4j.properties ---------------------------------------------------------------------- diff --git a/server/src/main/resources/kylin-server-log4j.properties b/server/src/main/resources/kylin-server-log4j.properties index a93627a..f4df80d 100644 --- a/server/src/main/resources/kylin-server-log4j.properties +++ b/server/src/main/resources/kylin-server-log4j.properties @@ -49,3 +49,8 @@ log4j.logger.org.apache.kylin.query=DEBUG, query log4j.logger.org.apache.kylin.rest.controller.JobController=DEBUG, job log4j.logger.org.apache.kylin.rest.service.JobService=DEBUG, job log4j.logger.org.apache.kylin.job=DEBUG, job + +#spring security config +log4j.logger.org.springframework.security=INFO,file +log4j.logger.org.opensaml=INFO,file +log4j.logger.PROTOCOL_MESSAGE=INFO,file http://git-wip-us.apache.org/repos/asf/kylin/blob/e8de0dc5/server/src/main/resources/kylinSecurity.xml ---------------------------------------------------------------------- diff --git a/server/src/main/resources/kylinSecurity.xml b/server/src/main/resources/kylinSecurity.xml index ee3c891..3b2125a 100644 --- a/server/src/main/resources/kylinSecurity.xml +++ b/server/src/main/resources/kylinSecurity.xml @@ -1,39 +1,16 @@ -<beans xmlns="http://www.springframework.org/schema/beans" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:scr="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:tx="http://www.springframework.org/schema/tx" + xmlns:scr="http://www.springframework.org/schema/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd http://www.springframework.org/schema/tx - http://www.springframework.org/schema/tx/spring-tx-3.1.xsd"> + http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <scr:global-method-security pre-post-annotations="enabled"> <scr:expression-handler ref="expressionHandler" /> </scr:global-method-security> - <scr:http auto-config="true" use-expressions="true"> - <scr:http-basic entry-point-ref="unauthorisedEntryPoint" /> - - <scr:intercept-url pattern="/api/user/authentication*/**" access="permitAll" /> - <scr:intercept-url pattern="/api/query*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/metadata*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/**/metrics" access="permitAll" /> - <scr:intercept-url pattern="/api/cache*/**" access="permitAll" /> - <scr:intercept-url pattern="/api/cubes/src/tables" access="hasAnyRole('ROLE_ANALYST')" /> - <scr:intercept-url pattern="/api/cubes*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/models*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/streaming*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/job*/**" access="isAuthenticated()" /> - <scr:intercept-url pattern="/api/admin/config" access="permitAll" /> - <scr:intercept-url pattern="/api/projects" access="permitAll" /> - <scr:intercept-url pattern="/api/admin*/**" access="hasRole('ROLE_ADMIN')" /> - <scr:intercept-url pattern="/api/**" access="isAuthenticated()" /> - - <scr:logout invalidate-session="true" delete-cookies="JSESSIONID" /> - <scr:session-management session-fixation-protection="newSession" /> - </scr:http> - - <!-- user auth --> - <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> - <!-- acl config --> <bean id="aclPermissionFactory" class="org.apache.kylin.rest.security.AclPermissionFactory" /> @@ -45,6 +22,12 @@ <constructor-arg ref="aclService" /> <property name="permissionFactory" ref="aclPermissionFactory" /> </bean> + + <bean id="ldapSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> + <constructor-arg value="${ldap.server}" /> + <property name="userDn" value="${ldap.username}" /> + <property name="password" value="${ldap.password}" /> + </bean> <bean id="aclAuthorizationStrategy" class="org.springframework.security.acls.domain.AclAuthorizationStrategyImpl"> @@ -70,72 +53,76 @@ <constructor-arg ref="auditLogger" /> </bean> - <beans profile="default"> - <bean id="ldapUserAuthProvider" class="org.apache.kylin.rest.security.LdapProvider"> + <beans profile="ldap"> + <bean id="kylinUserAuthProvider" class="org.apache.kylin.rest.security.KylinAuthenticationProvider"> <constructor-arg> - <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> - <constructor-arg ref="ldapSource" /> - <property name="userSearch"> - <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> - <constructor-arg index="0" value="${ldap.user.searchBase}" /> - <constructor-arg index="1" value="${ldap.user.searchPattern}" /> - <constructor-arg index="2" ref="ldapSource" /> + <bean id="ldapUserAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> + <constructor-arg> + <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> + <constructor-arg ref="ldapSource" /> + <property name="userSearch"> + <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> + <constructor-arg index="0" value="${ldap.user.searchBase}" /> + <constructor-arg index="1" value="${ldap.user.searchPattern}" /> + <constructor-arg index="2" ref="ldapSource" /> + </bean> + </property> </bean> - </property> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.apache.kylin.rest.security.AuthoritiesPopulator"> - <constructor-arg index="0" ref="ldapSource" /> - <constructor-arg index="1" value="${ldap.user.groupSearchBase}" /> - <constructor-arg index="2" value="${acl.adminRole}" /> - <constructor-arg index="3" value="${acl.defaultRole}" /> + </constructor-arg> + <constructor-arg> + <bean class="org.apache.kylin.rest.security.AuthoritiesPopulator"> + <constructor-arg index="0" ref="ldapSource" /> + <constructor-arg index="1" value="${ldap.user.groupSearchBase}" /> + <constructor-arg index="2" value="${acl.adminRole}" /> + <constructor-arg index="3" value="${acl.defaultRole}" /> + </bean> + </constructor-arg> </bean> </constructor-arg> </bean> - <bean id="ldapServiceAccountAuthProvider" class="org.apache.kylin.rest.security.LdapProvider"> + <bean id="kylinServiceAccountAuthProvider" class="org.apache.kylin.rest.security.KylinAuthenticationProvider"> <constructor-arg> - <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> - <constructor-arg ref="ldapSource" /> - <property name="userSearch"> - <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> - <constructor-arg index="0" value="${ldap.service.searchBase}" /> - <constructor-arg index="1" value="${ldap.service.searchPattern}" /> - <constructor-arg index="2" ref="ldapSource" /> + <bean id="ldapServiceAuthenticationProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider"> + <constructor-arg> + <bean class="org.springframework.security.ldap.authentication.BindAuthenticator"> + <constructor-arg ref="ldapSource" /> + <property name="userSearch"> + <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> + <constructor-arg index="0" value="${ldap.service.searchBase}" /> + <constructor-arg index="1" value="${ldap.service.searchPattern}" /> + <constructor-arg index="2" ref="ldapSource" /> + </bean> + </property> </bean> - </property> - </bean> - </constructor-arg> - <constructor-arg> - <bean class="org.apache.kylin.rest.security.AuthoritiesPopulator"> - <constructor-arg index="0" ref="ldapSource" /> - <constructor-arg index="1" value="${ldap.service.groupSearchBase}" /> - <constructor-arg index="2" value="${acl.adminRole}" /> - <constructor-arg index="3" value="${acl.defaultRole}" /> + </constructor-arg> + <constructor-arg> + <bean class="org.apache.kylin.rest.security.AuthoritiesPopulator"> + <constructor-arg index="0" ref="ldapSource" /> + <constructor-arg index="1" value="${ldap.service.groupSearchBase}" /> + <constructor-arg index="2" value="${acl.adminRole}" /> + <constructor-arg index="3" value="${acl.defaultRole}" /> + </bean> + </constructor-arg> </bean> </constructor-arg> </bean> - <scr:authentication-manager alias="authenticationManager"> + <scr:authentication-manager alias="ldapAuthenticationManager"> <!-- do user ldap auth --> - <scr:authentication-provider ref="ldapUserAuthProvider"></scr:authentication-provider> + <scr:authentication-provider ref="kylinUserAuthProvider"></scr:authentication-provider> <!-- do service account ldap auth --> - <scr:authentication-provider ref="ldapServiceAccountAuthProvider"></scr:authentication-provider> - - <!-- custom user provider <authentication-provider user-service-ref="userService"> <password-encoder ref="passwordEncoder" /> </authentication-provider> --> + <scr:authentication-provider ref="kylinServiceAccountAuthProvider"></scr:authentication-provider> </scr:authentication-manager> - <bean id="ldapSource" class="org.springframework.security.ldap.DefaultSpringSecurityContextSource"> - <constructor-arg value="${ldap.server}" /> - <property name="userDn" value="${ldap.username}" /> - <property name="password" value="${ldap.password}" /> - </bean> </beans> - <beans profile="sandbox,testing"> - <scr:authentication-manager alias="authenticationManager"> + <beans profile="testing"> + <!-- user auth --> + <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" /> + + <scr:authentication-manager alias="testingAuthenticationManager"> <scr:authentication-provider> <scr:user-service> <scr:user name="MODELER" password="$2a$10$Le5ernTeGNIARwMJsY0WaOLioNQdb0QD11DwjeyNqqNRp5NaDo2FG" authorities="ROLE_MODELER" /> @@ -146,4 +133,321 @@ </scr:authentication-provider> </scr:authentication-manager> </beans> + + <beans profile="testing,ldap"> + <scr:http auto-config="true" use-expressions="true"> + <scr:http-basic entry-point-ref="unauthorisedEntryPoint" /> + + <scr:intercept-url pattern="/api/user/authentication*/**" access="permitAll" /> + <scr:intercept-url pattern="/api/query*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/metadata*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/**/metrics" access="permitAll" /> + <scr:intercept-url pattern="/api/cache*/**" access="permitAll" /> + <scr:intercept-url pattern="/api/cubes/src/tables" access="hasAnyRole('ROLE_ANALYST')" /> + <scr:intercept-url pattern="/api/cubes*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/models*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/streaming*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/job*/**" access="isAuthenticated()" /> + <scr:intercept-url pattern="/api/admin/config" access="permitAll" /> + <scr:intercept-url pattern="/api/projects" access="permitAll" /> + <scr:intercept-url pattern="/api/admin*/**" access="hasRole('ROLE_ADMIN')" /> + <scr:intercept-url pattern="/api/**" access="isAuthenticated()" /> + + <scr:logout invalidate-session="true" delete-cookies="JSESSIONID" /> + <scr:session-management session-fixation-protection="newSession" /> + </scr:http> + </beans> + + <beans profile="saml"> + <!-- Enable auto-wiring --> + <context:annotation-config/> + + <!-- Scan for auto-wiring classes in spring saml packages --> + <context:component-scan base-package="org.springframework.security.saml"/> + + <!-- Unsecured pages --> + <scr:http security="none" pattern="/images/**"/> + <scr:http security="none" pattern="/css/**"/> + <scr:http security="none" pattern="/fonts/**"/> + <scr:http security="none" pattern="/js/**"/> + <scr:http security="none" pattern="/login/**"/> + <scr:http security="none" pattern="/api/projects" /> + + <!-- Secured pages with SAML as entry point --> + <scr:http entry-point-ref="samlEntryPoint" use-expressions="false"> + <scr:intercept-url pattern="/**" access="IS_AUTHENTICATED_FULLY"/> + <scr:custom-filter before="FIRST" ref="metadataGeneratorFilter"/> + <scr:custom-filter after="BASIC_AUTH_FILTER" ref="samlFilter"/> + </scr:http> + + <!-- Central storage of cryptographic keys --> + <bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager"> + <constructor-arg value="classpath:samlKeystore.jks"/> + <constructor-arg type="java.lang.String" value="changeit"/> + <constructor-arg> + <map> + <entry key="kylin" value="changeit"/> + </map> + </constructor-arg> + <constructor-arg type="java.lang.String" value="kylin"/> + </bean> + + <!-- Filters for processing of SAML messages --> + <bean id="samlFilter" class="org.springframework.security.web.FilterChainProxy"> + <scr:filter-chain-map request-matcher="ant"> + <scr:filter-chain pattern="/saml/login/**" filters="samlEntryPoint"/> + <scr:filter-chain pattern="/saml/logout/**" filters="samlLogoutFilter"/> + <scr:filter-chain pattern="/saml/metadata/**" filters="metadataDisplayFilter"/> + <scr:filter-chain pattern="/saml/SSO/**" filters="samlWebSSOProcessingFilter"/> + <scr:filter-chain pattern="/saml/SSOHoK/**" filters="samlWebSSOHoKProcessingFilter"/> + <scr:filter-chain pattern="/saml/SingleLogout/**" filters="samlLogoutProcessingFilter"/> + </scr:filter-chain-map> + </bean> + + <!-- Handler deciding where to redirect user after successful login --> + <bean id="successRedirectHandler" + class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler"> + <property name="defaultTargetUrl" value="/models"/> + </bean> + + <!-- Handler deciding where to redirect user after failed login --> + <bean id="failureRedirectHandler" + class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler"> + <property name="useForward" value="true"/> + <property name="defaultFailureUrl" value="/login"/> + </bean> + + <!-- Handler for successful logout --> + <bean id="successLogoutHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler"> + <property name="defaultTargetUrl" value="/login"/> + </bean> + + <scr:authentication-manager alias="samlAuthenticationManager"> + <!-- Register authentication manager for SAML provider --> + <scr:authentication-provider ref="kylinAuthenticationProvider"/> + </scr:authentication-manager> + + <!-- Logger for SAML messages and events --> + <bean id="samlLogger" class="org.springframework.security.saml.log.SAMLDefaultLogger"/> + + <!-- Filter automatically generates default SP metadata --> + <bean id="metadataGeneratorFilter" class="org.springframework.security.saml.metadata.MetadataGeneratorFilter"> + <constructor-arg> + <bean class="org.springframework.security.saml.metadata.MetadataGenerator"> + <property name="extendedMetadata"> + <bean class="org.springframework.security.saml.metadata.ExtendedMetadata"> + <property name="idpDiscoveryEnabled" value="false"/> + </bean> + </property> + <property name="entityBaseURL" value = "${saml.metadata.entityBaseURL}"/> + </bean> + </constructor-arg> + </bean> + + <!-- Entry point to initialize authentication, default values taken from properties file --> + <bean id="samlEntryPoint" class="org.springframework.security.saml.SAMLEntryPoint"> + <property name="defaultProfileOptions"> + <bean class="org.springframework.security.saml.websso.WebSSOProfileOptions"> + <property name="includeScoping" value="false"/> + </bean> + </property> + </bean> + + <!-- The filter is waiting for connections on URL suffixed with filterSuffix and presents SP metadata there --> + <bean id="metadataDisplayFilter" class="org.springframework.security.saml.metadata.MetadataDisplayFilter"/> + + <!-- IDP Metadata configuration - paths to metadata of IDPs in circle of trust is here --> + <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager"> + <constructor-arg> + <list> + <!-- Example of classpath metadata with Extended Metadata --> + <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate"> + <constructor-arg> + <bean class="org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider"> + <constructor-arg> + <value type="java.io.File">classpath:sso_metadata.xml</value> + </constructor-arg> + <property name="parserPool" ref="parserPool"/> + </bean> + </constructor-arg> + <constructor-arg> + <bean class="org.springframework.security.saml.metadata.ExtendedMetadata"> + </bean> + </constructor-arg> + <property name="metadataTrustCheck" value="false"/> + </bean> + </list> + </constructor-arg> + </bean> + + <bean id="ldapUserAuthoritiesPopulator" class="org.apache.kylin.rest.security.AuthoritiesPopulator"> + <constructor-arg index="0" ref="ldapSource" /> + <constructor-arg index="1" value="${ldap.user.groupSearchBase}" /> + <constructor-arg index="2" value="${acl.adminRole}" /> + <constructor-arg index="3" value="${acl.defaultRole}" /> + </bean> + + <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch"> + <constructor-arg index="0" value="${ldap.user.searchBase}" /> + <constructor-arg index="1" value="${ldap.user.searchPattern}" /> + <constructor-arg index="2" ref="ldapSource" /> + </bean> + + + <bean id="samlUserDetailsService" class="org.apache.kylin.rest.security.SAMLUserDetailsService"> + <constructor-arg> + <bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService"> + <constructor-arg ref="userSearch" /> + <constructor-arg ref="ldapUserAuthoritiesPopulator" /> + </bean> + </constructor-arg> + </bean> + + <bean id="kylinAuthenticationProvider" class="org.apache.kylin.rest.security.KylinAuthenticationProvider"> + <constructor-arg> + <!-- SAML Authentication Provider responsible for validating of received SAML messages --> + <bean id="samlAuthenticationProvider" class="org.springframework.security.saml.SAMLAuthenticationProvider"> + <!-- OPTIONAL property: can be used to store/load user data after login --> + <property name="userDetails" ref="samlUserDetailsService" /> + </bean> + </constructor-arg> + </bean> + + + <!-- Provider of default SAML Context --> + <!-- + <bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderImpl"/> + --> + + <!-- Provider of a SAML Context behind a LoadBanlancer or reverse proxy --> + <bean id="contextProvider" class="org.springframework.security.saml.context.SAMLContextProviderLB"> + <property name="scheme" value="${saml.context.scheme}"/> + <property name="serverName" value="${saml.context.serverName}"/> + <property name="serverPort" value="${saml.context.serverPort}"/> + <property name="includeServerPortInRequestURL" value="false"/> + <property name="contextPath" value="${saml.context.contextPath}"/> + </bean> + + + <!-- Processing filter for WebSSO profile messages --> + <bean id="samlWebSSOProcessingFilter" class="org.springframework.security.saml.SAMLProcessingFilter"> + <property name="authenticationManager" ref="samlAuthenticationManager"/> + <property name="authenticationSuccessHandler" ref="successRedirectHandler"/> + <property name="authenticationFailureHandler" ref="failureRedirectHandler"/> + </bean> + + <!-- Processing filter for WebSSO Holder-of-Key profile --> + <bean id="samlWebSSOHoKProcessingFilter" class="org.springframework.security.saml.SAMLWebSSOHoKProcessingFilter"> + <property name="authenticationManager" ref="samlAuthenticationManager"/> + <property name="authenticationSuccessHandler" ref="successRedirectHandler"/> + <property name="authenticationFailureHandler" ref="failureRedirectHandler"/> + </bean> + + <!-- Logout handler terminating local session --> + <bean id="logoutHandler" + class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"> + <property name="invalidateHttpSession" value="false"/> + </bean> + + <!-- Override default logout processing filter with the one processing SAML messages --> + <bean id="samlLogoutFilter" class="org.springframework.security.saml.SAMLLogoutFilter"> + <constructor-arg index="0" ref="successLogoutHandler"/> + <constructor-arg index="1" ref="logoutHandler"/> + <constructor-arg index="2" ref="logoutHandler"/> + </bean> + + <!-- Filter processing incoming logout messages --> + <!-- First argument determines URL user will be redirected to after successful global logout --> + <bean id="samlLogoutProcessingFilter" class="org.springframework.security.saml.SAMLLogoutProcessingFilter"> + <constructor-arg index="0" ref="successLogoutHandler"/> + <constructor-arg index="1" ref="logoutHandler"/> + </bean> + + <!-- Class loading incoming SAML messages from httpRequest stream --> + <bean id="processor" class="org.springframework.security.saml.processor.SAMLProcessorImpl"> + <constructor-arg> + <list> + <ref bean="redirectBinding"/> + <ref bean="postBinding"/> + <ref bean="artifactBinding"/> + <ref bean="soapBinding"/> + <ref bean="paosBinding"/> + </list> + </constructor-arg> + </bean> + + <!-- SAML 2.0 WebSSO Assertion Consumer --> + <bean id="webSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerImpl"/> + + <!-- SAML 2.0 Holder-of-Key WebSSO Assertion Consumer --> + <bean id="hokWebSSOprofileConsumer" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/> + + <!-- SAML 2.0 Web SSO profile --> + <bean id="webSSOprofile" class="org.springframework.security.saml.websso.WebSSOProfileImpl"/> + + <!-- SAML 2.0 Holder-of-Key Web SSO profile --> + <bean id="hokWebSSOProfile" class="org.springframework.security.saml.websso.WebSSOProfileConsumerHoKImpl"/> + + <!-- SAML 2.0 ECP profile --> + <bean id="ecpprofile" class="org.springframework.security.saml.websso.WebSSOProfileECPImpl"/> + + <!-- SAML 2.0 Logout Profile --> + <bean id="logoutprofile" class="org.springframework.security.saml.websso.SingleLogoutProfileImpl"/> + + <!-- Bindings, encoders and decoders used for creating and parsing messages --> + <bean id="postBinding" class="org.springframework.security.saml.processor.HTTPPostBinding"> + <constructor-arg ref="parserPool"/> + <constructor-arg ref="velocityEngine"/> + </bean> + + <bean id="redirectBinding" class="org.springframework.security.saml.processor.HTTPRedirectDeflateBinding"> + <constructor-arg ref="parserPool"/> + </bean> + + <bean id="artifactBinding" class="org.springframework.security.saml.processor.HTTPArtifactBinding"> + <constructor-arg ref="parserPool"/> + <constructor-arg ref="velocityEngine"/> + <constructor-arg> + <bean class="org.springframework.security.saml.websso.ArtifactResolutionProfileImpl"> + <constructor-arg> + <bean class="org.apache.commons.httpclient.HttpClient"> + <constructor-arg> + <bean class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager"/> + </constructor-arg> + </bean> + </constructor-arg> + <property name="processor"> + <bean class="org.springframework.security.saml.processor.SAMLProcessorImpl"> + <constructor-arg ref="soapBinding"/> + </bean> + </property> + </bean> + </constructor-arg> + </bean> + + <bean id="soapBinding" class="org.springframework.security.saml.processor.HTTPSOAP11Binding"> + <constructor-arg ref="parserPool"/> + </bean> + + <bean id="paosBinding" class="org.springframework.security.saml.processor.HTTPPAOS11Binding"> + <constructor-arg ref="parserPool"/> + </bean> + + <!-- Initialization of OpenSAML library--> + <bean class="org.springframework.security.saml.SAMLBootstrap"/> + + <!-- Initialization of the velocity engine --> + <bean id="velocityEngine" class="org.springframework.security.saml.util.VelocityFactory" factory-method="getEngine"/> + + <!-- XML parser pool needed for OpenSAML parsing --> + <bean id="parserPool" class="org.opensaml.xml.parse.StaticBasicParserPool" init-method="initialize"> + <property name="builderFeatures"> + <map> + <entry key="http://apache.org/xml/features/dom/defer-node-expansion" value="false"/> + </map> + </property> + </bean> + + <bean id="parserPoolHolder" class="org.springframework.security.saml.parser.ParserPoolHolder"/> + </beans> </beans> \ No newline at end of file
