Repository: metron Updated Branches: refs/heads/feature/METRON-1663-knoxsso 28f4b5704 -> 54880ba8f
http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationProvider.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationProvider.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationProvider.java new file mode 100644 index 0000000..42d8a2d --- /dev/null +++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationProvider.java @@ -0,0 +1,60 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; + +public class MetronAuthenticationProvider implements AuthenticationProvider { + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + if (authentication != null) { + authentication = getSSOAuthentication(authentication); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } + } + throw new MetronAuthenticationException("Authentication failed"); + } + + private Authentication getSSOAuthentication(Authentication authentication) { + return authentication; + } + + @Override + public boolean supports(Class<?> authentication) { + return true; + } + + public static List<GrantedAuthority> getAuthoritiesFromUGI(String userName) { + // TODO - if we have ldap, we can lookup groups for this user + + // TODO - if we have a default mapper we can use that + + List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>(); + grantedAuths.add(new SimpleGrantedAuthority("USER")); + return grantedAuths; + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronSecurityConfig.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronSecurityConfig.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronSecurityConfig.java new file mode 100644 index 0000000..7d3ec3b --- /dev/null +++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronSecurityConfig.java @@ -0,0 +1,188 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.interfaces.RSAPublicKey; +import java.util.List; + +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.http.HttpMethod; +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.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.password.NoOpPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +@SuppressWarnings("deprecation") +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) +public class MetronSecurityConfig extends WebSecurityConfigurerAdapter { + + private static final Logger LOG = LoggerFactory.getLogger(MetronSecurityConfig.class); + + @Value("${ldap.provider.url}") + private String providerUrl; + + @Value("${ldap.provider.userdn}") + private String providerUserDn; + + @Value("${ldap.provider.password}") + private String providerPassword; + + @Value("${ldap.user.dn.patterns}") + private String userDnPatterns; + + @Value("${ldap.user.passwordAttribute}") + private String passwordAttribute; + + @Value("${ldap.user.searchBase}") + private String userSearchBase; + + @Value("${ldap.user.searchFilter}") + private String userSearchFilter; + + @Value("${ldap.group.searchBase}") + private String groupSearchBase; + + @Value("${ldap.group.roleAttribute}") + private String groupRoleAttribute; + + @Value("${ldap.group.searchFilter}") + private String groupSearchFilter; + + @Value("${knox.sso.pubkeyFile:}") + private Path knoxKeyFile; + + @Value("${knox.sso.pubkey:}") + private String knoxKeyString; + + @Value("${knox.sso.url}") + private String knoxUrl; + + @Value("${knox.sso.cookie:hadoop-jwt}") + private String knoxCookie; + + @Value("${knox.sso.originalUrl:originalUrl}") + private String knoxOriginalUrl; + + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .authorizeRequests().antMatchers(HttpMethod.OPTIONS,"/**").permitAll().and() + .authorizeRequests().anyRequest().fullyAuthenticated() + .and() + .httpBasic() + .and() + .logout().disable(); + // @formatter:on + + // allow form based login if knox sso not in use + if (knoxUrl == null || knoxUrl.isEmpty()) { + http.formLogin(); + } + + http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER).sessionFixation(); + if (this.knoxUrl != null && !this.knoxUrl.isEmpty()) { + http.addFilterAt(ssoAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); + } + http.headers().disable(); + http.csrf().disable(); + } + + private KnoxSSOAuthenticationFilter ssoAuthenticationFilter() throws Exception { + String knoxKey; + if ((this.knoxKeyString == null || this.knoxKeyString.isEmpty()) && this.knoxKeyFile != null) { + List<String> keyLines = Files.readAllLines(knoxKeyFile, StandardCharsets.UTF_8); + if (keyLines != null) { + knoxKey = String.join("", keyLines); + } else { + knoxKey = ""; + } + } else { + knoxKey = this.knoxKeyString; + } + try { + RSAPublicKey parseRSAPublicKey = KnoxSSOAuthenticationFilter.parseRSAPublicKey(knoxKey); + return new KnoxSSOAuthenticationFilter(authenticationProvider(), knoxUrl, knoxCookie, knoxOriginalUrl, + parseRSAPublicKey); + } catch (Exception e) { + LOG.error("Cannot parse public key for KnoxSSO, please include the PEM string without certificate headers", + e); + throw (e); + } + } + + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + LOG.debug("Setting up LDAP authentication against %s", providerUrl); + // @formatter:off + if(this.providerUrl != null && !this.providerUrl.isEmpty()) { + auth.ldapAuthentication() + .userDnPatterns(userDnPatterns) + .userSearchBase(userSearchBase) + .userSearchFilter(userSearchFilter) + .groupRoleAttribute(groupRoleAttribute) + .groupSearchFilter(groupSearchFilter) + .groupSearchBase(groupSearchBase) + .contextSource() + .url(providerUrl) + .managerDn(providerUserDn) + .managerPassword(providerPassword) + .and() + .passwordCompare() + .passwordEncoder(passwordEncoder()) + .passwordAttribute(passwordAttribute); + } + // @formatter:on + try { + auth + .authenticationProvider(authenticationProvider()); + } catch (Exception e){ + LOG.error("Cannot setup authentication", e); + } + auth.userDetailsService(userDetailsService()); + } + + @Bean + public MetronAuthenticationProvider authenticationProvider() { + return new MetronAuthenticationProvider(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + // this currently uses plaintext passwords, which is not ideal + // TODO replace with a delegating encoder which runs through the good algos, or + // a config option based on the strength of passwords in the ldap store + + return NoOpPasswordEncoder.getInstance(); + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java new file mode 100644 index 0000000..a41ea7e --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java @@ -0,0 +1,164 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import java.io.File; +import java.util.HashSet; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.directory.server.core.DefaultDirectoryService; +import org.apache.directory.server.core.entry.ServerEntry; +import org.apache.directory.server.core.partition.Partition; +import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmIndex; +import org.apache.directory.server.core.partition.impl.btree.jdbm.JdbmPartition; +import org.apache.directory.server.ldap.LdapService; +import org.apache.directory.server.protocol.shared.SocketAcceptor; +import org.apache.directory.server.protocol.shared.store.LdifFileLoader; +import org.apache.directory.server.xdbm.Index; +import org.junit.Rule; +import org.junit.rules.TemporaryFolder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * A Bean based wrapper for an Embedded Apache Directory Server used to back + * LDAP Authentication. + */ +@Component +public class EmbeddedLdap implements InitializingBean, DisposableBean { + + public static final String EMBEDDED_LDAP_PROFILE = "embedded-ldap"; + private Logger LOG = LoggerFactory.getLogger(this.getClass()); + + @Value("${ldap.provider.url}") + private String providerUrl; + + @Value("${ldap.provider.userdn}") + private String providerUserDn; + + @Value("${ldap.provider.password}") + private String providerPassword; + + @Value("${ldap.user.dn.patterns}") + private String userDnPatterns; + + @Value("${ldap.user.passwordAttribute}") + private String passwordAttribute; + + @Value("${ldap.user.searchBase}") + private String userSearchBase; + + @Value("${ldap.user.searchFilter}") + private String userSearchFilter; + + @Value("${ldap.group.searchBase}") + private String groupSearchBase; + + @Value("${ldap.group.roleAttribute}") + private String groupRoleAttribute; + + @Value("${ldap.group.searchFilter}") + private String groupSearchFilter; + + @Rule + public TemporaryFolder workdir = new TemporaryFolder(); + + private LdapService ldapService; + + private DefaultDirectoryService directoryService; + + private Partition partition; + + @Override + public void destroy() throws Exception { + LOG.info("Stopping embedded LDAP"); + + ldapService.stop(); + directoryService.shutdown(); + + workdir.delete(); + } + + @Override + public void afterPropertiesSet() throws Exception { + workdir.create(); + + LOG.info("Starting embedded LDAP"); + LOG.debug("Using temporary directory %s", workdir.toString()); + + directoryService = new DefaultDirectoryService(); + directoryService.setWorkingDirectory(workdir.getRoot()); + directoryService.getChangeLog().setEnabled(false); + + partition = addPartition("testPartition", "dc=org"); + addIndex("objectClass", "ou", "uid", "cn"); + + SocketAcceptor socketAcceptor = new SocketAcceptor(null); + + Pattern p = Pattern.compile("ldaps?://([^:]*):(\\d*).*"); + Matcher m = p.matcher(providerUrl); + int port; + if (m.matches()) { + port = Integer.parseInt(m.group(2)); + } else { + port = 33389; + } + + ldapService = new LdapService(); + ldapService.setIpPort(port); + ldapService.setSearchBaseDn(userSearchBase); + ldapService.setDirectoryService(directoryService); + ldapService.setSocketAcceptor(socketAcceptor); + + directoryService.startup(); + ldapService.start(); + + // load default schema + applyLdif(new File("schema.ldif")); + LOG.debug("LDAP server started"); + } + + private Partition addPartition(String partitionId, String partitionDn) throws Exception { + Partition partition = new JdbmPartition(); + partition.setId(partitionId); + partition.setSuffix(partitionDn); + directoryService.addPartition(partition); + + return partition; + } + + public void addIndex(String... attrs) { + HashSet<Index<?, ServerEntry>> indexedAttributes = new HashSet<Index<?, ServerEntry>>(); + + for (String attribute : attrs) { + indexedAttributes.add(new JdbmIndex<String, ServerEntry>(attribute)); + } + + ((JdbmPartition) partition).setIndexedAttributes(indexedAttributes); + } + + public void applyLdif(File ldifFile) throws Exception { + new LdifFileLoader(directoryService.getAdminSession(), ldifFile, null, this.getClass().getClassLoader()) + .execute(); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java new file mode 100644 index 0000000..0b3015b --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java @@ -0,0 +1,170 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; + +import javax.servlet.ServletException; +import javax.servlet.http.Cookie; + +import org.junit.Test; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.Payload; +import com.nimbusds.jose.crypto.RSASSASigner; + +import net.minidev.json.JSONObject; + +public class JWTTests { + private static final String COOKIE_NAME = "hadoop-jwt"; + private static final String knoxUrl = "https://localhost:8443/gateway/default/knoxsso"; + + private static final Payload DEFAULT_PAYLOAD = new Payload("{ \"sub\": \"test\" }"); + + @Test + public void testValidJWT() throws Exception { + KeyPair key = createKey(); + requestThatSucceeds(tokenWithKey((RSAPrivateKey) key.getPrivate(),DEFAULT_PAYLOAD), key); + } + + @Test + public void testInvalidJWT() throws Exception { + KeyPair key = createKey(); + KeyPair badKey = createKey(); + assertFalse(key.equals(badKey)); + requestThatFails(tokenWithKey((RSAPrivateKey) badKey.getPrivate(),DEFAULT_PAYLOAD), key); + } + + @Test() + public void testExpiredJWT() throws Exception { + Date date = new Date(); + KeyPair key = createKey(); + + JSONObject json = new JSONObject(); + json.appendField("sub", "test"); + json.appendField("exp", (date.getTime() - 60000) / 1000); + + Payload payload = new Payload(json); + JWSObject token = tokenWithKey((RSAPrivateKey) key.getPrivate(), payload); + + requestThatFails(token, key); + } + + @Test() + public void testNotYetJWT() throws Exception { + Date date = new Date(); + KeyPair key = createKey(); + + JSONObject json = new JSONObject(); + json.appendField("sub", "test"); + json.appendField("exp", (date.getTime() + 60000) / 1000); + json.appendField("nbf", (date.getTime() + 30000) / 1000); + + Payload payload = new Payload(json); + JWSObject token = tokenWithKey((RSAPrivateKey) key.getPrivate(), payload); + + requestThatFails(token, key); + } + + @Test() + public void testCorrectTimeWindowJWT() throws Exception { + Date date = new Date(); + KeyPair key = createKey(); + + JSONObject json = new JSONObject(); + json.appendField("sub", "test"); + json.appendField("exp", (date.getTime() + 60000) / 1000); + json.appendField("nbf", (date.getTime() - 30000) / 1000); + + Payload payload = new Payload(json); + JWSObject token = tokenWithKey((RSAPrivateKey) key.getPrivate(), payload); + + requestThatSucceeds(token, key); + } + + private void requestThatSucceeds(JWSObject token, KeyPair key) throws IOException, ServletException { + MockHttpServletRequest request = requestWithJWT(token); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + + MetronAuthenticationProvider authenticationProvider = new MetronAuthenticationProvider(); + KnoxSSOAuthenticationFilter knoxSSOAuthenticationFilter = new KnoxSSOAuthenticationFilter( + authenticationProvider, knoxUrl, null, null, (RSAPublicKey) key.getPublic()); + + knoxSSOAuthenticationFilter.doFilter(request, response, chain); + + // ensure that the filter has passed a successful authentication context + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + assertNotNull("Authentication object is set", authentication); + assertEquals("test", ((User) authentication.getPrincipal()).getUsername()); + } + + private void requestThatFails(JWSObject token, KeyPair key) throws IOException, ServletException { + MockHttpServletRequest request = requestWithJWT(token); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); + + MetronAuthenticationProvider authenticationProvider = new MetronAuthenticationProvider(); + KnoxSSOAuthenticationFilter knoxSSOAuthenticationFilter = new KnoxSSOAuthenticationFilter( + authenticationProvider, knoxUrl, null, null, (RSAPublicKey) key.getPublic()); + + knoxSSOAuthenticationFilter.doFilter(request, response, chain); + + assertRedirectedToKnox(response); + } + + private KeyPair createKey() throws Exception { + return KeyPairGenerator.getInstance("RSA").generateKeyPair(); + } + + private JWSObject tokenWithKey(RSAPrivateKey key, Payload payload) throws JOSEException { + JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256), payload); + jwsObject.sign(new RSASSASigner(key)); + return jwsObject; + } + + private MockHttpServletRequest requestWithJWT(JWSObject jwt) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(new Cookie(COOKIE_NAME, jwt.serialize())); + return request; + } + + private static void assertRedirectedToKnox(MockHttpServletResponse response) { + assertTrue("Reponse is redirect to SSO", response.getHeader("Location").startsWith(knoxUrl)); + } + +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java new file mode 100644 index 0000000..efaa62c --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java @@ -0,0 +1,66 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.cert.CertificateException; +import java.security.interfaces.RSAPublicKey; +import java.util.Date; + +import javax.servlet.ServletException; + +import org.junit.Test; +import org.springframework.stereotype.Component; + +import com.nimbusds.jose.util.IOUtils; + +@Component +public class KnoxSSOAuthenticationFilterTests { + + @Test + public void testParsePemWithHeaders() throws CertificateException, ServletException, IOException { + RSAPublicKey parseRSAPublicKey = KnoxSSOAuthenticationFilter + .parseRSAPublicKey(readFile("org/apache/metron/ui/headers.pem")); + assertNotNull(parseRSAPublicKey); + } + + @Test + public void testParsePemWithoutHeaders() throws CertificateException, ServletException, IOException { + RSAPublicKey parseRSAPublicKey = KnoxSSOAuthenticationFilter.parseRSAPublicKey(readFile("org/apache/metron/ui/noheaders.pem")); + assertNotNull(parseRSAPublicKey); + } + + @Test(expected = ServletException.class) + public void testInvalidPem() throws CertificateException, ServletException, IOException { + @SuppressWarnings("unused") + RSAPublicKey parseRSAPublicKey = KnoxSSOAuthenticationFilter.parseRSAPublicKey(readFile("org/apache/metron/ui/invalid.pem")); + fail(); + } + + private String readFile(String file) throws IOException { + ClassLoader cl = this.getClass().getClassLoader(); + try (InputStream resourceAsStream = cl.getResourceAsStream(file)) { + return IOUtils.readInputStreamToString(resourceAsStream, Charset.defaultCharset()); + } + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/MetronAuthenticationProviderTests.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/MetronAuthenticationProviderTests.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/MetronAuthenticationProviderTests.java new file mode 100644 index 0000000..eba5341 --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/MetronAuthenticationProviderTests.java @@ -0,0 +1,33 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui; + +import org.junit.Before; + +public class MetronAuthenticationProviderTests { + private MetronAuthenticationProvider authenticationProvider; + + @Before + public void setup() { + authenticationProvider = new MetronAuthenticationProvider(); + } + + public void testGroupsFromUGI() { + + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java new file mode 100644 index 0000000..a47c74a --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.metron.ui.config; + +import org.apache.metron.ui.EmbeddedLdap; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class TestSecurityConfig { + + @Bean + public EmbeddedLdap embeddedLdap() { + return new EmbeddedLdap(); + } +} http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/resources/application-embedded-ldap.yml ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/resources/application-embedded-ldap.yml b/metron-interface/metron-ui-security/src/test/resources/application-embedded-ldap.yml new file mode 100644 index 0000000..5c4d7e2 --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/resources/application-embedded-ldap.yml @@ -0,0 +1,35 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +spring: + logging: + level: + root: debug + +ldap: + provider: + url: ldap://localhost:33389 + userdn: uid=admin,ou=people,dc=metron,dc=apache,dc=org + password: password + user: + dn.patterns: uid={0},ou=people,dc=metron,dc=apache,dc=org + passwordAttribute: userPassword + searchBase: ou=people,dc=metron,dc=apache,dc=org + searchFilter: "" + group: + searchBase: ou=groups,dc=metron,dc=apache,dc=org + searchFilter: "member={0}" + roleAttribute: "cn" http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/headers.pem ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/headers.pem b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/headers.pem new file mode 100644 index 0000000..7153bb4 --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/headers.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZBcGFjaGUxGDAWBgNVBAMMD0NBIGludGVy +bWVkaWF0ZTAeFw0xODA3MTExOTA0NTRaFw0yODEwMTYxOTA0NTRaMDsxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGQXBhY2hlMQ4wDAYDVQQDDAVu +b2RlMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6j8MIkAi4hZXd8 +rD+lmcQ6SmmUX8DrWKOu3cywUTcUR6ZfJQ+Lt0kZH2yDOPhpmr5TIEx7aTzAmloz +ZUVoArcoqjGan7Np5Hy2vC1rDeqMbueQ7m4LSpwFRzqb9ZnFycq+U1Jo5nrHwVdy +xfvo5yOYOgyWQT/upEsStiR0ADjyLPzTVQlErdAAVxKbRHF3ikWSvHzu8yoKcWAG +n7CbShuOF0bIMOR9e7GtlSQH6JMxH17oEU98KiVRvJV52RKHpHZpPfDb36YvsgTy +7ZczDmCQPNpU9hfV+vcgZEXFyrpxLgG7pHJBXPXWv8cw9rQLLim0297LNRpGAAz2 +Gc2todECAwEAAaOBrDCBqTAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAz +BglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmlj +YXRlMB0GA1UdDgQWBBQWMdyJLWA4vgE90pAuRa4/z4S4kDAOBgNVHQ8BAf8EBAMC +BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEAYDVR0RBAkwB4IFbm9kZTEwDQYJKoZI +hvcNAQELBQADggIBAMdFhC4xDxGs7i/gKhgBCv1JicNqk6Y2OQsS8ohk64ansx1r +uU0Rbx/pOsuD+d3ZuYeBaOYnHSLhExCcSxFjUlBkjS7aEigMxlHf2D+tYOgdcwlc +SjMaqyFDke+mR0rm8I15HviLjjZy1bDLnb8UsozLtdU040/MAtx9uF7SqvYUsTqy +alyfPeYZGpHZiFAmTcZ33uF3EByaSLACMVje0O1C9Xi/1v5Smp64NF15EF2DlHIv +TAj88oG7eEivVWie41mx8s/8WpR6XE3WFuZSc+j4qndtzwvmzlaO/e/v64ZzTPTL +SnrV424gtfZahjCb7+rSLQnSZShPeQessa1uF00xkCwlXuA7WXP9dAtOycySRsI+ +qy7vwD9Y5ZkZwFK8+8UnvySwwCSEHmy4zM0irA/XIKIRw7ahU3rxbkHgVCGh6Pyu +kGfv/+Wy9yW461w0aYUTMrUrS429CBDY0ek3T9eQ5bieJRjOYOl/uuPH+L4VSCOS +p2WIuXqqDMXqmxMUFNuaLYEg4Y51aLD0lkB+SH+tnOP5CZdufIKZRQhYiC+xcs2E +2/VvbqjAMe9vzF6d7a5EqbTkdS9k8CNnmxCfN+FlSl/iqUI3HKLVxNs+2Sux+Dhl +Nkt9qMcG2Gj0TxlqU43HrGeruVIxgC6Lj/QcIrc3Ddb1u7dccuNtF5UoqnVD +-----END CERTIFICATE----- \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/invalid.pem ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/invalid.pem b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/invalid.pem new file mode 100644 index 0000000..ffcd1bc --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/invalid.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +XIIEqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZBcGFjaGUxGDAWBgNVBAMMD0NBIGludGVy +bWVkaWF0ZTAeFw0xODA3MTExOTA0NTRaFw0yODEwMTYxOTA0NTRaMDsxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGQXBhY2hlMQ4wDAYDVQQDDAVu +b2RlMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6j8MIkAi4hZXd8 +rD+lmcQ6SmmUX8DrWKOu3cywUTcUR6ZfJQ+Lt0kZH2yDOPhpmr5TIEx7aTzAmloz +ZUVoArcoqjGan7Np5Hy2vC1rDeqMbueQ7m4LSpwFRzqb9ZnFycq+U1Jo5nrHwVdy +xfvo5yOYOgyWQT/upEsStiR0ADjyLPzTVQlErdAAVxKbRHF3ikWSvHzu8yoKcWAG +n7CbShuOF0bIMOR9e7GtlSQH6JMxH17oEU98KiVRvJV52RKHpHZpPfDb36YvsgTy +7ZczDmCQPNpU9hfV+vcgZEXFyrpxLgG7pHJBXPXWv8cw9rQLLim0297LNRpGAAz2 +Gc2todECAwEAAaOBrDCBqTAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAz +BglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmlj +YXRlMB0GA1UdDgQWBBQWMdyJLWA4vgE90pAuRa4/z4S4kDAOBgNVHQ8BAf8EBAMC +BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEAYDVR0RBAkwB4IFbm9kZTEwDQYJKoZI +hvcNAQELBQADggIBAMdFhC4xDxGs7i/gKhgBCv1JicNqk6Y2OQsS8ohk64ansx1r +uU0Rbx/pOsuD+d3ZuYeBaOYnHSLhExCcSxFjUlBkjS7aEigMxlHf2D+tYOgdcwlc +SjMaqyFDke+mR0rm8I15HviLjjZy1bDLnb8UsozLtdU040/MAtx9uF7SqvYUsTqy +alyfPeYZGpHZiFAmTcZ33uF3EByaSLACMVje0O1C9Xi/1v5Smp64NF15EF2DlHIv +TAj88oG7eEivVWie41mx8s/8WpR6XE3WFuZSc+j4qndtzwvmzlaO/e/v64ZzTPTL +SnrV424gtfZahjCb7+rSLQnSZShPeQessa1uF00xkCwlXuA7WXP9dAtOycySRsI+ +qy7vwD9Y5ZkZwFK8+8UnvySwwCSEHmy4zM0irA/XIKIRw7ahU3rxbkHgVCGh6Pyu +kGfv/+Wy9yW461w0aYUTMrUrS429CBDY0ek3T9eQ5bieJRjOYOl/uuPH+L4VSCOS +p2WIuXqqDMXqmxMUFNuaLYEg4Y51aLD0lkB+SH+tnOP5CZdufIKZRQhYiC+xcs2E +2/VvbqjAMe9vzF6d7a5EqbTkdS9k8CNnmxCfN+FlSl/iqUI3HKLVxNs+2Sux+Dhl +Nkt9qMcG2Gj0TxlqU43HrGeruVIxgC6Lj/QcIrc3Ddb1u7dccuNtF5UoqnVD +-----END CERTIFICATE----- \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/noheaders.pem ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/noheaders.pem b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/noheaders.pem new file mode 100644 index 0000000..267decf --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/noheaders.pem @@ -0,0 +1,25 @@ +MIIEqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMx +CzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZBcGFjaGUxGDAWBgNVBAMMD0NBIGludGVy +bWVkaWF0ZTAeFw0xODA3MTExOTA0NTRaFw0yODEwMTYxOTA0NTRaMDsxCzAJBgNV +BAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGQXBhY2hlMQ4wDAYDVQQDDAVu +b2RlMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6j8MIkAi4hZXd8 +rD+lmcQ6SmmUX8DrWKOu3cywUTcUR6ZfJQ+Lt0kZH2yDOPhpmr5TIEx7aTzAmloz +ZUVoArcoqjGan7Np5Hy2vC1rDeqMbueQ7m4LSpwFRzqb9ZnFycq+U1Jo5nrHwVdy +xfvo5yOYOgyWQT/upEsStiR0ADjyLPzTVQlErdAAVxKbRHF3ikWSvHzu8yoKcWAG +n7CbShuOF0bIMOR9e7GtlSQH6JMxH17oEU98KiVRvJV52RKHpHZpPfDb36YvsgTy +7ZczDmCQPNpU9hfV+vcgZEXFyrpxLgG7pHJBXPXWv8cw9rQLLim0297LNRpGAAz2 +Gc2todECAwEAAaOBrDCBqTAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIGQDAz +BglghkgBhvhCAQ0EJhYkT3BlblNTTCBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmlj +YXRlMB0GA1UdDgQWBBQWMdyJLWA4vgE90pAuRa4/z4S4kDAOBgNVHQ8BAf8EBAMC +BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEAYDVR0RBAkwB4IFbm9kZTEwDQYJKoZI +hvcNAQELBQADggIBAMdFhC4xDxGs7i/gKhgBCv1JicNqk6Y2OQsS8ohk64ansx1r +uU0Rbx/pOsuD+d3ZuYeBaOYnHSLhExCcSxFjUlBkjS7aEigMxlHf2D+tYOgdcwlc +SjMaqyFDke+mR0rm8I15HviLjjZy1bDLnb8UsozLtdU040/MAtx9uF7SqvYUsTqy +alyfPeYZGpHZiFAmTcZ33uF3EByaSLACMVje0O1C9Xi/1v5Smp64NF15EF2DlHIv +TAj88oG7eEivVWie41mx8s/8WpR6XE3WFuZSc+j4qndtzwvmzlaO/e/v64ZzTPTL +SnrV424gtfZahjCb7+rSLQnSZShPeQessa1uF00xkCwlXuA7WXP9dAtOycySRsI+ +qy7vwD9Y5ZkZwFK8+8UnvySwwCSEHmy4zM0irA/XIKIRw7ahU3rxbkHgVCGh6Pyu +kGfv/+Wy9yW461w0aYUTMrUrS429CBDY0ek3T9eQ5bieJRjOYOl/uuPH+L4VSCOS +p2WIuXqqDMXqmxMUFNuaLYEg4Y51aLD0lkB+SH+tnOP5CZdufIKZRQhYiC+xcs2E +2/VvbqjAMe9vzF6d7a5EqbTkdS9k8CNnmxCfN+FlSl/iqUI3HKLVxNs+2Sux+Dhl +Nkt9qMcG2Gj0TxlqU43HrGeruVIxgC6Lj/QcIrc3Ddb1u7dccuNtF5UoqnVD \ No newline at end of file http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/metron-ui-security/src/test/resources/schema.ldif ---------------------------------------------------------------------- diff --git a/metron-interface/metron-ui-security/src/test/resources/schema.ldif b/metron-interface/metron-ui-security/src/test/resources/schema.ldif new file mode 100644 index 0000000..73d2dfd --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/resources/schema.ldif @@ -0,0 +1,77 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +version: 1 + +dn: dc=org +objectclass: domain +objectclass: top +dc: org + +dn: dc=apache,dc=org +objectclass: domain +objectclass: top +dc: apache + +dn: dc=metron,dc=apache,dc=org +objectclass: dcObject +objectclass: domain +objectclass: top +dc: metron + +dn: ou=people,dc=metron,dc=apache,dc=org +objectclass:top +objectclass:organizationalUnit +ou: people + +dn: ou=groups,dc=metron,dc=apache,dc=org +objectclass:top +objectclass:organizationalUnit +ou: groups + +dn: uid=admin,ou=people,dc=metron,dc=apache,dc=org +objectclass:top +objectclass:person +objectclass:organizationalPerson +objectclass:inetOrgPerson +cn: Admin +sn: User +uid: admin +userPassword: password + + +dn: uid=user,ou=people,dc=metron,dc=apache,dc=org +objectclass:top +objectclass:person +objectclass:organizationalPerson +objectclass:inetOrgPerson +cn: Normal +sn: User +uid: user +userPassword: password + +dn: cn=admin,ou=groups,dc=metron,dc=apache,dc=org +objectClass: groupOfNames +objectClass: top +cn: admin +member: uid=admin,ou=people,dc=metron,dc=apache,dc=org + +dn: cn=user,ou=groups,dc=metron,dc=apache,dc=org +objectClass: groupOfNames +objectClass: top +cn: user +member: uid=admin,ou=people,dc=metron,dc=apache,dc=org +member: uid=user,ou=people,dc=metron,dc=apache,dc=org http://git-wip-us.apache.org/repos/asf/metron/blob/54880ba8/metron-interface/pom.xml ---------------------------------------------------------------------- diff --git a/metron-interface/pom.xml b/metron-interface/pom.xml index e6ccd2d..844f213 100644 --- a/metron-interface/pom.xml +++ b/metron-interface/pom.xml @@ -39,11 +39,24 @@ </license> </licenses> <modules> + <module>metron-ui-security</module> + <module>metron-ui-host</module> <module>metron-config</module> + <module>metron-config-host</module> <module>metron-alerts</module> + <module>metron-alerts-host</module> <module>metron-rest</module> <module>metron-rest-client</module> </modules> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> + <java.version>1.8</java.version> + <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> + <spring.boot.version>2.0.1.RELEASE</spring.boot.version> + </properties> + <dependencies> <dependency> <groupId>junit</groupId>
