From 90b75c27e55d868d17c59910f2ce6cf25f4f4abf Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 10:03:12 +0100 Subject: [PATCH 01/24] Add base projects for Hosting UIs in Spring Add Zuul capability Add KnoxSSO provider and security config --- .gitignore | 6 + dependencies_with_url.csv | 48 +++ metron-interface/metron-ui-host/pom.xml | 142 +++++++++ .../metron/ui/AbstractHostApplication.java | 28 ++ .../org/apache/metron/ui/UserController.java | 61 ++++ .../metron/ui/ZuulAuthenticationFilter.java | 77 +++++ .../java/org/apache/metron/ui/ZuulError.java | 34 +++ .../org/apache/metron/ui/ZuulErrorFilter.java | 69 +++++ .../src/main/resources/application.yml | 59 ++++ .../org/apache/metron/ui/EmbeddedLdap.java | 163 ++++++++++ .../apache/metron/ui/TestHostApplication.java | 25 ++ .../java/org/apache/metron/ui/WhoamiTest.java | 127 ++++++++ .../ui/ZuulAuthorizationHeaderProxyTest.java | 103 +++++++ .../apache/metron/ui/config/TestsConfig.java | 47 +++ .../src/test/resources/application-test.yml | 92 ++++++ .../src/test/resources/schema.ldif | 77 +++++ metron-interface/metron-ui-security/pom.xml | 111 +++++++ .../ui/KnoxSSOAuthenticationFilter.java | 278 ++++++++++++++++++ .../ui/MetronAuthenticationException.java | 29 ++ .../ui/MetronAuthenticationProvider.java | 60 ++++ .../metron/ui/MetronSecurityConfig.java | 188 ++++++++++++ .../java/org/apache/metron/ui/JWTTests.java | 110 +++++++ .../ui/KnoxSSOAuthenticationFilterTests.java | 65 ++++ .../ui/MetronAuthenticationProviderTests.java | 33 +++ .../org/apache/metron/ui/headers.pem | 27 ++ .../org/apache/metron/ui/invalid.pem | 27 ++ .../org/apache/metron/ui/noheaders.pem | 25 ++ metron-interface/pom.xml | 2 + 28 files changed, 2113 insertions(+) create mode 100644 metron-interface/metron-ui-host/pom.xml create mode 100644 metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java create mode 100644 metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java create mode 100644 metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java create mode 100644 metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java create mode 100644 metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java create mode 100644 metron-interface/metron-ui-host/src/main/resources/application.yml create mode 100644 metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java create mode 100644 metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java create mode 100644 metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java create mode 100644 metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java create mode 100644 metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java create mode 100644 metron-interface/metron-ui-host/src/test/resources/application-test.yml create mode 100644 metron-interface/metron-ui-host/src/test/resources/schema.ldif create mode 100644 metron-interface/metron-ui-security/pom.xml create mode 100644 metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java create mode 100644 metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java create mode 100644 metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationProvider.java create mode 100644 metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronSecurityConfig.java create mode 100644 metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java create mode 100644 metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java create mode 100644 metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/MetronAuthenticationProviderTests.java create mode 100644 metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/headers.pem create mode 100644 metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/invalid.pem create mode 100644 metron-interface/metron-ui-security/src/test/resources/org/apache/metron/ui/noheaders.pem diff --git a/.gitignore b/.gitignore index 12fd7cd213..5b0971eadc 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,9 @@ temp/** temp/**/* metron-interface/metron-alerts/node/ repodata/ + +# Ignores for eclipse IDE +.springBeans +.factorypath +.vscode +.pydevproject \ No newline at end of file diff --git a/dependencies_with_url.csv b/dependencies_with_url.csv index 6ac1f23f90..5740ffee6a 100644 --- a/dependencies_with_url.csv +++ b/dependencies_with_url.csv @@ -421,3 +421,51 @@ com.google.code.gson:gson:jar:2.8.2:compile,ASLv2,https://github.com/google/gson com.zaxxer:HikariCP:jar:2.7.8:compile,ASLv2,https://github.com/brettwooldridge/HikariCP org.hibernate.validator:hibernate-validator:jar:6.0.9.Final:compile,ASLv2,https://github.com/hibernate/hibernate-validator +com.github.stephenc.jcip:jcip-annotations:jar:1.0-1:compile,ASLv2,http://stephenc.github.io/jcip-annotations/ +com.google.guava:guava:jar:15.0:compile,ASLv2, +com.netflix.archaius:archaius-core:jar:0.7.6:compile,ASLv2,https://github.com/Netflix/archaius +com.netflix.hystrix:hystrix-core:jar:1.5.12:compile,ASLv2,https://github.com/Netflix/Hystrix +com.netflix.hystrix:hystrix-javanica:jar:1.5.12:compile,ASLv2,https://github.com/Netflix/Hystrix +com.netflix.hystrix:hystrix-metrics-event-stream:jar:1.5.12:compile,ASLv2,https://github.com/Netflix/Hystrix +com.netflix.hystrix:hystrix-serialization:jar:1.5.12:compile,ASLv2,https://github.com/Netflix/Hystrix +com.netflix.netflix-commons:netflix-commons-util:jar:0.3.0:compile +com.netflix.ribbon:ribbon-core:jar:2.2.5:compile,ASLv2,https://github.com/Netflix/ribbon +com.netflix.ribbon:ribbon-httpclient:jar:2.2.5:compile,ASLv2,https://github.com/Netflix/ribbon +com.netflix.ribbon:ribbon-loadbalancer:jar:2.2.5:compile,ASLv2,https://github.com/Netflix/ribbon +com.netflix.ribbon:ribbon:jar:2.2.5:compile,ASLv2,https://github.com/Netflix/ribbon +com.netflix.zuul:zuul-core:jar:1.3.1:compile,ASLv2,https://github.com/Netflix/zuul +com.nimbusds:nimbus-jose-jwt:jar:4.41.2:compile,ASLv2,https://connect2id.com/products/nimbus-jose-jwt +commons-configuration:commons-configuration:jar:1.8:compile,ASLv2,http://commons.apache.org/ +commons-lang:commons-lang:jar:2.3:compile,ASLv2,http://commons.apache.org/ +io.micrometer:micrometer-core:jar:1.0.3:compile,ASLv2,http://micrometer.io/ +io.reactivex:rxjava-reactive-streams:jar:1.2.1:compile,ASLv2,http://reactivex.io/ +io.reactivex:rxjava:jar:1.2.0:compile,ASLv2,http://reactivex.io/ +io.undertow:undertow-core:jar:1.4.23.Final:compile,ASLv2,http://undertow.io/ +io.undertow:undertow-servlet:jar:1.4.23.Final:compile,ASLv2,http://undertow.io/ +io.undertow:undertow-websockets-jsr:jar:1.4.23.Final:compile,ASLv2,http://undertow.io/ +org.bouncycastle:bcpkix-jdk15on:jar:1.56:compile,MIT,https://www.bouncycastle.org/ +org.bouncycastle:bcprov-jdk15on:jar:1.56:compile,MIT,https://www.bouncycastle.org/ +org.glassfish:javax.el:jar:3.0.0:compile,Common Development and Distribution License (CDDL) v1.0 +org.hdrhistogram:HdrHistogram:jar:2.1.10:compile,BSD 2-clause,http://hdrhistogram.org/ +org.jboss.xnio:xnio-api:jar:3.3.8.Final:compile,ASLv2,http://xnio.jboss.org/ +org.latencyutils:LatencyUtils:jar:2.0.3:compile,BSD 2-clause,http://latencyutils.org/ +org.springframework.boot:spring-boot-actuator-autoconfigure:jar:2.0.1.RELEASE:compile,ASLv2,https://spring.io/projects/spring-boot +org.springframework.boot:spring-boot-actuator:jar:2.0.1.RELEASE:compile,ASLv2,https://spring.io/projects/spring-boot +org.springframework.boot:spring-boot-starter-actuator:jar:2.0.1.RELEASE:compile,ASLv2,https://spring.io/projects/spring-boot +org.springframework.boot:spring-boot-starter-undertow:jar:2.0.1.RELEASE:compile,ASLv2,https://spring.io/projects/spring-boot +org.springframework.cloud:spring-cloud-commons:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-context:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-netflix-archaius:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-netflix-core:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-netflix-ribbon:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-netflix-zuul:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-starter-netflix-archaius:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-starter-netflix-hystrix:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-starter-netflix-ribbon:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-starter-netflix-zuul:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.cloud:spring-cloud-starter:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ +org.springframework.ldap:spring-ldap-core:jar:2.3.2.RELEASE:compile,ASLv2,https://spring.io/projects/spring-ldap +org.springframework.security:spring-security-crypto:jar:5.0.4.RELEASE:compile,ASLv2,https://spring.io/projects/spring-security +org.springframework.security:spring-security-ldap:jar:5.0.4.RELEASE:compile,ASLv2,https://spring.io/projects/spring-security +org.springframework.security:spring-security-rsa:jar:1.0.5.RELEASE:compile,ASLv2,https://spring.io/projects/spring-security +org.springframework.cloud:spring-cloud-commons:jar:2.0.0.RELEASE:compile,ASLv2,http://projects.spring.io/spring-cloud/ diff --git a/metron-interface/metron-ui-host/pom.xml b/metron-interface/metron-ui-host/pom.xml new file mode 100644 index 0000000000..9746f70342 --- /dev/null +++ b/metron-interface/metron-ui-host/pom.xml @@ -0,0 +1,142 @@ + + + + 4.0.0 + + metron-ui-host + jar + + Metron Generic UI Host + Spring Server to host config ui + + + org.apache.metron + metron-interface + 0.5.1 + + + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + 4.41.2 + + + + + + org.apache.metron + metron-ui-security + ${project.parent.version} + + + + org.springframework.boot + spring-boot-starter-web + + + ch.qos.logback + logback-classic + + + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + + + + org.slf4j + slf4j-api + + + + org.slf4j + slf4j-log4j12 + + + + org.springframework.security + spring-security-test + test + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-test-autoconfigure + test + + + + org.springframework.security + spring-security-test + test + + + + org.apache.directory.server + apacheds-all + 1.5.4 + test + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + + + + + + + diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java new file mode 100644 index 0000000000..fd7095db31 --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/AbstractHostApplication.java @@ -0,0 +1,28 @@ +/** + * 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.springframework.cloud.netflix.zuul.EnableZuulProxy; +import org.springframework.context.annotation.Configuration; + +public abstract class AbstractHostApplication { + @Configuration + @EnableZuulProxy + public static class ZuulConfig { + } +} diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java new file mode 100644 index 0000000000..990da04edf --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -0,0 +1,61 @@ +/** + * 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.security.Principal; +import java.util.List; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * A trivial endpoint to ping for currently authenticated user principal + * + */ +@RestController +public class UserController { + @Value("knox.sso.url") + private String knoxSSOUrl; + + @RequestMapping(path = "/whoami", method = RequestMethod.GET) + public String user(Principal user) { + return user.getName(); + } + + @Secured("IS_AUTHENTICATED_FULLY") + @RequestMapping(path = "/whoami/roles", method = RequestMethod.GET) + public List user() { + UserDetails userDetails = (UserDetails)SecurityContextHolder.getContext(). + getAuthentication().getPrincipal(); + return userDetails.getAuthorities().stream().map(ga -> ga.getAuthority()).collect(Collectors.toList()); + } + + @RequestMapping(path = "/logout", method = RequestMethod.GET) + public void logout(Principal user, HttpServletResponse httpServletResponse) { + String logoutUrl = knoxSSOUrl.replaceAll("(web|knox)sso", "$1ssout"); + httpServletResponse.setHeader("Location", logoutUrl); + } +} diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java new file mode 100644 index 0000000000..a322612334 --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulAuthenticationFilter.java @@ -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. + */ +package org.apache.metron.ui; + +import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Component; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; + +@Component +public class ZuulAuthenticationFilter extends ZuulFilter { + + private static final String AUTHORIZATION_HEADER = "Authorization"; + public static final String COOKIE_NAME = "hadoop-jwt"; + + /** + * Only filter if we have no Authorization header already + */ + @Override + public boolean shouldFilter() { + RequestContext currentContext = RequestContext.getCurrentContext(); + String currentAuth = currentContext.getRequest().getHeader(AUTHORIZATION_HEADER); + if (currentAuth == null || currentAuth.isEmpty()) { + return true; + } + return false; + } + + @Override + public Object run() throws ZuulException { + RequestContext ctx = RequestContext.getCurrentContext(); + HttpServletRequest request = ctx.getRequest(); + Cookie[] cookies = request.getCookies(); + for (Cookie cookie : cookies) { + if (COOKIE_NAME.equals(cookie.getName())) { + // add this cookie to the Authorization header + String newAuthHeader = "Bearer " + cookie.getValue(); + ctx.getZuulRequestHeaders().put(AUTHORIZATION_HEADER, newAuthHeader); + break; + } + } + return null; + } + + @Override + public String filterType() { + return PRE_TYPE; + } + + @Override + public int filterOrder() { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java new file mode 100644 index 0000000000..c5ae65e25c --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulError.java @@ -0,0 +1,34 @@ +/** + * 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; + +public class ZuulError { + private String error; + + public ZuulError(Throwable throwable) { + this.setError(throwable.getMessage()); + } + + public String getError() { + return error; + } + + public void setError(String error) { + this.error = error; + } +} diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java new file mode 100644 index 0000000000..359d082d4f --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java @@ -0,0 +1,69 @@ +/** + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; + +@Component +public class ZuulErrorFilter extends ZuulFilter { + + private static final Logger LOG = LoggerFactory.getLogger(ZuulErrorFilter.class); + + @Override + public String filterType() { + return "error"; + } + + @Override + public int filterOrder() { + return 0; + } + + @Override + public boolean shouldFilter() { + return RequestContext.getCurrentContext().getThrowable() != null; + } + + @Override + public Object run() { + try { + RequestContext ctx = RequestContext.getCurrentContext(); + Throwable throwable = ctx.getThrowable(); + if (throwable != null) { + LOG.error("Zuul failure: " + throwable.getMessage(), throwable); + ctx.getResponse().setContentType("application/json"); + ZuulError error = new ZuulError(throwable); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.writeValue(ctx.getResponse().getOutputStream(), error); + ctx.setResponseStatusCode(500); + + } + } catch (Exception ex) { + LOG.error("Exception in custom error filter", ex); + ReflectionUtils.rethrowRuntimeException(ex); + } + return null; + } +} \ No newline at end of file diff --git a/metron-interface/metron-ui-host/src/main/resources/application.yml b/metron-interface/metron-ui-host/src/main/resources/application.yml new file mode 100644 index 0000000000..eebb04bc35 --- /dev/null +++ b/metron-interface/metron-ui-host/src/main/resources/application.yml @@ -0,0 +1,59 @@ +# 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. +metron: + version: 0.5.0 + +logging: + level: + root: WARN + +server: + port: 4201 + +zuul: + routes: + rest: + path: /api/v1/** + url: http://localhost:8082/api/v1 + +proxy: + auth: + routes: + rest: passthru + +ldap: + provider: + url: ldap://localhost:33389 + userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org + password: password + user: + dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org + passwordAttribute: userPassword + searchBase: ou=people,dc=hadoop,dc=apache,dc=org + searchFilter: "" + group: + searchBase: ou=groups,dc=hadoop,dc=apache,dc=org + searchFilter: "member={0}" + roleAttribute: "cn" + +knox: + sso: + url: '' + pubkey: '' + +ribbon: + ConnectTimeout: 3000 + ReadTimeout: 60000 \ No newline at end of file diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java new file mode 100644 index 0000000000..cd65baf4f1 --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java @@ -0,0 +1,163 @@ +/** + * 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 { + + 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(this.getClass().getClassLoader().getResource("schema.ldif").toURI())); + 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> indexedAttributes = new HashSet>(); + + for (String attribute : attrs) { + indexedAttributes.add(new JdbmIndex(attribute)); + } + + ((JdbmPartition) partition).setIndexedAttributes(indexedAttributes); + } + + public void applyLdif(File ldifFile) throws Exception { + new LdifFileLoader(directoryService.getAdminSession(), ldifFile, null, this.getClass().getClassLoader()) + .execute(); + } +} diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java new file mode 100644 index 0000000000..04feb53ba3 --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/TestHostApplication.java @@ -0,0 +1,25 @@ +/** + * 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.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class TestHostApplication extends AbstractHostApplication { + +} diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java new file mode 100644 index 0000000000..f631d7d4ac --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java @@ -0,0 +1,127 @@ +/** + * 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.hamcrest.Matchers.containsString; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; +import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.redirectedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +import javax.servlet.http.Cookie; + +import org.apache.metron.ui.config.TestsConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import com.fasterxml.jackson.annotation.JsonAlias; +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; + +@RunWith(SpringRunner.class) +@TestPropertySource("classpath:application-test.yml") +@ContextConfiguration(classes = { MetronSecurityConfig.class, + TestsConfig.class }, initializers = ConfigFileApplicationContextInitializer.class) +@SpringBootTest(classes = TestHostApplication.class) +@WebAppConfiguration +public class WhoamiTest { + + @Autowired + private WebApplicationContext context; + + private MockMvc mockMvc; + + private String username = "admin"; + private String password = "password"; + + @Value("${knox.sso.pubkey}") + private String publickey; + + @Value("${knox.sso.privatekey}") + private String privatekey; + + @Value("${knox.sso.url}") + private String knoxUrl; + + @Before + public void setup() { + mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build(); + } + + @Test + public void testWhoamiNoAuth() throws Exception { + mockMvc.perform(get("/whoami")).andExpect(status().is3xxRedirection()) + .andExpect(redirectedUrl(knoxUrl + "?originalUrl=http://localhost/whoami")); + } + + @Test + public void testWhoamiBasicAuth() throws Exception { + assertLoginCorrect(mockMvc.perform(get("/whoami").with(httpBasic(username, password)))); + } + + private ResultActions assertLoginCorrect(ResultActions actions) throws Exception { + return actions.andExpect(status().isOk()).andExpect(content().string(containsString(username))); + } + + @Test + public void testWhoamiJwtAuth() throws Exception { + String keyStr = String.join("", privatekey.split("\\s*|\\r|\\n")); + byte[] dec = Base64.getDecoder().decode(keyStr); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(dec); + KeyFactory kf = KeyFactory.getInstance("RSA"); + PrivateKey privKey = kf.generatePrivate(keySpec); + + JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256), + new Payload("{ \"sub\": \"" + username + "\" }")); + jwsObject.sign(new RSASSASigner(privKey)); + String token = jwsObject.serialize(); + + assertLoginCorrect(mockMvc.perform(get("/whoami").cookie(new Cookie("hadoop-jwt", token)))); + } + + @Test + public void testWhoamiRoles() throws Exception { + mockMvc.perform(get("/whoami/roles").with(httpBasic(username, password))).andExpect(status().isOk()) + .andExpect( + content().string("[\"ROLE_USER\",\"ROLE_ADMIN\"]")); + } + +} diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java new file mode 100644 index 0000000000..8f4c055a19 --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/ZuulAuthorizationHeaderProxyTest.java @@ -0,0 +1,103 @@ +/** + * 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.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.security.SecureRandom; + +import javax.servlet.http.Cookie; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; + +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSAlgorithm; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.KeyLengthException; +import com.nimbusds.jose.Payload; +import com.nimbusds.jose.crypto.MACSigner; + +public class ZuulAuthorizationHeaderProxyTest { + + private static final String BASIC_AUTH_HEADER = "Basic dGVzdDp0ZXN0"; + + private RequestContext context; + + private byte[] sharedKey = new byte[32]; + + private String validToken; + + private boolean keyInited = false; + + @Before + public void setTestRequestcontext() { + context = new RequestContext(); + context.setResponse(new MockHttpServletResponse()); + context.setResponseGZipped(false); + + RequestContext.testSetCurrentContext(context); + } + + @Test + public void testThatZuulPassesCookiesToAuthorization() throws ZuulException, KeyLengthException, JOSEException { + ZuulAuthenticationFilter zuulAuthenticationFilter = new ZuulAuthenticationFilter(); + + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(validCookie()); + context.setRequest(request); + zuulAuthenticationFilter.run(); + + String header = context.getZuulRequestHeaders().get("Authorization"); + assertTrue("Authorization contains bearer", header.startsWith("Bearer ")); + assertTrue("Authorization contains cookie value", header.endsWith(validToken())); + } + + @Test + public void testDoesNotReplaceAuthorizationHeader() throws ZuulException, KeyLengthException, JOSEException { + ZuulAuthenticationFilter zuulAuthenticationFilter = new ZuulAuthenticationFilter(); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(validCookie()); + request.addHeader("Authorization", BASIC_AUTH_HEADER); + context.setRequest(request); + assertFalse(zuulAuthenticationFilter.shouldFilter()); + } + + private Cookie validCookie() throws KeyLengthException, JOSEException { + return new Cookie(ZuulAuthenticationFilter.COOKIE_NAME, validToken()); + } + + private String validToken() throws KeyLengthException, JOSEException { + if (!this.keyInited ) { + new SecureRandom().nextBytes(sharedKey); + this.keyInited = true; + } + if (this.validToken == null) { + JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload("Test")); + jwsObject.sign(new MACSigner(sharedKey)); + this.validToken = jwsObject.serialize(); + } + return this.validToken; + } +} diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java new file mode 100644 index 0000000000..605298963f --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java @@ -0,0 +1,47 @@ +/** + * 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.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; + +@Configuration +public class TestsConfig { + /** + * PropertyPlaceholderConfigurer bean required to make SPEL Values injectable in + * Tests from YAML config. + * + */ + @Bean + public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { + YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); + yaml.setResources(new ClassPathResource("application-test.yml")); + PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer(); + propertyPlaceholderConfigurer.setProperties(yaml.getObject()); + return propertyPlaceholderConfigurer; + } + + @Bean + public EmbeddedLdap embeddedLdap() { + return new EmbeddedLdap(); + } +} diff --git a/metron-interface/metron-ui-host/src/test/resources/application-test.yml b/metron-interface/metron-ui-host/src/test/resources/application-test.yml new file mode 100644 index 0000000000..2dcb2cd23b --- /dev/null +++ b/metron-interface/metron-ui-host/src/test/resources/application-test.yml @@ -0,0 +1,92 @@ +# 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" + +knox: + sso: + url: https://localhost:8443/gateway/knoxsso/api/v1/websso + pubkey: | + 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 + privatekey: | + MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDOo/DCJAIuIWV3 + fKw/pZnEOkpplF/A61ijrt3MsFE3FEemXyUPi7dJGR9sgzj4aZq+UyBMe2k8wJpa + M2VFaAK3KKoxmp+zaeR8trwtaw3qjG7nkO5uC0qcBUc6m/WZxcnKvlNSaOZ6x8FX + csX76OcjmDoMlkE/7qRLErYkdAA48iz801UJRK3QAFcSm0Rxd4pFkrx87vMqCnFg + Bp+wm0objhdGyDDkfXuxrZUkB+iTMR9e6BFPfColUbyVedkSh6R2aT3w29+mL7IE + 8u2XMw5gkDzaVPYX1fr3IGRFxcq6cS4Bu6RyQVz11r/HMPa0Cy4ptNveyzUaRgAM + 9hnNraHRAgMBAAECggEBAJ2MMvDiIWNohQMf4/g221DYHIn43TSqew95MJRyTcmP + xb0cR5ZdsOWjqOjD97i2U4wOts55PVhbhJOHIgxT69YXxAND38Ub1GAdtsVuHNMa + NSiKwK7YHw9rms4dwJh4S40vpTlsz2UHTerNkBOrlCb4VjHokWEcItk2L/cFFnJT + Gymql1ZpD3BMprZW12yjLX38utCsuH2uFdYj2l53BtNNt8Su4ToXmvB4+rqW+ktF + rHINUe64EoKLnbBKpjzDlYH16uGVGAt6VBHU8Gr6n70gdWJvrLIaFy8ZNxRiER+x + qwgFad/aQWCGedb5PRtkQmvB+EIGRBbJ0zeZV+IrqMECgYEA8uWwcfAIOo9mZIUP + vlUCODO7oCIB7n1J5OZQlkKgCWpiIKgvSpGW/+dBaSKCspLfmlvFsb5XwKio26io + xGJ8gBnwexY4vuHsdA3DZWDcC1IHBf/chix5sRlYAqh6NRfgmhoUq05fYJTpr+A5 + EWXZs1Edt0wj0mswPdNgkevHX9kCgYEA2cmPLhRKP4zrNZj3ojChjiFXrYtryQVv + Vypd6sznyuiyN1DGYBN1VrEg+ukpAYv6Ez0clX7Xa/03BUAUhwTYve/ZRrZC5w3P + 4EYCRAaNzDZWqhQrb+z3GFz+h4eOVZ6dnkAmuFqIf+ws11pocM04dCjy2e97UZZA + naGu54iHjrkCgYEAhtR/RE/kkXUmdmfyXEnd6Iq2/OXDwrnjed9rHm2vXmqiO9SA + I9l7Q2QASDby6+NhodKNg+PP3E8DJKOTwyeUSpubhQfJyhOo6Kb3LuA8ZUBMS8VC + iWxIxMj3tMoGxFATyhbuIEVp5jfjHFDP/NtXpBVD9IqcW+JKLheWxIln68kCgYAq + z8+AnGZ4FaiLEbXkQTEQ8ob8y4J1ssbPWLm7lWofXhzieNN2QXz4fLth94GjFzQi + ognDbXrFdLJjKtSeMhq1Q7fviZafOvzZNonte2hWc3wX1P0w9GEife1fEQuu0w5i + 9HNoHAvnMbMi5lfPjNgDJaWPp98TC7lKA2WRiCo1qQKBgErhfGKRxU13ZTPsKXZf + 7i0vMC7n5CPJajrFDml/NZLODHwif2KqZE+gHlxtgWayU1UigSdUVDXdRZbS0RmA + yrmpu5zIdozX3pFmBsObpvd5TtLvq9er+HF93gedBotRqqVuK8yX7VY/M9/nBJnc + FITLrkrz6SPpr9Wm+ufjKxn2 diff --git a/metron-interface/metron-ui-host/src/test/resources/schema.ldif b/metron-interface/metron-ui-host/src/test/resources/schema.ldif new file mode 100644 index 0000000000..73d2dfd1ae --- /dev/null +++ b/metron-interface/metron-ui-host/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 diff --git a/metron-interface/metron-ui-security/pom.xml b/metron-interface/metron-ui-security/pom.xml new file mode 100644 index 0000000000..b42af640e5 --- /dev/null +++ b/metron-interface/metron-ui-security/pom.xml @@ -0,0 +1,111 @@ + + + + 4.0.0 + + + metron-ui-security + jar + + Metron SSO Security Configs + Spring Security setup to Metron SSO + + + org.apache.metron + metron-interface + 0.5.1 + + + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + 4.41.2 + + + + + org.springframework.boot + spring-boot-starter-web + + + ch.qos.logback + logback-classic + + + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.ldap + spring-ldap-core + + + org.springframework.security + spring-security-ldap + + + + com.nimbusds + nimbus-jose-jwt + ${jwt.version} + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.security + spring-security-test + test + + + + commons-io + commons-io + 2.5 + test + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + + diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java new file mode 100644 index 0000000000..dfd027b91a --- /dev/null +++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java @@ -0,0 +1,278 @@ +/** + * 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.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.PublicKey; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; +import java.util.List; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.WebAuthenticationDetails; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.crypto.RSASSAVerifier; +import com.nimbusds.jwt.SignedJWT; + +public class KnoxSSOAuthenticationFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(KnoxSSOAuthenticationFilter.class); + + private final String knoxUrl; + private final RSAPublicKey publicKey; + private final MetronAuthenticationProvider authenticationProvider; + private String knoxCookie; + private String knoxOriginalUrl; + + public KnoxSSOAuthenticationFilter(MetronAuthenticationProvider authenticationProvider, String knoxUrl, + String knoxCookie, String knoxOriginalUrl, RSAPublicKey publicKey) { + super(); + this.authenticationProvider = authenticationProvider; + this.knoxUrl = knoxUrl; + if (knoxCookie == null) { + this.knoxCookie = "hadoop-jwt"; + } else { + this.knoxCookie = knoxCookie; + } + if (knoxOriginalUrl == null) { + this.knoxOriginalUrl = "originalUrl"; + } else { + this.knoxOriginalUrl = knoxOriginalUrl; + } + this.publicKey = publicKey; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void destroy() { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + String authHeader = httpRequest.getHeader("Authorization"); + // if SSO is not enabled, skip this filter + if (this.knoxUrl.isEmpty() || (authHeader != null && authHeader.startsWith("Basic"))) { + chain.doFilter(request, response); + } else { + String serializedJWT = getJWTFromAuthorization(httpRequest); + if (serializedJWT == null) { + serializedJWT = getJWTFromCookie(httpRequest); + } + + if (serializedJWT != null) { + SignedJWT jwtToken = null; + try { + jwtToken = SignedJWT.parse(serializedJWT); + boolean valid = validateToken(jwtToken); + // if the public key provide is correct and also token is not expired the + // process token + if (valid) { + String userName = jwtToken.getJWTClaimsSet().getSubject(); + LOG.info("SSO login user : {} ", userName); + // if we get the userName from the token then log into atlas using the same user + if (userName != null && !userName.trim().isEmpty()) { + List grantedAuths = MetronAuthenticationProvider + .getAuthoritiesFromUGI(userName); + final UserDetails principal = new User(userName, "", grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, "", grantedAuths); + WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); + ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); + Authentication authentication = authenticationProvider.authenticate(finalAuthentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + chain.doFilter(request, response); + } else { // if the token is not valid then redirect to knox sso + redirectToKnox(httpRequest, httpResponse, chain); + } + } catch (ParseException e) { + LOG.warn("Unable to parse the JWT token", e); + redirectToKnox(httpRequest, httpResponse, chain); + } + } else { + redirectToKnox(httpRequest, httpResponse, chain); + } + } + } + + private void redirectToKnox(HttpServletRequest httpRequest, HttpServletResponse httpResponse, FilterChain chain) + throws IOException, ServletException { + // should probably check it's a browser + String ssourl = constructLoginURL(httpRequest); + httpResponse.sendRedirect(ssourl); + } + + /** + * Create the URL to be used for authentication of the user in the absence of a + * JWT token within the incoming request. + * + * @param request + * for getting the original request URL + * @return url to use as login url for redirect + */ + protected String constructLoginURL(HttpServletRequest request) { + String delimiter = "?"; + if (knoxUrl.contains("?")) { + delimiter = "&"; + } + String loginURL = knoxUrl + delimiter + knoxOriginalUrl + "=" + request.getRequestURL().toString() + + getOriginalQueryString(request); + return loginURL; + } + + private String getOriginalQueryString(HttpServletRequest request) { + String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; + } + + /** + * Verify the signature of the JWT token in this method. This method depends on + * the public key that was established during init based upon the provisioned + * public key. Override this method in subclasses in order to customize the + * signature verification behavior. + * + * @param jwtToken + * the token that contains the signature to be validated + * @return valid true if signature verifies successfully; false otherwise + */ + protected boolean validateToken(SignedJWT jwtToken) { + boolean valid = false; + if (JWSObject.State.SIGNED == jwtToken.getState()) { + LOG.debug("SSO token is in a SIGNED state"); + if (jwtToken.getSignature() != null) { + LOG.debug("SSO token signature is not null"); + try { + JWSVerifier verifier = new RSASSAVerifier(publicKey); + if (jwtToken.verify(verifier)) { + valid = true; + LOG.debug("SSO token has been successfully verified"); + } else { + LOG.warn("SSO signature verification failed.Please check the public key"); + } + } catch (JOSEException je) { + LOG.warn("Error while validating signature", je); + } catch (Exception e) { + LOG.warn("Error while validating signature", e); + } + } + // Now check that the signature algorithm was as expected + if (valid) { + String receivedSigAlg = jwtToken.getHeader().getAlgorithm().getName(); + if (!receivedSigAlg.equals("RS256")) { + valid = false; + } + } + } + return valid; + } + + private String getJWTFromAuthorization(HttpServletRequest httpRequest) { + String header = httpRequest.getHeader("Authorization"); + return (header != null && header.matches("Bearer (.*)")) ? header.substring(7) : null; + } + + /** + * Encapsulate the acquisition of the JWT token from HTTP cookies within the + * request. + * + * Taken from + * + * @param req + * servlet request to get the JWT token from + * @return serialized JWT token + */ + protected String getJWTFromCookie(HttpServletRequest req) { + String serializedJWT = null; + Cookie[] cookies = req.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + LOG.debug(String.format("Found cookie: %s [%s]", cookie.getName(), cookie.getValue())); + if (knoxCookie.equals(cookie.getName())) { + if (LOG.isDebugEnabled()) { + LOG.debug(knoxCookie + " cookie has been found and is being processed"); + } + serializedJWT = cookie.getValue(); + break; + } + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug(knoxCookie + " not found"); + } + } + return serializedJWT; + } + + public static RSAPublicKey parseRSAPublicKey(String pem) + throws CertificateException, UnsupportedEncodingException, ServletException { + String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"; + String PEM_FOOTER = "\n-----END CERTIFICATE-----"; + String fullPem = (pem.startsWith(PEM_HEADER) && pem.endsWith(PEM_FOOTER)) ? pem : PEM_HEADER + pem + PEM_FOOTER; + PublicKey key = null; + try { + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream is = new ByteArrayInputStream(fullPem.getBytes("UTF8")); + X509Certificate cer = (X509Certificate) fact.generateCertificate(is); + key = cer.getPublicKey(); + } catch (CertificateException ce) { + String message = null; + if (pem.startsWith(PEM_HEADER)) { + message = "CertificateException - be sure not to include PEM header " + + "and footer in the PEM configuration element."; + } else { + message = "CertificateException - PEM may be corrupt"; + } + throw new ServletException(message, ce); + } catch (UnsupportedEncodingException uee) { + throw new ServletException(uee); + } + return (RSAPublicKey) key; + } + +} diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java new file mode 100644 index 0000000000..ddf177ba42 --- /dev/null +++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/MetronAuthenticationException.java @@ -0,0 +1,29 @@ +/** + * 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.springframework.security.core.AuthenticationException; + +public class MetronAuthenticationException extends AuthenticationException{ + public MetronAuthenticationException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 1L; + +} 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 0000000000..42d8a2df6f --- /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 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 grantedAuths = new ArrayList(); + grantedAuths.add(new SimpleGrantedAuthority("USER")); + return grantedAuths; + } +} 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 0000000000..e8e6835757 --- /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(); + } + + private KnoxSSOAuthenticationFilter ssoAuthenticationFilter() throws Exception { + String knoxKey; + if ((this.knoxKeyString == null || this.knoxKeyString.isEmpty()) && this.knoxKeyFile != null) { + List 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(); + } + +} 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 0000000000..9155546f48 --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/JWTTests.java @@ -0,0 +1,110 @@ +/** + * 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.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; + +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; + +public class JWTTests { + private static final String COOKIE_NAME = "hadoop-jwt"; + private static final String knoxUrl = "https://localhost:8443/gateway/default/knoxsso"; + + @Test + public void testValidJWT() throws Exception { + KeyPair key = createKey(); + + MockHttpServletRequest request = requestWithJWT(tokenWithKey((RSAPrivateKey) key.getPrivate())); + 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()); + } + + @Test + public void testInvalidJWT() throws Exception { + KeyPair key = createKey(); + KeyPair badKey = createKey(); + assertFalse(key.equals(badKey)); + + MockHttpServletRequest request = requestWithJWT(tokenWithKey((RSAPrivateKey) badKey.getPrivate())); + 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 String tokenWithKey(RSAPrivateKey key) throws JOSEException { + JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256), new Payload("{ \"sub\": \"test\" }")); + jwsObject.sign(new RSASSASigner(key)); + return jwsObject.serialize(); + } + + private MockHttpServletRequest requestWithJWT(String jwt) { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setCookies(new Cookie(COOKIE_NAME, jwt)); + return request; + } + + private static void assertRedirectedToKnox(MockHttpServletResponse response) { + assertTrue("Reponse is redirect to SSO", response.getHeader("Location").startsWith(knoxUrl)); + } + +} 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 0000000000..ea96e36a9e --- /dev/null +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/KnoxSSOAuthenticationFilterTests.java @@ -0,0 +1,65 @@ +/** + * 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 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()); + } + } +} 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 0000000000..eba5341482 --- /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() { + + } +} 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 0000000000..7153bb490f --- /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 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 0000000000..ffcd1bc244 --- /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 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 0000000000..267decf4d6 --- /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 diff --git a/metron-interface/pom.xml b/metron-interface/pom.xml index e6ccd2d279..467781b37d 100644 --- a/metron-interface/pom.xml +++ b/metron-interface/pom.xml @@ -39,6 +39,8 @@ + metron-ui-security + metron-ui-host metron-config metron-alerts metron-rest From 8443329c90247c533bc4ffa5ef7cc64955c55f32 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 10:20:04 +0100 Subject: [PATCH 02/24] Packaging changes for the UIs --- metron-interface/metron-alerts/pom.xml | 21 +- .../metron-alerts/scripts/alerts-server.js | 76 ------ .../metron-alerts/scripts/metron-alerts-ui | 159 ------------ .../metron-alerts/scripts/package.json | 21 -- metron-interface/metron-config/pom.xml | 236 +++++++++--------- .../scripts/metron-management-ui | 159 ------------ .../metron-config/scripts/package.json | 22 -- .../metron-config/scripts/server.js | 76 ------ 8 files changed, 119 insertions(+), 651 deletions(-) delete mode 100644 metron-interface/metron-alerts/scripts/alerts-server.js delete mode 100644 metron-interface/metron-alerts/scripts/metron-alerts-ui delete mode 100644 metron-interface/metron-alerts/scripts/package.json delete mode 100644 metron-interface/metron-config/scripts/metron-management-ui delete mode 100644 metron-interface/metron-config/scripts/package.json delete mode 100755 metron-interface/metron-config/scripts/server.js diff --git a/metron-interface/metron-alerts/pom.xml b/metron-interface/metron-alerts/pom.xml index e7706b3401..b47f2410ec 100644 --- a/metron-interface/metron-alerts/pom.xml +++ b/metron-interface/metron-alerts/pom.xml @@ -31,6 +31,12 @@ + + + dist + public + + com.github.eirslett @@ -96,21 +102,6 @@ - - maven-assembly-plugin - - assembly.xml - - - - make-assembly - package - - single - - - - org.codehaus.mojo exec-maven-plugin diff --git a/metron-interface/metron-alerts/scripts/alerts-server.js b/metron-interface/metron-alerts/scripts/alerts-server.js deleted file mode 100644 index 716f37b4cd..0000000000 --- a/metron-interface/metron-alerts/scripts/alerts-server.js +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node -/** - * 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. - */ - -'use strict'; - -var os = require('os'); -var app = require('express')(); -var path = require('path'); -var compression = require('compression'); -var serveStatic = require('serve-static'); -var favicon = require('serve-favicon'); -var proxy = require('http-proxy-middleware'); -var argv = require('optimist') - .demand(['c']) - .alias('c', 'config_file') - .usage('Usage: alerts-server.js -c [config_file]') - .describe('c', 'Path to alerts_ui.yml') - .argv; -var YAML = require('yamljs'); - -var metronUIAddress = ''; -var ifaces = os.networkInterfaces(); -var uiConfig = YAML.load(argv.c); - -Object.keys(ifaces).forEach(function (dev) { - ifaces[dev].forEach(function (details) { - if (details.family === 'IPv4') { - metronUIAddress += '\n'; - metronUIAddress += 'http://' + details.address + ':' + uiConfig.port; - } - }); -}); - -function setCustomCacheControl (res, path) { - if (serveStatic.mime.lookup(path) === 'text/html') { - res.setHeader('Cache-Control', 'public, max-age=10') - } - res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString()); -} - -app.use(compression()); - -var restUrl = 'http://' + uiConfig.rest.host + ':' + uiConfig.rest.port; -app.use('/api/v1', proxy(restUrl)); -app.use('/logout', proxy(restUrl)); - -app.use(favicon(path.join(__dirname, '../alerts-ui/favicon.ico'))); - -app.use(serveStatic(path.join(__dirname, '../alerts-ui'), { - maxAge: '1d', - setHeaders: setCustomCacheControl -})); - -app.get('*', function(req, res){ - res.sendFile(path.join(__dirname, '../alerts-ui/index.html')); -}); - -app.listen(uiConfig.port, function(){ - console.log("Metron alerts ui is listening on " + metronUIAddress); -}); diff --git a/metron-interface/metron-alerts/scripts/metron-alerts-ui b/metron-interface/metron-alerts/scripts/metron-alerts-ui deleted file mode 100644 index bb8e67c1d0..0000000000 --- a/metron-interface/metron-alerts/scripts/metron-alerts-ui +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash -# -# 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. -# -# metron alerts UI service -# chkconfig: - 20 80 -# description: Alerts UI -# processname: metron-alerts-ui -# - -# all LSB compliant distributions provide the following -# http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/iniscrptfunc.html -if [ -f /lib/lsb/init-functions ]; then - . /lib/lsb/init-functions -fi - -NAME=metron-alerts-ui -DESC="Metron Alerts UI" -METRON_VERSION=${project.version} -METRON_HOME=/usr/metron/$METRON_VERSION -METRON_LOG_DIR="/var/log/metron" -METRON_PID_DIR="/var/run/metron" -METRON_USER="metron" -METRON_GROUP="metron" -METRON_SYSCONFIG="/etc/default/metron" -if [ -f "$METRON_SYSCONFIG" ]; then - set -a - . "$METRON_SYSCONFIG" -fi - -PIDFILE="$METRON_PID_DIR/$NAME.pid" - -DAEMON="node $METRON_HOME/web/expressjs/alerts-server.js -c $METRON_HOME/config/alerts_ui.yml" - -# -# start the rest application -# -start() { - - # if pidfile exists, do not start another - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - printf "OK [$PID]\n" - return - fi - - if [ ! -d "$METRON_LOG_DIR" ]; then - mkdir -p "$METRON_LOG_DIR" && chown "$METRON_USER":"$METRON_GROUP" "$METRON_LOG_DIR" - fi - - if [ ! -d "$METRON_PID_DIR" ]; then - mkdir -p "$METRON_PID_DIR" && chown "$METRON_USER":"$METRON_GROUP" "$METRON_PID_DIR" - fi - - # kick-off the daemon - CMD="$DAEMON >> $METRON_LOG_DIR/$NAME.log 2>&1 & echo \$!" - PID=`su -c "$CMD" $METRON_USER` - - if [ -z $PID ]; then - printf "Fail\n" - else - echo $PID > $PIDFILE - printf "Ok [$PID]\n" - fi -} - -# -# stop the rest application -# -stop() { - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - while sleep 1 - echo -n "." - kill -0 $PID >/dev/null 2>&1 - do - kill $PID - done - rm -f $PIDFILE - printf "%s\n" "Ok" - else - printf "%s\n" "Not running" - fi -} - -# -# status check of the rest application -# -status() { - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - if [ -z "`ps axf | grep ${PID} | grep -v grep`" ]; then - printf "%s\n" "Process dead but pidfile exists" - else - echo "Running" - fi - else - printf "%s\n" "Service not running" - fi -} - -case "$1" in - - ############################################################################## - # start - # - start) - printf "%-50s \n" "Starting $NAME..." - start - ;; - - ############################################################################## - # status - # - status) - printf "%-50s \n" "Checking $NAME..." - status - ;; - - ############################################################################## - # stop - # - stop) - printf "%-50s \n" "Stopping $NAME..." - stop - ;; - - ############################################################################## - # restart - # - restart) - $0 stop - $0 start - ;; - - ############################################################################## - # reload - # - reload) - ;; - - *) - echo "Usage: $0 {status|start|stop|restart}" - exit 1 -esac diff --git a/metron-interface/metron-alerts/scripts/package.json b/metron-interface/metron-alerts/scripts/package.json deleted file mode 100644 index d41da8195f..0000000000 --- a/metron-interface/metron-alerts/scripts/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "metron-alerts-ui-web-server", - "version": "0.4.0", - "description": "Metron alerts ui web server", - "main": "server.js", - "dependencies": { - "compression": "1.6.2", - "express": "4.15.2", - "http-proxy-middleware": "0.17.4", - "optimist": "0.6.1", - "serve-favicon": "2.4.2", - "serve-static": "1.12.1" - }, - "devDependencies": {}, - "scripts": { - "start": "node server.js" - }, - "private": true, - "author": "", - "license": "Apache 2.0" -} diff --git a/metron-interface/metron-config/pom.xml b/metron-interface/metron-config/pom.xml index 14acdc6d7c..e48515b29e 100644 --- a/metron-interface/metron-config/pom.xml +++ b/metron-interface/metron-config/pom.xml @@ -12,133 +12,123 @@ the specific language governing permissions and limitations under the License. --> - - 4.0.0 - - org.apache.metron - metron-interface - 0.5.1 - - metron-config - https://metron.apache.org/ - - UTF-8 - UTF-8 - v6.2.0 - 3.8.9 - - - - - - - - com.github.eirslett - frontend-maven-plugin - 1.3 + + 4.0.0 + + org.apache.metron + metron-interface + 0.5.1 + + metron-config + https://metron.apache.org/ + + UTF-8 + UTF-8 + v7.10.0 + 4.2.0 + + + + + + + dist + public + + + + + com.github.eirslett + frontend-maven-plugin + 1.3 + + ./ + ${node.version} + ${npm.version} + false + + + + generate-resources + install node and npm + + install-node-and-npm + + + + generate-resources + npm install + + npm + + + install + + + + generate-resources + ng build + + npm + - ./ - ${node.version} - ${npm.version} - false + run build - - - generate-resources - install node and npm - - install-node-and-npm - - - - generate-resources - npm install - - npm - - - install - - - - generate-resources - ng build - - npm - - - run build - - - - npm test - - npm - - test - - test - - - - - - maven-clean-plugin - 3.0.0 + + + npm test + + npm + + test - - - coverage - false - - - dist - false - - - node - false - - - node_modules - false - - + test - - - maven-assembly-plugin + + + + + maven-clean-plugin + 3.0.0 + + + + coverage + false + + + dist + false + + + node + false + + + node_modules + false + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.5.0 + + + prepend-license-header + prepare-package + + exec + - assembly.xml + ./scripts/prepend_license_header.sh - - - make-assembly - package - - single - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.5.0 - - - prepend-license-header - prepare-package - - exec - - - ./scripts/prepend_license_header.sh - - - - - - + + + + + diff --git a/metron-interface/metron-config/scripts/metron-management-ui b/metron-interface/metron-config/scripts/metron-management-ui deleted file mode 100644 index 76b9be8e37..0000000000 --- a/metron-interface/metron-config/scripts/metron-management-ui +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/env bash -# -# 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. -# -# metron management UI service -# chkconfig: - 20 80 -# description: Management UI -# processname: metron-management-ui -# - -# all LSB compliant distributions provide the following -# http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/iniscrptfunc.html -if [ -f /lib/lsb/init-functions ]; then - . /lib/lsb/init-functions -fi - -NAME=metron-management-ui -DESC="Metron Management UI" -METRON_VERSION=${project.version} -METRON_HOME=/usr/metron/$METRON_VERSION -METRON_LOG_DIR="/var/log/metron" -METRON_PID_DIR="/var/run/metron" -METRON_USER="metron" -METRON_GROUP="metron" -METRON_SYSCONFIG="/etc/default/metron" -if [ -f "$METRON_SYSCONFIG" ]; then - set -a - . "$METRON_SYSCONFIG" -fi - -PIDFILE="$METRON_PID_DIR/$NAME.pid" - -DAEMON="node $METRON_HOME/web/expressjs/server.js -c $METRON_HOME/config/management_ui.yml" - -# -# start the rest application -# -start() { - - # if pidfile exists, do not start another - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - printf "OK [$PID]\n" - return - fi - - if [ ! -d "$METRON_LOG_DIR" ]; then - mkdir -p "$METRON_LOG_DIR" && chown "$METRON_USER":"$METRON_GROUP" "$METRON_LOG_DIR" - fi - - if [ ! -d "$METRON_PID_DIR" ]; then - mkdir -p "$METRON_PID_DIR" && chown "$METRON_USER":"$METRON_GROUP" "$METRON_PID_DIR" - fi - - # kick-off the daemon - CMD="$DAEMON >> $METRON_LOG_DIR/$NAME.log 2>&1 & echo \$!" - PID=`su -c "$CMD" $METRON_USER` - - if [ -z $PID ]; then - printf "Fail\n" - else - echo $PID > $PIDFILE - printf "Ok [$PID]\n" - fi -} - -# -# stop the rest application -# -stop() { - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - while sleep 1 - echo -n "." - kill -0 $PID >/dev/null 2>&1 - do - kill $PID - done - rm -f $PIDFILE - printf "%s\n" "Ok" - else - printf "%s\n" "Not running" - fi -} - -# -# status check of the rest application -# -status() { - if [ -f $PIDFILE ]; then - PID=`cat $PIDFILE` - if [ -z "`ps axf | grep ${PID} | grep -v grep`" ]; then - printf "%s\n" "Process dead but pidfile exists" - else - echo "Running" - fi - else - printf "%s\n" "Service not running" - fi -} - -case "$1" in - - ############################################################################## - # start - # - start) - printf "%-50s \n" "Starting $NAME..." - start - ;; - - ############################################################################## - # status - # - status) - printf "%-50s \n" "Checking $NAME..." - status - ;; - - ############################################################################## - # stop - # - stop) - printf "%-50s \n" "Stopping $NAME..." - stop - ;; - - ############################################################################## - # restart - # - restart) - $0 stop - $0 start - ;; - - ############################################################################## - # reload - # - reload) - ;; - - *) - echo "Usage: $0 {status|start|stop|restart}" - exit 1 -esac diff --git a/metron-interface/metron-config/scripts/package.json b/metron-interface/metron-config/scripts/package.json deleted file mode 100644 index 24d17e0096..0000000000 --- a/metron-interface/metron-config/scripts/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "metron-management-ui-web-server", - "version": "0.5.1", - "description": "Metron management ui web server", - "main": "server.js", - "dependencies": { - "compression": "1.6.2", - "express": "4.15.2", - "http-proxy-middleware": "0.17.4", - "optimist": "0.6.1", - "serve-favicon": "2.4.2", - "serve-static": "1.12.1", - "yamljs": "0.2.9" - }, - "devDependencies": {}, - "scripts": { - "start": "node server.js" - }, - "private": true, - "author": "", - "license": "Apache-2.0" -} diff --git a/metron-interface/metron-config/scripts/server.js b/metron-interface/metron-config/scripts/server.js deleted file mode 100755 index 7c8ee9ea41..0000000000 --- a/metron-interface/metron-config/scripts/server.js +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env node -/** - * 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. - */ - -'use strict'; - -var os = require('os'); -var app = require('express')(); -var path = require('path'); -var compression = require('compression'); -var serveStatic = require('serve-static'); -var favicon = require('serve-favicon'); -var proxy = require('http-proxy-middleware'); -var argv = require('optimist') - .demand(['c']) - .alias('c', 'config_file') - .usage('Usage: server.js -c [config_file]') - .describe('c', 'Path to management_ui.yml') - .argv; -var YAML = require('yamljs'); - -var metronUIAddress = ''; -var ifaces = os.networkInterfaces(); -var uiConfig = YAML.load(argv.c); - -Object.keys(ifaces).forEach(function (dev) { - ifaces[dev].forEach(function (details) { - if (details.family === 'IPv4') { - metronUIAddress += '\n'; - metronUIAddress += 'http://' + details.address + ':' + uiConfig.port; - } - }); -}); - -function setCustomCacheControl (res, path) { - if (serveStatic.mime.lookup(path) === 'text/html') { - res.setHeader('Cache-Control', 'public, max-age=10') - } - res.setHeader("Expires", new Date(Date.now() + 2592000000).toUTCString()); -} - -app.use(compression()); - -var restUrl = 'http://' + uiConfig.rest.host + ':' + uiConfig.rest.port; -app.use('/api/v1', proxy(restUrl)); -app.use('/logout', proxy(restUrl)); - -app.use(favicon(path.join(__dirname, '../management-ui/favicon.ico'))); - -app.use(serveStatic(path.join(__dirname, '../management-ui'), { - maxAge: '1d', - setHeaders: setCustomCacheControl -})); - -app.get('*', function(req, res){ - res.sendFile(path.join(__dirname, '../management-ui/index.html')); -}); - -app.listen(uiConfig.port, function(){ - console.log("Metron server listening on " + metronUIAddress); -}); From 627d37cf8092260a37c1f80d599025dbf6d3d2f7 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 10:28:29 +0100 Subject: [PATCH 03/24] Integrate SSO base to metron-rest and added test config --- metron-interface/metron-rest/pom.xml | 5 +- .../metron/rest/MetronRestApplication.java | 16 +-- .../metron/rest/config/WebSecurityConfig.java | 108 ------------------ .../src/main/resources/application-test.yml | 19 +++ .../src/main/scripts/metron-rest.sh | 7 +- .../apache/metron/rest/config/TestConfig.java | 18 +++ .../rest/config/TestSecurityConfig.java | 44 +++++++ 7 files changed, 99 insertions(+), 118 deletions(-) delete mode 100644 metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java create mode 100644 metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java diff --git a/metron-interface/metron-rest/pom.xml b/metron-interface/metron-rest/pom.xml index 543d5b4eb8..69d00120ab 100644 --- a/metron-interface/metron-rest/pom.xml +++ b/metron-interface/metron-rest/pom.xml @@ -91,8 +91,9 @@ - org.springframework.boot - spring-boot-starter-security + org.apache.metron + metron-ui-security + ${project.parent.version} org.springframework.security.kerberos diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java index 52cdf8fa5f..b315de601f 100644 --- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java +++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/MetronRestApplication.java @@ -18,21 +18,23 @@ package org.apache.metron.rest; import org.apache.metron.rest.util.ParserIndex; +import org.apache.metron.ui.MetronSecurityConfig; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration; import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration; - -import static org.apache.metron.rest.MetronRestConstants.LOGGING_SYSTEM_PROPERTY; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; @SpringBootApplication @EnableAutoConfiguration(exclude = { GsonAutoConfiguration.class, KafkaAutoConfiguration.class }) +@ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) }) public class MetronRestApplication { - public static void main(String[] args) { - ParserIndex.reload(); - System.setProperty(LOGGING_SYSTEM_PROPERTY, "none"); - SpringApplication.run(MetronRestApplication.class, args); - } + public static void main(String[] args) { + ParserIndex.reload(); + System.setProperty(MetronRestConstants.LOGGING_SYSTEM_PROPERTY, "none"); + SpringApplication.run(MetronRestApplication.class, args); + } } diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java deleted file mode 100644 index f84cdfabab..0000000000 --- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/config/WebSecurityConfig.java +++ /dev/null @@ -1,108 +0,0 @@ -/** - * 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.rest.config; - -import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_ADMIN; -import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_USER; - -import org.apache.metron.rest.MetronRestConstants; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.crypto.password.NoOpPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler; -import org.springframework.security.web.csrf.CookieCsrfTokenRepository; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import javax.sql.DataSource; -import java.util.Arrays; -import java.util.List; - -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity(securedEnabled = true) -@Controller -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - - @Autowired - private Environment environment; - - @RequestMapping(value = {"/login", "/logout", "/sensors", "/sensors*/**"}, method = RequestMethod.GET) - public String handleNGRequests() { - return "forward:/index.html"; - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http - .authorizeRequests() - .antMatchers("/", "/home", "/login").permitAll() - .antMatchers("/app/**").permitAll() - .antMatchers("/vendor/**").permitAll() - .antMatchers("/fonts/**").permitAll() - .antMatchers("/assets/images/**").permitAll() - .antMatchers("/*.js").permitAll() - .antMatchers("/*.ttf").permitAll() - .antMatchers("/*.woff2").permitAll() - .anyRequest().authenticated() - .and().httpBasic() - .and() - .logout() - .logoutSuccessHandler(new HttpStatusReturningLogoutSuccessHandler()) - .invalidateHttpSession(true) - .deleteCookies("JSESSIONID"); - if (Arrays.asList(environment.getActiveProfiles()).contains(MetronRestConstants.CSRF_ENABLE_PROFILE)) { - http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()); - } else { - http.csrf().disable(); - } - } - - @Autowired - private DataSource dataSource; - - @Autowired - public void configureJdbc(AuthenticationManagerBuilder auth) throws Exception { - List activeProfiles = Arrays.asList(environment.getActiveProfiles()); - if (activeProfiles.contains(MetronRestConstants.DEV_PROFILE) || - activeProfiles.contains(MetronRestConstants.TEST_PROFILE)) { - auth.jdbcAuthentication().dataSource(dataSource) - .withUser("user").password("password").roles(SECURITY_ROLE_USER).and() - .withUser("user1").password("password").roles(SECURITY_ROLE_USER).and() - .withUser("user2").password("password").roles(SECURITY_ROLE_USER).and() - .withUser("admin").password("password").roles(SECURITY_ROLE_USER, SECURITY_ROLE_ADMIN); - } else { - auth.jdbcAuthentication().dataSource(dataSource); - } - } - - @Bean - public PasswordEncoder passwordEncoder() { - return NoOpPasswordEncoder.getInstance(); - } -} diff --git a/metron-interface/metron-rest/src/main/resources/application-test.yml b/metron-interface/metron-rest/src/main/resources/application-test.yml index 0e794cbf58..e6532faf25 100644 --- a/metron-interface/metron-rest/src/main/resources/application-test.yml +++ b/metron-interface/metron-rest/src/main/resources/application-test.yml @@ -59,3 +59,22 @@ meta: dao: # By default, we use the InMemoryMetaAlertDao for our tests impl: org.apache.metron.indexing.dao.InMemoryMetaAlertDao + +knox: + sso: + url: + +ldap: + provider: + url: ldap://localhost:33389 + userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org + password: password + user: + dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org + passwordAttribute: userPassword + searchBase: ou=people,dc=hadoop,dc=apache,dc=org + searchFilter: "" + group: + searchBase: ou=groups,dc=hadoop,dc=apache,dc=org + searchFilter: "member={0}" + roleAttribute: "cn" diff --git a/metron-interface/metron-rest/src/main/scripts/metron-rest.sh b/metron-interface/metron-rest/src/main/scripts/metron-rest.sh index c0c9fac581..728b60b38e 100644 --- a/metron-interface/metron-rest/src/main/scripts/metron-rest.sh +++ b/metron-interface/metron-rest/src/main/scripts/metron-rest.sh @@ -81,10 +81,15 @@ echo "METRON_SPRING_PROFILES_ACTIVE=${METRON_SPRING_PROFILES_ACTIVE}" # the vagrant Spring profile provides configuration values, otherwise configuration is provided by rest_application.yml if [[ !(${METRON_SPRING_PROFILES_ACTIVE} == *"vagrant"*) ]]; then - METRON_CONFIG_LOCATION=" --spring.config.location=$METRON_HOME/config/rest_application.yml,classpath:/application.yml" + METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application.yml,$METRON_HOME/config/rest_application.yml,$METRON_HOME/config/rest_security.yml" + echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}" + METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION} +else + METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application-vagrant.yml,$METRON_HOME/config/rest_application.yml,$METRON_HOME/config/rest_security.yml" echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}" METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION} fi + METRON_SPRING_OPTIONS+=" --server.port=$METRON_REST_PORT" if [ ${METRON_SPRING_PROFILES_ACTIVE} ]; then METRON_PROFILES_ACTIVE=" --spring.profiles.active=${METRON_SPRING_PROFILES_ACTIVE}" diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java index 008f3fcb20..9c1cecd18b 100644 --- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java +++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java @@ -41,9 +41,12 @@ import org.apache.metron.rest.mock.MockStormCLIClientWrapper; import org.apache.metron.rest.mock.MockStormRestTemplate; import org.apache.metron.rest.service.impl.StormCLIWrapper; +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; +import org.springframework.core.io.ClassPathResource; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.web.client.RestTemplate; @@ -185,4 +188,19 @@ public RestTemplate restTemplate(StormCLIWrapper stormCLIClientWrapper) { public UserSettingsClient userSettingsClient() throws RestException, IOException { return new UserSettingsClient(new MockHBaseTableProvider().addToCache("user_settings", "cf"), Bytes.toBytes("cf")); } + + /** + * PropertyPlaceholderConfigurer bean required to make SPEL Values injectable in + * Tests from YAML config. + * + */ + @Bean + public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { + YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); + yaml.setResources(new ClassPathResource("application-test.yml")); + PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer(); + propertyPlaceholderConfigurer.setProperties(yaml.getObject()); + return propertyPlaceholderConfigurer; + } + } diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java new file mode 100644 index 0000000000..04e82b90c1 --- /dev/null +++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestSecurityConfig.java @@ -0,0 +1,44 @@ +/** + * 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.rest.config; + +import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_ADMIN; +import static org.apache.metron.rest.MetronRestConstants.SECURITY_ROLE_USER; +import static org.apache.metron.rest.MetronRestConstants.TEST_PROFILE; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.test.context.ActiveProfiles; + +@Configuration +@ActiveProfiles(TEST_PROFILE) +@Order(99) +public class TestSecurityConfig extends WebSecurityConfigurerAdapter { + @Override + public void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth.inMemoryAuthentication() + .withUser("user").password("password").roles(SECURITY_ROLE_USER).and() + .withUser("user1").password("password").roles(SECURITY_ROLE_USER).and() + .withUser("user2").password("password").roles(SECURITY_ROLE_USER).and() + .withUser("admin").password("password").roles(SECURITY_ROLE_USER, SECURITY_ROLE_ADMIN); + // @formatter:on + } +} \ No newline at end of file From 824763624ec9e8abce94db0e5aaf51d7c92a6506 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 10:52:28 +0100 Subject: [PATCH 04/24] Clean up assembly build process based on new packaging of uis --- metron-interface/metron-alerts/.gitignore | 3 + metron-interface/metron-alerts/assembly.xml | 55 ----------------- metron-interface/metron-config/.gitignore | 3 + metron-interface/metron-config/assembly.xml | 65 --------------------- 4 files changed, 6 insertions(+), 120 deletions(-) delete mode 100644 metron-interface/metron-alerts/assembly.xml delete mode 100644 metron-interface/metron-config/assembly.xml diff --git a/metron-interface/metron-alerts/.gitignore b/metron-interface/metron-alerts/.gitignore index 703c7a902b..de23844151 100644 --- a/metron-interface/metron-alerts/.gitignore +++ b/metron-interface/metron-alerts/.gitignore @@ -4,3 +4,6 @@ metron-alerts.iml node_modules/ /dist/ coverage + +# Build Files +bin/ diff --git a/metron-interface/metron-alerts/assembly.xml b/metron-interface/metron-alerts/assembly.xml deleted file mode 100644 index f392a66c1d..0000000000 --- a/metron-interface/metron-alerts/assembly.xml +++ /dev/null @@ -1,55 +0,0 @@ - - - - archive - - tar.gz - - false - - - ${project.basedir}/dist - /web/alerts-ui - - **/.npmignore - - 0644 - - - ${project.basedir}/scripts - web/expressjs - - alerts-server.js - - 0644 - - - ${project.basedir}/scripts - bin - true - - metron-alerts-ui - - 0755 - unix - true - - - - - src/favicon.ico - /web/alerts-ui - - - diff --git a/metron-interface/metron-config/.gitignore b/metron-interface/metron-config/.gitignore index 4be186c4cc..82c2d7a1bf 100644 --- a/metron-interface/metron-config/.gitignore +++ b/metron-interface/metron-config/.gitignore @@ -40,3 +40,6 @@ testem.log #System Files .DS_Store Thumbs.db + +# Build Files +bin/ diff --git a/metron-interface/metron-config/assembly.xml b/metron-interface/metron-config/assembly.xml deleted file mode 100644 index c07fbd63a3..0000000000 --- a/metron-interface/metron-config/assembly.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - archive - - tar.gz - - false - - - ${project.basedir}/dist - web/management-ui - - **/.npmignore - - 0644 - - - ${project.basedir}/scripts - web/expressjs - - package.json - server.js - - 0644 - - - ${project.basedir}/scripts - bin - true - - metron-management-ui - - 0755 - unix - true - - - ${project.basedir} - web/management-ui/license - - LICENSE - NOTICE - - 0644 - - - - - src/favicon.ico - web/management-ui - - - From bb83e0bac91581410c5bb8f62e112c930cd0d89b Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 11:04:44 +0100 Subject: [PATCH 05/24] Fixed security test for UI settings --- .../rest/controller/AlertsUIController.java | 6 ++++-- .../apache/metron/rest/config/TestConfig.java | 2 -- .../AlertsUIControllerIntegrationTest.java | 18 ++++++++++++------ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java index fe2968fe67..660eb18b09 100644 --- a/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java +++ b/metron-interface/metron-rest/src/main/java/org/apache/metron/rest/controller/AlertsUIController.java @@ -34,6 +34,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.parameters.P; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -103,7 +105,7 @@ ResponseEntity save(@ApiParam(name = "alertsUIUserSettings", value = return responseEntity; } - @Secured({"ROLE_" + SECURITY_ROLE_ADMIN}) + @PreAuthorize("hasRole('ROLE_" + SECURITY_ROLE_ADMIN + "') or #user == authentication.principal.username") @ApiOperation(value = "Deletes a user's settings. Only users that are part of " + "the \"ROLE_ADMIN\" role are allowed to delete user settings.") @ApiResponses(value = {@ApiResponse(message = "User settings were deleted", code = 200), @@ -113,7 +115,7 @@ ResponseEntity save(@ApiParam(name = "alertsUIUserSettings", value = @RequestMapping(value = "/settings/{user}", method = RequestMethod.DELETE) ResponseEntity delete( @ApiParam(name = "user", value = "The user whose settings will be deleted", required = true) - @PathVariable String user) + @P("user") @PathVariable String user) throws RestException { if (alertsUIService.deleteAlertsUIUserSettings(user)) { return new ResponseEntity<>(HttpStatus.OK); diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java index 9c1cecd18b..27a510f3d2 100644 --- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java +++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java @@ -183,7 +183,6 @@ public RestTemplate restTemplate(StormCLIWrapper stormCLIClientWrapper) { return AdminUtils$.MODULE$; } - @Bean() public UserSettingsClient userSettingsClient() throws RestException, IOException { return new UserSettingsClient(new MockHBaseTableProvider().addToCache("user_settings", "cf"), Bytes.toBytes("cf")); @@ -202,5 +201,4 @@ public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { propertyPlaceholderConfigurer.setProperties(yaml.getObject()); return propertyPlaceholderConfigurer; } - } diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java index 49863d670d..98b4819ae9 100644 --- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java +++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/controller/AlertsUIControllerIntegrationTest.java @@ -157,7 +157,7 @@ public void testSecurity() throws Exception { .andExpect(status().isUnauthorized()); this.mockMvc.perform(get(alertUrl + "/settings/all").with(httpBasic(user1, password)).with(csrf())) .andExpect(status().isForbidden()); - this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(user1, password)).with(csrf())) + this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(user2, password)).with(csrf())) .andExpect(status().isForbidden()); } @@ -275,13 +275,16 @@ private void alertsProfilesShouldBeCreatedOrUpdated() throws Exception { * @throws Exception */ private void alertsProfilesShouldBeProperlyDeleted() throws Exception { - // user1 deletes their profile - this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(admin, password))) + this.mockMvc.perform(delete(alertUrl + "/settings/user1") + .with(httpBasic(admin, password)) + .with(csrf())) .andExpect(status().isOk()); // user1 should get a 404 when trying to delete an alerts profile that doesn't exist - this.mockMvc.perform(delete(alertUrl + "/settings/user1").with(httpBasic(admin, password))) + this.mockMvc.perform(delete(alertUrl + "/settings/user1") + .with(httpBasic(admin, password)) + .with(csrf())) .andExpect(status().isNotFound()); // user1 should get a 404 when trying to retrieve their alerts profile @@ -289,7 +292,8 @@ private void alertsProfilesShouldBeProperlyDeleted() throws Exception { .andExpect(status().isNotFound()); // user2's alerts profile should still exist - this.mockMvc.perform(get(alertUrl + "/settings").with(httpBasic(user2, password))) + this.mockMvc.perform(get(alertUrl + "/settings") + .with(httpBasic(user2, password))) .andExpect(status().isOk()) .andExpect( content().contentType(MediaType.parseMediaType("application/json;charset=UTF-8"))) @@ -303,7 +307,9 @@ private void alertsProfilesShouldBeProperlyDeleted() throws Exception { .andExpect(content().json("{\"" + user2 + "\": " + user2AlertUserSettingsJson + "}")); // user2 deletes their profile - this.mockMvc.perform(delete(alertUrl + "/settings/user2").with(httpBasic(admin, password))) + this.mockMvc.perform(delete(alertUrl + "/settings/user2") + .with(httpBasic(admin, password)) + .with(csrf())) .andExpect(status().isOk()); // user2 should get a 404 when trying to delete an alerts profile that doesn't exist From dd6f3fb9eff2eafb3b8049abc836d7cb476a2396 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 11:05:59 +0100 Subject: [PATCH 06/24] Added hosting applications for UIs --- .../metron-alerts-host/.gitignore | 24 +++ metron-interface/metron-alerts-host/pom.xml | 143 ++++++++++++++++++ .../src/main/assembly/assembly.xml | 39 +++++ .../apache/metron/ui/AlertsApplication.java | 31 ++++ .../src/main/resources/application.yml | 61 ++++++++ .../src/main/scripts/metron-alerts.sh | 54 +++++++ metron-interface/metron-config-host/pom.xml | 143 ++++++++++++++++++ .../src/main/assembly/assembly.xml | 39 +++++ .../apache/metron/ui/ConfigApplication.java | 31 ++++ .../src/main/resources/application.yml | 34 +++++ .../src/main/scripts/metron-config.sh | 54 +++++++ .../metron-rest-client/bin/pom.xml | 52 +++++++ metron-interface/pom.xml | 2 + 13 files changed, 707 insertions(+) create mode 100644 metron-interface/metron-alerts-host/.gitignore create mode 100644 metron-interface/metron-alerts-host/pom.xml create mode 100644 metron-interface/metron-alerts-host/src/main/assembly/assembly.xml create mode 100644 metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java create mode 100644 metron-interface/metron-alerts-host/src/main/resources/application.yml create mode 100644 metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh create mode 100644 metron-interface/metron-config-host/pom.xml create mode 100644 metron-interface/metron-config-host/src/main/assembly/assembly.xml create mode 100644 metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java create mode 100644 metron-interface/metron-config-host/src/main/resources/application.yml create mode 100644 metron-interface/metron-config-host/src/main/scripts/metron-config.sh create mode 100644 metron-interface/metron-rest-client/bin/pom.xml diff --git a/metron-interface/metron-alerts-host/.gitignore b/metron-interface/metron-alerts-host/.gitignore new file mode 100644 index 0000000000..e0dea388e4 --- /dev/null +++ b/metron-interface/metron-alerts-host/.gitignore @@ -0,0 +1,24 @@ +/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ diff --git a/metron-interface/metron-alerts-host/pom.xml b/metron-interface/metron-alerts-host/pom.xml new file mode 100644 index 0000000000..76d1f73afc --- /dev/null +++ b/metron-interface/metron-alerts-host/pom.xml @@ -0,0 +1,143 @@ + + + + + 4.0.0 + + org.apache.metron.ui + metron-alerts-host + jar + + Metron Alerts UI Host + Spring Server to run frontend + + + org.apache.metron + metron-interface + 0.5.1 + + + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + + + + + org.apache.metron + metron-alerts + ${project.parent.version} + + + + org.apache.metron + metron-ui-host + ${project.parent.version} + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + org.apache.logging.log4j + log4j-slf4j-impl + + + ch.qos.logback + logback-classic + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + org.apache.metron.ui.AlertsApplication + + metron-alerts + Metron Alerts UI server + Metron Alerts + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-shared-resources + + unpack-dependencies + + generate-resources + + ${project.build.directory}/generated-resources + shared-resources + ${project.groupId} + true + + + + + + maven-assembly-plugin + + src/main/assembly/assembly.xml + + + + package + + single + + + + + + + diff --git a/metron-interface/metron-alerts-host/src/main/assembly/assembly.xml b/metron-interface/metron-alerts-host/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..1377f6048d --- /dev/null +++ b/metron-interface/metron-alerts-host/src/main/assembly/assembly.xml @@ -0,0 +1,39 @@ + + + + archive + + tar.gz + + false + + + ${project.basedir}/target + + ${project.artifactId}-${project.version}.jar + + lib + true + + + ${project.basedir}/src/main/scripts + + metron-alerts.sh + + bin + true + true + + + diff --git a/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java b/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java new file mode 100644 index 0000000000..d6f0dfed73 --- /dev/null +++ b/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.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; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; + +@SpringBootApplication +@ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) }) +public class AlertsApplication extends AbstractHostApplication { + public static void main(String[] args) { + SpringApplication.run(AlertsApplication.class, args); + } +} diff --git a/metron-interface/metron-alerts-host/src/main/resources/application.yml b/metron-interface/metron-alerts-host/src/main/resources/application.yml new file mode 100644 index 0000000000..3eb9045a7e --- /dev/null +++ b/metron-interface/metron-alerts-host/src/main/resources/application.yml @@ -0,0 +1,61 @@ +# 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. + +metron: + version: ${project.version} + +server: + session: + cookie: + secure: false + name: METRONUISESSION + +zuul: + routes: + rest: + path: /api/v1/** + url: http://localhost:8082/api/v1/ + sensitiveHeaders: Cookie,Set-Cookie + +proxy: + auth: + routes: + rest: passthru + +ribbon: + ConnectTimeout: 3000 + ReadTimeout: 60000 + + +ldap: + provider: + url: ldap://localhost:33389 + userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org + password: admin-password + user: + dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org + passwordAttribute: userPassword + searchBase: ou=people,dc=hadoop,dc=apache,dc=org + searchFilter: "" + group: + searchBase: ou=groups,dc=hadoop,dc=apache,dc=org + searchFilter: "member={0}" + roleAttribute: "cn" + +knox: + sso: + url: '' + pubkey: '' \ No newline at end of file diff --git a/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh b/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh new file mode 100644 index 0000000000..e974b8999f --- /dev/null +++ b/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# 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. +# + +if [ -z "${METRON_SSL_PASSWORD}" ]; then + echo "METRON_SSL_PASSWORD unset." +fi + +METRON_VERSION=${project.version} +METRON_HOME="${METRON_HOME:-/usr/metron/${METRON_VERSION}}" +METRON_SYSCONFIG="${METRON_SYSCONFIG:-/etc/default/metron}" + +echo "METRON_VERSION=${METRON_VERSION}" +echo "METRON_HOME=${METRON_HOME}" +echo "METRON_SYSCONFIG=${METRON_SYSCONFIG}" + +if [ -f "$METRON_SYSCONFIG" ]; then + echo "METRON_SYSCONFIG=${METRON_SYSCONFIG}" + set -a + . "$METRON_SYSCONFIG" +fi + +echo "METRON_SPRING_PROFILES_ACTIVE=${METRON_SPRING_PROFILES_ACTIVE}" + +METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application.yml,$METRON_HOME/config/alerts_ui.yml" +echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}" +METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION} + +# Find the metron alerts jar +files=( "${METRON_HOME}/lib/metron-alerts-host-*.jar" ) +echo "Default metron-alerts-host jar is: ${files[0]}" +APP_JAR="${files[0]}" + +export CONF_FOLDER=$METHRON_HOME/config +export LOG_FOLDER=/var/log/metron/ +export RUN_ARGS=$METRON_SPRING_OPTIONS +export APP_NAME=metron-alerts +export MODE=service +${APP_JAR} $1 diff --git a/metron-interface/metron-config-host/pom.xml b/metron-interface/metron-config-host/pom.xml new file mode 100644 index 0000000000..cc01a0eb2c --- /dev/null +++ b/metron-interface/metron-config-host/pom.xml @@ -0,0 +1,143 @@ + + + + + 4.0.0 + + org.apache.metron.ui + metron-config-host + jar + + Metron Config UI Host + Spring Server to host config ui + + + org.apache.metron + metron-interface + 0.5.1 + + + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + + + + + org.apache.metron + metron-config + ${project.parent.version} + + + + org.apache.metron + metron-ui-host + ${project.parent.version} + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + org.apache.logging.log4j + log4j-slf4j-impl + + + ch.qos.logback + logback-classic + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + org.apache.metron.ui.ConfigApplication + + metron-config + Metron Management UI server + Metron Management + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-shared-resources + + unpack-dependencies + + generate-resources + + ${project.build.directory}/generated-resources + shared-resources + ${project.groupId} + true + + + + + + maven-assembly-plugin + + src/main/assembly/assembly.xml + + + + package + + single + + + + + + + + diff --git a/metron-interface/metron-config-host/src/main/assembly/assembly.xml b/metron-interface/metron-config-host/src/main/assembly/assembly.xml new file mode 100644 index 0000000000..e20c39faf7 --- /dev/null +++ b/metron-interface/metron-config-host/src/main/assembly/assembly.xml @@ -0,0 +1,39 @@ + + + + archive + + tar.gz + + false + + + ${project.basedir}/target + + ${project.artifactId}-${project.version}.jar + + lib + true + + + ${project.basedir}/src/main/scripts + + metron-config.sh + + bin + true + true + + + diff --git a/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java b/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java new file mode 100644 index 0000000000..ab08da1a2b --- /dev/null +++ b/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.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; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.ComponentScans; + +@SpringBootApplication +@ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) }) +public class ConfigApplication extends AbstractHostApplication { + public static void main(String[] args) { + SpringApplication.run(ConfigApplication.class, args); + } +} diff --git a/metron-interface/metron-config-host/src/main/resources/application.yml b/metron-interface/metron-config-host/src/main/resources/application.yml new file mode 100644 index 0000000000..906b739300 --- /dev/null +++ b/metron-interface/metron-config-host/src/main/resources/application.yml @@ -0,0 +1,34 @@ +# 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. +metron: + version: 0.5.0 + +logging: + level: + root: INFO + +server: + port: 4200 + +zuul: + routes: + rest: + path: /api/v1/** + url: http://localhost:8082/api/v1 + +ribbon: + ConnectTimeout: 3000 + ReadTimeout: 60000 \ No newline at end of file diff --git a/metron-interface/metron-config-host/src/main/scripts/metron-config.sh b/metron-interface/metron-config-host/src/main/scripts/metron-config.sh new file mode 100644 index 0000000000..90d275a393 --- /dev/null +++ b/metron-interface/metron-config-host/src/main/scripts/metron-config.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# 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. +# + +if [ -z "${METRON_SSL_PASSWORD}" ]; then + echo "METRON_SSL_PASSWORD unset." +fi + +METRON_VERSION=${project.version} +METRON_HOME="${METRON_HOME:-/usr/metron/${METRON_VERSION}}" +METRON_SYSCONFIG="${METRON_SYSCONFIG:-/etc/default/metron}" + +echo "METRON_VERSION=${METRON_VERSION}" +echo "METRON_HOME=${METRON_HOME}" +echo "METRON_SYSCONFIG=${METRON_SYSCONFIG}" + +if [ -f "$METRON_SYSCONFIG" ]; then + echo "METRON_SYSCONFIG=${METRON_SYSCONFIG}" + set -a + . "$METRON_SYSCONFIG" +fi + +echo "METRON_SPRING_PROFILES_ACTIVE=${METRON_SPRING_PROFILES_ACTIVE}" + +METRON_CONFIG_LOCATION=" --spring.config.location=classpath:/application.yml,$METRON_HOME/config/config_ui.yml" +echo "METRON_CONFIG_LOCATION=${METRON_CONFIG_LOCATION}" +METRON_SPRING_OPTIONS+=${METRON_CONFIG_LOCATION} + +# Find the metron alerts jar +files=( "${METRON_HOME}/lib/metron-config-host-*.jar" ) +echo "Default metron-alerts-host jar is: ${files[0]}" +APP_JAR="${files[0]}" + +export CONF_FOLDER=$METHRON_HOME/config +export LOG_FOLDER=/var/log/metron/ +export RUN_ARGS=$METRON_SPRING_OPTIONS +export APP_NAME=metron-config +export MODE=service +${APP_JAR} $1 \ No newline at end of file diff --git a/metron-interface/metron-rest-client/bin/pom.xml b/metron-interface/metron-rest-client/bin/pom.xml new file mode 100644 index 0000000000..9ef9ae62db --- /dev/null +++ b/metron-interface/metron-rest-client/bin/pom.xml @@ -0,0 +1,52 @@ + + + + + 4.0.0 + + org.apache.metron + metron-interface + 0.5.1 + + metron-rest-client + https://metron.apache.org/ + + 2.1.1 + + + + org.apache.metron + metron-common + ${project.parent.version} + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.apache.metron + metron-indexing + ${project.parent.version} + + + org.eclipse.persistence + javax.persistence + ${eclipse.javax.persistence.version} + + + + diff --git a/metron-interface/pom.xml b/metron-interface/pom.xml index 467781b37d..c91de033b1 100644 --- a/metron-interface/pom.xml +++ b/metron-interface/pom.xml @@ -42,7 +42,9 @@ metron-ui-security metron-ui-host metron-config + metron-config-host metron-alerts + metron-alerts-host metron-rest metron-rest-client From 278f9cb40dca4d0ef1e0239ec34f85062c0d852f Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 12 Jul 2018 18:43:00 +0100 Subject: [PATCH 07/24] Removed nodejs from rpm build process --- metron-deployment/packaging/ambari/metron-mpack/README.md | 2 -- .../resources/common-services/METRON/CURRENT/metainfo.xml | 3 --- metron-deployment/packaging/docker/ansible-docker/Dockerfile | 3 --- metron-deployment/packaging/docker/deb-docker/Dockerfile | 4 ---- .../packaging/docker/deb-docker/prepackage/metron-config | 3 --- metron-deployment/packaging/docker/rpm-docker/Dockerfile | 4 ---- 6 files changed, 19 deletions(-) diff --git a/metron-deployment/packaging/ambari/metron-mpack/README.md b/metron-deployment/packaging/ambari/metron-mpack/README.md index cd9399d555..785d20e71e 100644 --- a/metron-deployment/packaging/ambari/metron-mpack/README.md +++ b/metron-deployment/packaging/ambari/metron-mpack/README.md @@ -26,8 +26,6 @@ This allows you to easily install Metron using a simple, guided process. This a * Installable Metron packages (either RPMs or DEBs) located in a repository on each host at `/localrepo`. -* A [Node.js](https://nodejs.org/en/download/package-manager/) repository installed on the host running the Management and Alarm UI. - ### Quick Start 1. Build the Metron MPack. Execute the following command from the project's root directory. diff --git a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/metainfo.xml b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/metainfo.xml index f83d93bb66..85302a990f 100644 --- a/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/metainfo.xml +++ b/metron-deployment/packaging/ambari/metron-mpack/src/main/resources/common-services/METRON/CURRENT/metainfo.xml @@ -421,9 +421,6 @@ metron-rest - - nodejs - metron-config diff --git a/metron-deployment/packaging/docker/ansible-docker/Dockerfile b/metron-deployment/packaging/docker/ansible-docker/Dockerfile index ebf002ae98..d5f7d24763 100644 --- a/metron-deployment/packaging/docker/ansible-docker/Dockerfile +++ b/metron-deployment/packaging/docker/ansible-docker/Dockerfile @@ -58,8 +58,5 @@ RUN yum -y install asciidoc rpm-build rpm2cpio tar unzip xmlto zip rpmlint make # create a .bashrc for root, enabling the cpp 11 toolset RUN touch /root/.bashrc \ && cat '/opt/rh/devtoolset-4/enable' >> /root/.bashrc -# install node so that the node dependencies can be packaged into the RPMs -RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - -RUN yum -y install nodejs WORKDIR /root diff --git a/metron-deployment/packaging/docker/deb-docker/Dockerfile b/metron-deployment/packaging/docker/deb-docker/Dockerfile index 44203c6fb2..d4e1f91c7b 100644 --- a/metron-deployment/packaging/docker/deb-docker/Dockerfile +++ b/metron-deployment/packaging/docker/deb-docker/Dockerfile @@ -26,8 +26,4 @@ RUN apt-get update && apt-get install -y \ dpkg-dev \ gettext -# install nodejs so that the node dependencies can be packaged into the DEBs -RUN curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - -RUN apt-get install -y nodejs - WORKDIR /root diff --git a/metron-deployment/packaging/docker/deb-docker/prepackage/metron-config b/metron-deployment/packaging/docker/deb-docker/prepackage/metron-config index a5ac74f7e3..90bf183dee 100644 --- a/metron-deployment/packaging/docker/deb-docker/prepackage/metron-config +++ b/metron-deployment/packaging/docker/deb-docker/prepackage/metron-config @@ -27,6 +27,3 @@ # the working directory containing all of the files to # be packaged will be passed as the only argument to this script. # - -# package the 'production' node dependencies -npm install --prefix="${PACKAGE_WORKDIR}/${METRON_HOME}/web/expressjs" --only=production diff --git a/metron-deployment/packaging/docker/rpm-docker/Dockerfile b/metron-deployment/packaging/docker/rpm-docker/Dockerfile index a2dae8e04b..2fd25654b0 100644 --- a/metron-deployment/packaging/docker/rpm-docker/Dockerfile +++ b/metron-deployment/packaging/docker/rpm-docker/Dockerfile @@ -27,7 +27,3 @@ RUN mv apache-maven-3.2.5 /opt/maven RUN ln -s /opt/maven/bin/mvn /usr/bin/mvn RUN yum -y install asciidoc rpm-build rpm2cpio tar unzip xmlto zip rpmlint && yum clean all WORKDIR /root - -# install node so that the node dependencies can be packaged into the RPMs -RUN curl --silent --location https://rpm.nodesource.com/setup_6.x | bash - -RUN yum -y install gcc-c++ make nodejs From c2fde46d5504769b19e9fd0872dcaf55ec6fbb6f Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 19 Jul 2018 12:19:44 +0100 Subject: [PATCH 08/24] RPM changes for new hosting method --- .../docker/rpm-docker/SPECS/metron.spec | 97 ++++--------------- 1 file changed, 19 insertions(+), 78 deletions(-) diff --git a/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec b/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec index 4b88fd0b82..df712ad629 100644 --- a/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec +++ b/metron-deployment/packaging/docker/rpm-docker/SPECS/metron.spec @@ -53,10 +53,10 @@ Source6: metron-indexing-%{full_version}-archive.tar.gz Source7: metron-pcap-backend-%{full_version}-archive.tar.gz Source8: metron-profiler-%{full_version}-archive.tar.gz Source9: metron-rest-%{full_version}-archive.tar.gz -Source10: metron-config-%{full_version}-archive.tar.gz +Source10: metron-config-host-%{full_version}-archive.tar.gz Source11: metron-management-%{full_version}-archive.tar.gz Source12: metron-maas-service-%{full_version}-archive.tar.gz -Source13: metron-alerts-%{full_version}-archive.tar.gz +Source13: metron-alerts-host-%{full_version}-archive.tar.gz Source14: metron-performance-%{full_version}-archive.tar.gz %description @@ -77,7 +77,11 @@ rm -rf %{_builddir}/* %install rm -rf %{buildroot} mkdir -p %{buildroot}%{metron_home} -mkdir -p %{buildroot}/etc/init.d + +# make PID locations for metron uis +mkdir -p %{buildroot}/var/run/metron-alerts +mkdir -p %{buildroot}/var/run/metron-config +mkdir -p %{buildroot}/var/run/metron-rest # copy source files and untar tar -xzf %{SOURCE0} -C %{buildroot}%{metron_home} @@ -96,12 +100,6 @@ tar -xzf %{SOURCE12} -C %{buildroot}%{metron_home} tar -xzf %{SOURCE13} -C %{buildroot}%{metron_home} tar -xzf %{SOURCE14} -C %{buildroot}%{metron_home} -install %{buildroot}%{metron_home}/bin/metron-management-ui %{buildroot}/etc/init.d/ -install %{buildroot}%{metron_home}/bin/metron-alerts-ui %{buildroot}/etc/init.d/ - -# allows node dependencies to be packaged in the RPMs -npm install --prefix="%{buildroot}%{metron_home}/web/expressjs" --only=production - # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %package common @@ -435,41 +433,12 @@ This package installs the Metron Management UI %{metron_home} %defattr(-,root,root,755) %dir %{metron_root} %dir %{metron_home} +%dir %{metron_home}/config %dir %{metron_home}/bin -%dir %{metron_home}/web -%dir %{metron_home}/web/expressjs -%dir %{metron_home}/web/expressjs/node_modules -%dir %{metron_home}/web/expressjs/node_modules/.bin -%dir %{metron_home}/web/management-ui -%dir %{metron_home}/web/management-ui/assets -%dir %{metron_home}/web/management-ui/assets/ace -%dir %{metron_home}/web/management-ui/assets/ace/snippets -%dir %{metron_home}/web/management-ui/assets/fonts -%dir %{metron_home}/web/management-ui/assets/fonts/Roboto -%dir %{metron_home}/web/management-ui/assets/images -%dir %{metron_home}/web/management-ui/license -%{metron_home}/bin/metron-management-ui -/etc/init.d/metron-management-ui -%attr(0755,root,root) %{metron_home}/web/expressjs/node_modules/* -%attr(0755,root,root) %{metron_home}/web/expressjs/node_modules/.bin/* -%attr(0755,root,root) %{metron_home}/web/expressjs/server.js -%attr(0644,root,root) %{metron_home}/web/expressjs/package.json -%attr(0644,root,root) %{metron_home}/web/management-ui/favicon.ico -%attr(0644,root,root) %{metron_home}/web/management-ui/index.html -%attr(0644,root,root) %{metron_home}/web/management-ui/*.js -%attr(0644,root,root) %{metron_home}/web/management-ui/*.js.gz -%attr(0644,root,root) %{metron_home}/web/management-ui/*.ttf -%attr(0644,root,root) %{metron_home}/web/management-ui/*.svg -%attr(0644,root,root) %{metron_home}/web/management-ui/*.eot -%attr(0644,root,root) %{metron_home}/web/management-ui/*.woff -%attr(0644,root,root) %{metron_home}/web/management-ui/*.woff2 -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/ace/*.js -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/ace/LICENSE -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/ace/snippets/*.js -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/fonts/Roboto/LICENSE.txt -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/fonts/Roboto/*.ttf -%attr(0644,root,root) %{metron_home}/web/management-ui/assets/images/* -%attr(0644,root,root) %{metron_home}/web/management-ui/license/* +%dir %{metron_home}/lib +%attr(0755,root,root) %{metron_home}/lib/metron-config-host-%{full_version}.jar +%attr(0755,root,root) %{metron_home}/bin/metron-config.sh + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -504,46 +473,18 @@ This package installs the Metron Alerts UI %{metron_home} %defattr(-,root,root,755) %dir %{metron_root} %dir %{metron_home} +%dir %{metron_home}/config %dir %{metron_home}/bin -%dir %{metron_home}/web -%dir %{metron_home}/web/alerts-ui -%dir %{metron_home}/web/alerts-ui/assets -%dir %{metron_home}/web/alerts-ui/assets/ace -%dir %{metron_home}/web/alerts-ui/assets/fonts -%dir %{metron_home}/web/alerts-ui/assets/fonts/Roboto -%dir %{metron_home}/web/alerts-ui/assets/images -%{metron_home}/bin/metron-alerts-ui -/etc/init.d/metron-alerts-ui -%attr(0755,root,root) %{metron_home}/web/expressjs/alerts-server.js -%attr(0644,root,root) %{metron_home}/web/alerts-ui/favicon.ico -%attr(0644,root,root) %{metron_home}/web/alerts-ui/index.html -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.bundle.css -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.js -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.ttf -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.svg -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.jpg -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.eot -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.woff -%attr(0644,root,root) %{metron_home}/web/alerts-ui/*.woff2 -%attr(0644,root,root) %{metron_home}/web/alerts-ui/3rdpartylicenses.txt -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/ace/*.js -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/ace/LICENSE -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/fonts/font.css -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/fonts/Roboto/LICENSE.txt -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/fonts/Roboto/*.ttf -%attr(0644,root,root) %{metron_home}/web/alerts-ui/assets/images/* - -# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +%dir %{metron_home}/lib +%attr(0755,root,root) %{metron_home}/lib/metron-alerts-host-%{full_version}.jar +%attr(0755,root,root) %{metron_home}/bin/metron-alerts.sh -%post config -chkconfig --add metron-management-ui -chkconfig --add metron-alerts-ui -%preun config -chkconfig --del metron-management-ui -chkconfig --del metron-alerts-ui +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ %changelog +* Thu Jul 19 2018 Apache Metron - 0.5.1 +- Added new UI hosting methods * Thu Feb 1 2018 Apache Metron - 0.4.3 - Add Solr install script to Solr RPM * Tue Sep 25 2017 Apache Metron - 0.4.2 From 09a40b7e98f63f4cf1b6f2482e8e695d1dbe9288 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Mon, 23 Jul 2018 12:26:11 +0100 Subject: [PATCH 09/24] Package hosted versions in RPM --- metron-deployment/packaging/docker/rpm-docker/pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metron-deployment/packaging/docker/rpm-docker/pom.xml b/metron-deployment/packaging/docker/rpm-docker/pom.xml index ba570793f9..878f55a72a 100644 --- a/metron-deployment/packaging/docker/rpm-docker/pom.xml +++ b/metron-deployment/packaging/docker/rpm-docker/pom.xml @@ -174,7 +174,7 @@ - ${metron_dir}/metron-interface/metron-config/target/ + ${metron_dir}/metron-interface/metron-config-host/target/ *.tar.gz @@ -186,7 +186,7 @@ - ${metron_dir}/metron-interface/metron-alerts/target/ + ${metron_dir}/metron-interface/metron-alerts-host/target/ *.tar.gz From 12b1b23b75b69977a8c67d6ad2ce3f4252c9de53 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Mon, 23 Jul 2018 19:32:11 +0100 Subject: [PATCH 10/24] Simplified Zuul Error handling --- .../org/apache/metron/ui/ZuulErrorFilter.java | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java index 359d082d4f..9d58c165c6 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/ZuulErrorFilter.java @@ -19,51 +19,49 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; +import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException; import org.springframework.stereotype.Component; import org.springframework.util.ReflectionUtils; -import com.fasterxml.jackson.databind.ObjectMapper; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; @Component public class ZuulErrorFilter extends ZuulFilter { private static final Logger LOG = LoggerFactory.getLogger(ZuulErrorFilter.class); - @Override - public String filterType() { - return "error"; - } + @Override + public boolean shouldFilter() { + return true; + } - @Override - public int filterOrder() { - return 0; - } - - @Override - public boolean shouldFilter() { - return RequestContext.getCurrentContext().getThrowable() != null; - } - - @Override - public Object run() { - try { + @Override + public Object run() throws ZuulException { + try { RequestContext ctx = RequestContext.getCurrentContext(); Throwable throwable = ctx.getThrowable(); - if (throwable != null) { + if (throwable != null && throwable instanceof ZuulException) { LOG.error("Zuul failure: " + throwable.getMessage(), throwable); - ctx.getResponse().setContentType("application/json"); - ZuulError error = new ZuulError(throwable); - ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.writeValue(ctx.getResponse().getOutputStream(), error); - ctx.setResponseStatusCode(500); - + ctx.setThrowable(new ZuulRuntimeException((ZuulException) throwable)); } } catch (Exception ex) { LOG.error("Exception in custom error filter", ex); ReflectionUtils.rethrowRuntimeException(ex); } - return null; - } + return null; + } + + @Override + public String filterType() { + return FilterConstants.ERROR_TYPE; + } + + @Override + public int filterOrder() { + return FilterConstants.SEND_ERROR_FILTER_ORDER - 1; + } + } \ No newline at end of file From 1c1318c5546a086dd321beb4e07884ec1bb17856 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Mon, 30 Jul 2018 13:02:36 +0100 Subject: [PATCH 11/24] Fixes for logout endpoint --- .../java/org/apache/metron/ui/UserController.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index 990da04edf..a6a52f8264 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -17,6 +17,9 @@ */ package org.apache.metron.ui; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.Principal; import java.util.List; import java.util.stream.Collectors; @@ -29,6 +32,7 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -37,7 +41,7 @@ */ @RestController public class UserController { - @Value("knox.sso.url") + @Value("${knox.sso.url}") private String knoxSSOUrl; @RequestMapping(path = "/whoami", method = RequestMethod.GET) @@ -54,8 +58,11 @@ public List user() { } @RequestMapping(path = "/logout", method = RequestMethod.GET) - public void logout(Principal user, HttpServletResponse httpServletResponse) { - String logoutUrl = knoxSSOUrl.replaceAll("(web|knox)sso", "$1ssout"); - httpServletResponse.setHeader("Location", logoutUrl); + public void logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { + StringBuilder logoutUrl = new StringBuilder(knoxSSOUrl.replaceAll("knoxsso", "knoxssout")); + logoutUrl.append(knoxSSOUrl.contains("?") ? "&": "?"); + logoutUrl.append("originalUrl="); + logoutUrl.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name())); + httpServletResponse.setHeader("Location", logoutUrl.toString()); } } From 923593d2620fb4822bc81b81744e36251d087300 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Mon, 30 Jul 2018 16:03:06 +0100 Subject: [PATCH 12/24] Made PID_FOLDER explicit to avoid bug in spring boot launch --- .../metron-alerts-host/src/main/scripts/metron-alerts.sh | 1 + .../metron-config-host/src/main/scripts/metron-config.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh b/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh index e974b8999f..a171cff967 100644 --- a/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh +++ b/metron-interface/metron-alerts-host/src/main/scripts/metron-alerts.sh @@ -48,6 +48,7 @@ APP_JAR="${files[0]}" export CONF_FOLDER=$METHRON_HOME/config export LOG_FOLDER=/var/log/metron/ +export PID_FOLDER=/var/run/metron/ export RUN_ARGS=$METRON_SPRING_OPTIONS export APP_NAME=metron-alerts export MODE=service diff --git a/metron-interface/metron-config-host/src/main/scripts/metron-config.sh b/metron-interface/metron-config-host/src/main/scripts/metron-config.sh index 90d275a393..5d4d72ae74 100644 --- a/metron-interface/metron-config-host/src/main/scripts/metron-config.sh +++ b/metron-interface/metron-config-host/src/main/scripts/metron-config.sh @@ -48,6 +48,7 @@ APP_JAR="${files[0]}" export CONF_FOLDER=$METHRON_HOME/config export LOG_FOLDER=/var/log/metron/ +export PID_FOLDER=/var/run/metron/ export RUN_ARGS=$METRON_SPRING_OPTIONS export APP_NAME=metron-config export MODE=service From b0f1edde522930c5327c1bebc593a905cf87f38e Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Mon, 30 Jul 2018 19:46:30 +0100 Subject: [PATCH 13/24] Fixed redirect on Logout --- .../java/org/apache/metron/ui/UserController.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index a6a52f8264..ce95100902 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -58,11 +58,12 @@ public List user() { } @RequestMapping(path = "/logout", method = RequestMethod.GET) - public void logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { - StringBuilder logoutUrl = new StringBuilder(knoxSSOUrl.replaceAll("knoxsso", "knoxssout")); - logoutUrl.append(knoxSSOUrl.contains("?") ? "&": "?"); - logoutUrl.append("originalUrl="); - logoutUrl.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name())); - httpServletResponse.setHeader("Location", logoutUrl.toString()); + public String logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { + StringBuilder redirect = new StringBuilder("redirect:" ); + redirect.append(knoxSSOUrl.replaceAll("knoxsso", "knoxssout")); + redirect.append(knoxSSOUrl.contains("?") ? "&": "?"); + redirect.append("originalUrl="); + redirect.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name())); + return redirect.toString(); } } From b27a626d77c60d5f09de9574ef29dc9f116d9c94 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 31 Jul 2018 13:22:16 +0100 Subject: [PATCH 14/24] Added ability to handle expires and not before claims in JWT Refactored some tests for JWT --- .../ui/KnoxSSOAuthenticationFilter.java | 15 ++- .../java/org/apache/metron/ui/JWTTests.java | 116 +++++++++++++----- .../ui/KnoxSSOAuthenticationFilterTests.java | 1 + 3 files changed, 102 insertions(+), 30 deletions(-) diff --git a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java index dfd027b91a..8cd64d000d 100644 --- a/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java +++ b/metron-interface/metron-ui-security/src/main/java/org/apache/metron/ui/KnoxSSOAuthenticationFilter.java @@ -26,6 +26,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPublicKey; import java.text.ParseException; +import java.util.Date; import java.util.List; import javax.servlet.Filter; @@ -115,7 +116,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha if (valid) { String userName = jwtToken.getJWTClaimsSet().getSubject(); LOG.info("SSO login user : {} ", userName); - // if we get the userName from the token then log into atlas using the same user if (userName != null && !userName.trim().isEmpty()) { List grantedAuths = MetronAuthenticationProvider .getAuthoritiesFromUGI(userName); @@ -127,6 +127,17 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha Authentication authentication = authenticationProvider.authenticate(finalAuthentication); SecurityContextHolder.getContext().setAuthentication(authentication); } + Date expirationTime = jwtToken.getJWTClaimsSet().getExpirationTime(); + Date notBeforeTime = jwtToken.getJWTClaimsSet().getNotBeforeTime(); + Date now = new Date(); + if (expirationTime != null && now.after(expirationTime)) { + LOG.info("SSO token expired: {} ", userName); + redirectToKnox(httpRequest, httpResponse, chain); + } + if (notBeforeTime != null && now.before(notBeforeTime)) { + LOG.info("SSO token not yet valid: {} ", userName); + redirectToKnox(httpRequest, httpResponse, chain); + } chain.doFilter(request, response); } else { // if the token is not valid then redirect to knox sso redirectToKnox(httpRequest, httpResponse, chain); @@ -135,7 +146,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha LOG.warn("Unable to parse the JWT token", e); redirectToKnox(httpRequest, httpResponse, chain); } - } else { + } else { // if there is no token, redirect redirectToKnox(httpRequest, httpResponse, chain); } } 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 index 9155546f48..0b3015b80f 100644 --- 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 @@ -22,11 +22,14 @@ 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; @@ -44,28 +47,18 @@ 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(); - - MockHttpServletRequest request = requestWithJWT(tokenWithKey((RSAPrivateKey) key.getPrivate())); - 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()); + requestThatSucceeds(tokenWithKey((RSAPrivateKey) key.getPrivate(),DEFAULT_PAYLOAD), key); } @Test @@ -73,33 +66,100 @@ public void testInvalidJWT() throws Exception { KeyPair key = createKey(); KeyPair badKey = createKey(); assertFalse(key.equals(badKey)); + requestThatFails(tokenWithKey((RSAPrivateKey) badKey.getPrivate(),DEFAULT_PAYLOAD), key); + } - MockHttpServletRequest request = requestWithJWT(tokenWithKey((RSAPrivateKey) badKey.getPrivate())); - MockHttpServletResponse response = new MockHttpServletResponse(); - MockFilterChain chain = new MockFilterChain(); + @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()); + } - MetronAuthenticationProvider authenticationProvider = new MetronAuthenticationProvider(); - KnoxSSOAuthenticationFilter knoxSSOAuthenticationFilter = new KnoxSSOAuthenticationFilter( - authenticationProvider, knoxUrl, null, null, (RSAPublicKey) key.getPublic()); + private void requestThatFails(JWSObject token, KeyPair key) throws IOException, ServletException { + MockHttpServletRequest request = requestWithJWT(token); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain chain = new MockFilterChain(); - knoxSSOAuthenticationFilter.doFilter(request, response, chain); + MetronAuthenticationProvider authenticationProvider = new MetronAuthenticationProvider(); + KnoxSSOAuthenticationFilter knoxSSOAuthenticationFilter = new KnoxSSOAuthenticationFilter( + authenticationProvider, knoxUrl, null, null, (RSAPublicKey) key.getPublic()); - assertRedirectedToKnox(response); + knoxSSOAuthenticationFilter.doFilter(request, response, chain); + + assertRedirectedToKnox(response); } private KeyPair createKey() throws Exception { return KeyPairGenerator.getInstance("RSA").generateKeyPair(); } - private String tokenWithKey(RSAPrivateKey key) throws JOSEException { - JWSObject jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.RS256), new Payload("{ \"sub\": \"test\" }")); + 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.serialize(); + return jwsObject; } - private MockHttpServletRequest requestWithJWT(String jwt) { + private MockHttpServletRequest requestWithJWT(JWSObject jwt) { MockHttpServletRequest request = new MockHttpServletRequest(); - request.setCookies(new Cookie(COOKIE_NAME, jwt)); + request.setCookies(new Cookie(COOKIE_NAME, jwt.serialize())); return request; } 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 index ea96e36a9e..efaa62c8e5 100644 --- 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 @@ -25,6 +25,7 @@ import java.nio.charset.Charset; import java.security.cert.CertificateException; import java.security.interfaces.RSAPublicKey; +import java.util.Date; import javax.servlet.ServletException; From 499b58187131bde8bdea53c58fd55f8b1c9aa89d Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 31 Jul 2018 22:44:13 +0100 Subject: [PATCH 15/24] Fix to the logout url generation --- .../src/main/java/org/apache/metron/ui/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index ce95100902..435babbc7f 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -60,7 +60,7 @@ public List user() { @RequestMapping(path = "/logout", method = RequestMethod.GET) public String logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { StringBuilder redirect = new StringBuilder("redirect:" ); - redirect.append(knoxSSOUrl.replaceAll("knoxsso", "knoxssout")); + redirect.append(knoxSSOUrl.replaceAll("websso", "webssout")); redirect.append(knoxSSOUrl.contains("?") ? "&": "?"); redirect.append("originalUrl="); redirect.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name())); From baacc245ed83f1f758c9247431b652902c18fd22 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 31 Jul 2018 22:47:28 +0100 Subject: [PATCH 16/24] Fixed redirect method --- .../src/main/java/org/apache/metron/ui/UserController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index 435babbc7f..22dffdd274 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -34,6 +34,8 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.view.RedirectView; /** * A trivial endpoint to ping for currently authenticated user principal @@ -58,12 +60,13 @@ public List user() { } @RequestMapping(path = "/logout", method = RequestMethod.GET) - public String logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { + public RedirectView logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { StringBuilder redirect = new StringBuilder("redirect:" ); redirect.append(knoxSSOUrl.replaceAll("websso", "webssout")); redirect.append(knoxSSOUrl.contains("?") ? "&": "?"); redirect.append("originalUrl="); redirect.append(URLEncoder.encode(originalUrl, StandardCharsets.UTF_8.name())); - return redirect.toString(); + + return new RedirectView(redirect.toString()); } } From 6400a50730dcbfbcb9e52674377d8c6323c8573b Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 31 Jul 2018 23:34:47 +0100 Subject: [PATCH 17/24] Cleaned up imports --- .../src/main/java/org/apache/metron/ui/UserController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index 22dffdd274..c48c15ff4a 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -34,7 +34,6 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.view.RedirectView; /** From 8e30074f095621a76bc35168dd06b09c94c8a5fa Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Wed, 1 Aug 2018 12:18:39 +0100 Subject: [PATCH 18/24] Fixed redirect URL for Logout --- .../src/main/java/org/apache/metron/ui/UserController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java index c48c15ff4a..d11d16f13b 100644 --- a/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java +++ b/metron-interface/metron-ui-host/src/main/java/org/apache/metron/ui/UserController.java @@ -60,7 +60,7 @@ public List user() { @RequestMapping(path = "/logout", method = RequestMethod.GET) public RedirectView logout(Principal user, HttpServletResponse httpServletResponse, @RequestParam("originalUrl") String originalUrl) throws UnsupportedEncodingException { - StringBuilder redirect = new StringBuilder("redirect:" ); + StringBuilder redirect = new StringBuilder(); redirect.append(knoxSSOUrl.replaceAll("websso", "webssout")); redirect.append(knoxSSOUrl.contains("?") ? "&": "?"); redirect.append("originalUrl="); From 9cff19f7ad8766112bcb2291d1419e5ed20d0b43 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 2 Aug 2018 01:01:11 +0100 Subject: [PATCH 19/24] Disable CSRF --- .../main/java/org/apache/metron/ui/MetronSecurityConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index e8e6835757..7d3ec3b31b 100644 --- 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 @@ -114,7 +114,7 @@ protected void configure(HttpSecurity http) throws Exception { http.addFilterAt(ssoAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } http.headers().disable(); - http.csrf(); + http.csrf().disable(); } private KnoxSSOAuthenticationFilter ssoAuthenticationFilter() throws Exception { From 5bb386a5e84180b7d454bd1485423e7d55681eaa Mon Sep 17 00:00:00 2001 From: merrimanr Date: Wed, 22 Aug 2018 14:17:15 -0500 Subject: [PATCH 20/24] initial commit --- .../apache/metron/rest/config/TestConfig.java | 18 +--------- metron-interface/metron-ui-host/pom.xml | 8 +++++ .../java/org/apache/metron/ui/WhoamiTest.java | 15 +++----- .../src/test/resources/application-test.yml | 19 ++-------- metron-interface/metron-ui-security/pom.xml | 24 +++++++++++++ .../org/apache/metron/ui/EmbeddedLdap.java | 3 +- .../metron/ui/config/TestSecurityConfig.java} | 18 +--------- .../resources/application-embedded-ldap.yml | 35 +++++++++++++++++++ .../src/test/resources/schema.ldif | 0 9 files changed, 77 insertions(+), 63 deletions(-) rename metron-interface/{metron-ui-host => metron-ui-security}/src/test/java/org/apache/metron/ui/EmbeddedLdap.java (97%) rename metron-interface/{metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java => metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java} (56%) create mode 100644 metron-interface/metron-ui-security/src/test/resources/application-embedded-ldap.yml rename metron-interface/{metron-ui-host => metron-ui-security}/src/test/resources/schema.ldif (100%) diff --git a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java index 27a510f3d2..008f3fcb20 100644 --- a/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java +++ b/metron-interface/metron-rest/src/test/java/org/apache/metron/rest/config/TestConfig.java @@ -41,12 +41,9 @@ import org.apache.metron.rest.mock.MockStormCLIClientWrapper; import org.apache.metron.rest.mock.MockStormRestTemplate; import org.apache.metron.rest.service.impl.StormCLIWrapper; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; -import org.springframework.core.io.ClassPathResource; import org.springframework.kafka.core.ConsumerFactory; import org.springframework.kafka.core.DefaultKafkaConsumerFactory; import org.springframework.web.client.RestTemplate; @@ -183,22 +180,9 @@ public RestTemplate restTemplate(StormCLIWrapper stormCLIClientWrapper) { return AdminUtils$.MODULE$; } + @Bean() public UserSettingsClient userSettingsClient() throws RestException, IOException { return new UserSettingsClient(new MockHBaseTableProvider().addToCache("user_settings", "cf"), Bytes.toBytes("cf")); } - - /** - * PropertyPlaceholderConfigurer bean required to make SPEL Values injectable in - * Tests from YAML config. - * - */ - @Bean - public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { - YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); - yaml.setResources(new ClassPathResource("application-test.yml")); - PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer(); - propertyPlaceholderConfigurer.setProperties(yaml.getObject()); - return propertyPlaceholderConfigurer; - } } diff --git a/metron-interface/metron-ui-host/pom.xml b/metron-interface/metron-ui-host/pom.xml index 9746f70342..047847b63a 100644 --- a/metron-interface/metron-ui-host/pom.xml +++ b/metron-interface/metron-ui-host/pom.xml @@ -104,6 +104,14 @@ 1.5.4 test + + + org.apache.metron + metron-ui-security + ${project.parent.version} + test + test-jar + diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java index f631d7d4ac..98c4a9fc69 100644 --- a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java +++ b/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/WhoamiTest.java @@ -17,6 +17,7 @@ */ package org.apache.metron.ui; +import static org.apache.metron.ui.EmbeddedLdap.EMBEDDED_LDAP_PROFILE; import static org.hamcrest.Matchers.containsString; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity; @@ -32,24 +33,19 @@ import javax.servlet.http.Cookie; -import org.apache.metron.ui.config.TestsConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.ConfigFileApplicationContextInitializer; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; -import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import com.fasterxml.jackson.annotation.JsonAlias; import com.nimbusds.jose.JWSAlgorithm; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSObject; @@ -57,11 +53,8 @@ import com.nimbusds.jose.crypto.RSASSASigner; @RunWith(SpringRunner.class) -@TestPropertySource("classpath:application-test.yml") -@ContextConfiguration(classes = { MetronSecurityConfig.class, - TestsConfig.class }, initializers = ConfigFileApplicationContextInitializer.class) -@SpringBootTest(classes = TestHostApplication.class) -@WebAppConfiguration +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles({"test", EMBEDDED_LDAP_PROFILE}) public class WhoamiTest { @Autowired diff --git a/metron-interface/metron-ui-host/src/test/resources/application-test.yml b/metron-interface/metron-ui-host/src/test/resources/application-test.yml index 2dcb2cd23b..f89a618d72 100644 --- a/metron-interface/metron-ui-host/src/test/resources/application-test.yml +++ b/metron-interface/metron-ui-host/src/test/resources/application-test.yml @@ -14,30 +14,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -spring: +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" - knox: sso: url: https://localhost:8443/gateway/knoxsso/api/v1/websso - pubkey: | + pubkey: | MIIEqTCCApGgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCVVMx CzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZBcGFjaGUxGDAWBgNVBAMMD0NBIGludGVy bWVkaWF0ZTAeFw0xODA3MTExOTA0NTRaFw0yODEwMTYxOTA0NTRaMDsxCzAJBgNV diff --git a/metron-interface/metron-ui-security/pom.xml b/metron-interface/metron-ui-security/pom.xml index b42af640e5..dc980d4a03 100644 --- a/metron-interface/metron-ui-security/pom.xml +++ b/metron-interface/metron-ui-security/pom.xml @@ -87,6 +87,13 @@ test + + org.apache.directory.server + apacheds-all + 1.5.4 + test + + @@ -108,4 +115,21 @@ + + + + + org.apache.maven.plugins + maven-jar-plugin + ${global_jar_version} + + + + test-jar + + + + + + diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java similarity index 97% rename from metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java rename to metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java index cd65baf4f1..a41ea7e1d5 100644 --- a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/EmbeddedLdap.java +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/EmbeddedLdap.java @@ -47,6 +47,7 @@ @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}") @@ -133,7 +134,7 @@ public void afterPropertiesSet() throws Exception { ldapService.start(); // load default schema - applyLdif(new File(this.getClass().getClassLoader().getResource("schema.ldif").toURI())); + applyLdif(new File("schema.ldif")); LOG.debug("LDAP server started"); } diff --git a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java similarity index 56% rename from metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java rename to metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java index 605298963f..a47c74acc6 100644 --- a/metron-interface/metron-ui-host/src/test/java/org/apache/metron/ui/config/TestsConfig.java +++ b/metron-interface/metron-ui-security/src/test/java/org/apache/metron/ui/config/TestSecurityConfig.java @@ -18,27 +18,11 @@ package org.apache.metron.ui.config; import org.apache.metron.ui.EmbeddedLdap; -import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; -import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.io.ClassPathResource; @Configuration -public class TestsConfig { - /** - * PropertyPlaceholderConfigurer bean required to make SPEL Values injectable in - * Tests from YAML config. - * - */ - @Bean - public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer() { - YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); - yaml.setResources(new ClassPathResource("application-test.yml")); - PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer(); - propertyPlaceholderConfigurer.setProperties(yaml.getObject()); - return propertyPlaceholderConfigurer; - } +public class TestSecurityConfig { @Bean public EmbeddedLdap embeddedLdap() { 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 0000000000..5c4d7e2975 --- /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" diff --git a/metron-interface/metron-ui-host/src/test/resources/schema.ldif b/metron-interface/metron-ui-security/src/test/resources/schema.ldif similarity index 100% rename from metron-interface/metron-ui-host/src/test/resources/schema.ldif rename to metron-interface/metron-ui-security/src/test/resources/schema.ldif From a92488ea92f968a20959484871b2de58279f6021 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Thu, 23 Aug 2018 12:16:14 +0100 Subject: [PATCH 21/24] Removed accidental pom addition --- .../metron-rest-client/bin/pom.xml | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 metron-interface/metron-rest-client/bin/pom.xml diff --git a/metron-interface/metron-rest-client/bin/pom.xml b/metron-interface/metron-rest-client/bin/pom.xml deleted file mode 100644 index 9ef9ae62db..0000000000 --- a/metron-interface/metron-rest-client/bin/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - 4.0.0 - - org.apache.metron - metron-interface - 0.5.1 - - metron-rest-client - https://metron.apache.org/ - - 2.1.1 - - - - org.apache.metron - metron-common - ${project.parent.version} - - - com.fasterxml.jackson.core - jackson-databind - - - - - org.apache.metron - metron-indexing - ${project.parent.version} - - - org.eclipse.persistence - javax.persistence - ${eclipse.javax.persistence.version} - - - - From a3b43b021a1ce5ac8827791edaa3115cc4f21127 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 28 Aug 2018 17:28:39 +1200 Subject: [PATCH 22/24] Fixed whitespace --- metron-interface/metron-alerts-host/pom.xml | 237 +++++++++--------- .../apache/metron/ui/AlertsApplication.java | 32 ++- .../apache/metron/ui/ConfigApplication.java | 6 +- 3 files changed, 136 insertions(+), 139 deletions(-) diff --git a/metron-interface/metron-alerts-host/pom.xml b/metron-interface/metron-alerts-host/pom.xml index 76d1f73afc..5030d69ac9 100644 --- a/metron-interface/metron-alerts-host/pom.xml +++ b/metron-interface/metron-alerts-host/pom.xml @@ -12,132 +12,131 @@ the specific language governing permissions and limitations under the License. --> - - 4.0.0 + + 4.0.0 - org.apache.metron.ui - metron-alerts-host - jar + org.apache.metron.ui + metron-alerts-host + jar - Metron Alerts UI Host - Spring Server to run frontend + Metron Alerts UI Host + Spring Server to run frontend - - org.apache.metron - metron-interface - 0.5.1 - + + org.apache.metron + metron-interface + 0.5.1 + - - UTF-8 - UTF-8 - 1.8 - Finchley.RELEASE - 2.0.1.RELEASE - + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + - - - org.apache.metron - metron-alerts - ${project.parent.version} - - - - org.apache.metron - metron-ui-host - ${project.parent.version} - + + + org.apache.metron + metron-alerts + ${project.parent.version} + - + + org.apache.metron + metron-ui-host + ${project.parent.version} + - - - - org.springframework.cloud - spring-cloud-dependencies - ${spring-cloud.version} - pom - import - - - - org.springframework.boot - spring-boot-dependencies - ${spring.boot.version} - pom - import - - - org.apache.logging.log4j - log4j-slf4j-impl - - - ch.qos.logback - logback-classic - - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - true - org.apache.metron.ui.AlertsApplication - - metron-alerts - Metron Alerts UI server - Metron Alerts - - - - - - repackage - - - - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack-shared-resources - - unpack-dependencies - - generate-resources - - ${project.build.directory}/generated-resources - shared-resources - ${project.groupId} - true - - - - - - maven-assembly-plugin - - src/main/assembly/assembly.xml - - - - package - - single - - - - - - + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + + org.springframework.boot + spring-boot-dependencies + ${spring.boot.version} + pom + import + + + org.apache.logging.log4j + log4j-slf4j-impl + + + ch.qos.logback + logback-classic + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + org.apache.metron.ui.AlertsApplication + + metron-alerts + Metron Alerts UI server + Metron Alerts + + + + + + repackage + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-shared-resources + + unpack-dependencies + + generate-resources + + ${project.build.directory}/generated-resources + shared-resources + ${project.groupId} + true + + + + + + maven-assembly-plugin + + src/main/assembly/assembly.xml + + + + package + + single + + + + + + diff --git a/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java b/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java index d6f0dfed73..8423dcc587 100644 --- a/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java +++ b/metron-interface/metron-alerts-host/src/main/java/org/apache/metron/ui/AlertsApplication.java @@ -1,19 +1,16 @@ /** - * 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 + * 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 + * 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. + * 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; @@ -23,9 +20,10 @@ import org.springframework.context.annotation.ComponentScans; @SpringBootApplication -@ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) }) +@ComponentScans( + value = {@ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class)}) public class AlertsApplication extends AbstractHostApplication { - public static void main(String[] args) { - SpringApplication.run(AlertsApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(AlertsApplication.class, args); + } } diff --git a/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java b/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java index ab08da1a2b..d769be3020 100644 --- a/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java +++ b/metron-interface/metron-config-host/src/main/java/org/apache/metron/ui/ConfigApplication.java @@ -25,7 +25,7 @@ @SpringBootApplication @ComponentScans(value = { @ComponentScan, @ComponentScan(basePackageClasses = MetronSecurityConfig.class) }) public class ConfigApplication extends AbstractHostApplication { - public static void main(String[] args) { - SpringApplication.run(ConfigApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ConfigApplication.class, args); + } } From dcd9934ddc855c450eed4b9ab0b97c515aa970a7 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 28 Aug 2018 18:24:03 +1200 Subject: [PATCH 23/24] Pushed up spring boot version properties to interface pom --- metron-interface/metron-alerts-host/pom.xml | 5 ++--- metron-interface/metron-config-host/pom.xml | 7 +++---- metron-interface/metron-rest/pom.xml | 1 - metron-interface/metron-ui-host/pom.xml | 14 +------------- metron-interface/pom.xml | 9 +++++++++ 5 files changed, 15 insertions(+), 21 deletions(-) diff --git a/metron-interface/metron-alerts-host/pom.xml b/metron-interface/metron-alerts-host/pom.xml index 5030d69ac9..aa2ba1b79e 100644 --- a/metron-interface/metron-alerts-host/pom.xml +++ b/metron-interface/metron-alerts-host/pom.xml @@ -33,10 +33,8 @@ UTF-8 UTF-8 1.8 - Finchley.RELEASE - 2.0.1.RELEASE - + org.apache.metron @@ -87,6 +85,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring.boot.version} true org.apache.metron.ui.AlertsApplication diff --git a/metron-interface/metron-config-host/pom.xml b/metron-interface/metron-config-host/pom.xml index cc01a0eb2c..f51692b45e 100644 --- a/metron-interface/metron-config-host/pom.xml +++ b/metron-interface/metron-config-host/pom.xml @@ -29,15 +29,13 @@ metron-interface 0.5.1 - + UTF-8 UTF-8 1.8 - Finchley.RELEASE - 2.0.1.RELEASE - + org.apache.metron @@ -87,6 +85,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring.boot.version} true org.apache.metron.ui.ConfigApplication diff --git a/metron-interface/metron-rest/pom.xml b/metron-interface/metron-rest/pom.xml index 69d00120ab..d9d4bfe118 100644 --- a/metron-interface/metron-rest/pom.xml +++ b/metron-interface/metron-rest/pom.xml @@ -29,7 +29,6 @@ 4.5 2.7.1 1.6.4 - 2.0.1.RELEASE 1.0.1.RELEASE 2.5.0 5.1.40 diff --git a/metron-interface/metron-ui-host/pom.xml b/metron-interface/metron-ui-host/pom.xml index 047847b63a..82ee1f3982 100644 --- a/metron-interface/metron-ui-host/pom.xml +++ b/metron-interface/metron-ui-host/pom.xml @@ -30,8 +30,6 @@ UTF-8 UTF-8 1.8 - Finchley.RELEASE - 2.0.1.RELEASE 4.41.2 @@ -59,11 +57,6 @@ spring-cloud-starter-netflix-zuul - - org.springframework.cloud - spring-cloud-starter-netflix-zuul - - org.slf4j slf4j-api @@ -92,12 +85,6 @@ test - - org.springframework.security - spring-security-test - test - - org.apache.directory.server apacheds-all @@ -139,6 +126,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring.boot.version} true diff --git a/metron-interface/pom.xml b/metron-interface/pom.xml index c91de033b1..844f213929 100644 --- a/metron-interface/pom.xml +++ b/metron-interface/pom.xml @@ -48,6 +48,15 @@ metron-rest metron-rest-client + + + UTF-8 + UTF-8 + 1.8 + Finchley.RELEASE + 2.0.1.RELEASE + + junit From 0a923433e772f1e3d1169b67c375dc1b4aeac6b3 Mon Sep 17 00:00:00 2001 From: Simon Elliston Ball Date: Tue, 28 Aug 2018 18:38:09 +1200 Subject: [PATCH 24/24] Metron REST docs changes for new authentication method --- metron-interface/metron-rest/README.md | 95 +++++++++----------------- 1 file changed, 32 insertions(+), 63 deletions(-) diff --git a/metron-interface/metron-rest/README.md b/metron-interface/metron-rest/README.md index 44594f7613..42c3d0b520 100644 --- a/metron-interface/metron-rest/README.md +++ b/metron-interface/metron-rest/README.md @@ -25,7 +25,6 @@ This module provides a RESTful API for interacting with Metron. * A running real-time store, either Elasticsearch or Solr depending on which one is enabled * Java 8 installed * Storm CLI and Metron topology scripts (start_parser_topology.sh, start_enrichment_topology.sh, start_elasticsearch_topology.sh) installed -* A relational database ## Installation @@ -66,10 +65,6 @@ No optional parameter has a default. | Environment Variable | Description | ------------------------------------- | ----------- -| METRON_JDBC_DRIVER | JDBC driver class -| METRON_JDBC_URL | JDBC url -| METRON_JDBC_USERNAME | JDBC username -| METRON_JDBC_PLATFORM | JDBC platform (one of h2, mysql, postgres, oracle | ZOOKEEPER | Zookeeper quorum (ex. node1:2181,node2:2181) | BROKERLIST | Kafka Broker list (ex. node1:6667,node2:6667) | HDFS_URL | HDFS url or `fs.defaultFS` Hadoop setting (ex. hdfs://node1:8020) @@ -80,7 +75,6 @@ No optional parameter has a default. | METRON_LOG_DIR | Directory where the log file is written | Optional | /var/log/metron/ | METRON_PID_FILE | File where the pid is written | Optional | /var/run/metron/ | METRON_REST_PORT | REST application port | Optional | 8082 -| METRON_JDBC_CLIENT_PATH | Path to JDBC client jar | Optional | H2 is bundled | METRON_TEMP_GROK_PATH | Temporary directory used to test grok statements | Optional | ./patterns/temp | METRON_DEFAULT_GROK_PATH | Defaults HDFS directory used to store grok statements | Optional | /apps/metron/patterns | SECURITY_ENABLED | Enables Kerberos support | Optional | false @@ -96,27 +90,6 @@ No optional parameter has a default. These are set in the `/etc/default/metron` file. -## Database setup - -The REST application persists data in a relational database and requires a dedicated database user and database (see https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html for more detail). -Spring uses Hibernate as the default ORM framework but another framework is needed becaused Hibernate is not compatible with the Apache 2 license. For this reason Metron uses [EclipseLink](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-embedded-database-support). See the [Spring Data JPA - EclipseLink](https://github.com/spring-projects/spring-data-examples/tree/master/jpa/eclipselink) project for an example on how to configure EclipseLink in Spring. - -### Development - -The REST application comes with [embedded database support](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html#boot-features-embedded-database-support) for development purposes. - -For example, edit these variables in `/etc/default/metron` before starting the application to configure H2: -``` -METRON_JDBC_DRIVER="org.h2.Driver" -METRON_JDBC_URL="jdbc:h2:file:~/metrondb" -METRON_JDBC_USERNAME="root" -METRON_JDBC_PLATFORM="h2" -``` - -### Production - -The REST application should be configured with a production-grade database outside of development. - #### Ambari Install Installing with Ambari is recommended for production deployments. @@ -125,48 +98,17 @@ This includes managing the PID file, directing logging, etc. #### Manual Install -The following configures the application for MySQL: - -1. Install MySQL if not already available (this example uses version 5.7, installation instructions can be found [here](https://dev.mysql.com/doc/refman/5.7/en/linux-installation-yum-repo.html)) - -1. Create a metron user and REST database and permission the user for that database: - ``` - CREATE USER 'metron'@'node1' IDENTIFIED BY 'Myp@ssw0rd'; - CREATE DATABASE IF NOT EXISTS metronrest; - GRANT ALL PRIVILEGES ON metronrest.* TO 'metron'@'node1'; - ``` - -1. Create the security tables as described in the [Spring Security Guide](https://docs.spring.io/spring-security/site/docs/5.0.4.RELEASE/reference/htmlsingle/#user-schema). - -1. Install the MySQL JDBC client onto the REST application host and configurate the METRON_JDBC_CLIENT_PATH variable: - ``` - cd $METRON_HOME/lib - wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-java-5.1.41.tar.gz - tar xf mysql-connector-java-5.1.41.tar.gz - ``` - -1. Edit these variables in `/etc/default/metron` to configure the REST application for MySQL: - ``` - METRON_JDBC_DRIVER="com.mysql.jdbc.Driver" - METRON_JDBC_URL="jdbc:mysql://mysql_host:3306/metronrest" - METRON_JDBC_USERNAME="metron" - METRON_JDBC_PLATFORM="mysql" - METRON_JDBC_CLIENT_PATH=$METRON_HOME/lib/mysql-connector-java-5.1.41/mysql-connector-java-5.1.41-bin.jar - ``` - 1. Switch to the metron user ``` sudo su - metron ``` -1. Start the REST API. Adjust the password as necessary. +1. Start the REST API. ``` set -o allexport; source /etc/default/metron; set +o allexport; - export METRON_JDBC_PASSWORD='Myp@ssw0rd'; $METRON_HOME/bin/metron-rest.sh - unset METRON_JDBC_PASSWORD; ``` ## Usage @@ -177,13 +119,40 @@ The REST application can be accessed with the Swagger UI at http://host:port/swa ### Authentication -The metron-rest module uses [Spring Security](http://projects.spring.io/spring-security/) for authentication and stores user credentials in the relational database configured above. The required tables are created automatically the first time the application is started so that should be done first. For example (continuing the MySQL example above), users can be added by connecting to MySQL and running: +The metron-rest module uses [Spring Security](http://projects.spring.io/spring-security/) for authentication, and supports LDAP based authentication and [Knox SSO](https://knox.apache.org/books/knox-1-1-0/user-guide.html#KnoxSSO+Setup+and+Configuration) based authentication using jwt tokens. + +To configure LDAP based application add the following to the rest_application.yml file (note, this would usually be done via the Ambari configuration interface): + ``` -use metronrest; -insert into users (username, password, enabled) values ('your_username','your_password',1); -insert into authorities (username, authority) values ('your_username', 'ROLE_USER'); +ldap: + provider: + url: ldap://node1:33389 + userdn: uid=admin,ou=people,dc=hadoop,dc=apache,dc=org + password: admin-password + user: + dn.patterns: uid={0},ou=people,dc=hadoop,dc=apache,dc=org + passwordAttribute: userPassword + searchBase: ou=people,dc=hadoop,dc=apache,dc=org + searchFilter: "" + group: + searchBase: ou=groups,dc=hadoop,dc=apache,dc=org + searchFilter: "member={0}" + roleAttribute: "cn" ``` +This example assumes you are using the Demo LDAP server provided by the Knox project, running on a host call node1 (as in full dev) on port 33389. + +To configure the use of Knox SSO, add: + +``` +knox: + sso: + url: 'https://{gateway_host}:{gateway_port}/gateway/knoxsso/api/v1/websso' + pubkey: '' +``` + +This would usually be done with through Ambari. + ### Kerberos Metron REST can be configured for a cluster with Kerberos enabled. A client JAAS file is required for Kafka and Zookeeper and a Kerberos keytab for the metron user principal is required for all other services. Configure these settings in the `/etc/default/metron` file: