From 8427a05abce1f06c7e27c11378e402ec23ce66a7 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Mon, 13 Apr 2026 16:08:47 +0530 Subject: [PATCH 01/22] Adding rest-notification module from cdh_main to opensource. --- rest-notification-webapp/pom.xml | 243 ++++++ .../rest/AtlasRepositoryConfiguration.java | 88 ++ .../rest/AtlasServerIdSelector.java | 82 ++ .../notification/rest/DateTimeHelper.java | 56 ++ .../rest/LocalServletRequest.java | 400 +++++++++ .../rest/RestHAConfiguration.java | 208 +++++ .../rest/RestNotificationMain.java | 208 +++++ .../notification/rest/SecurityProperties.java | 32 + .../atlas/notification/rest/Servlets.java | 223 +++++ .../rest/util/CredentialProviderUtility.java | 227 +++++ .../notification/rest/web/dao/UserDao.java | 266 ++++++ .../rest/web/filters/ActiveServerFilter.java | 176 ++++ .../AtlasAuthenticationEntryPoint.java | 60 ++ .../filters/AtlasAuthenticationFilter.java | 818 ++++++++++++++++++ .../filters/AtlasCSRFPreventionFilter.java | 267 ++++++ ...lasDelegatingAuthenticationEntryPoint.java | 61 ++ .../rest/web/filters/AtlasHeaderFilter.java | 54 ++ .../AtlasKnoxSSOAuthenticationFilter.java | 594 +++++++++++++ .../filters/AtlasResponseRequestWrapper.java | 33 + .../rest/web/filters/AuditFilter.java | 206 +++++ .../rest/web/filters/HeadersUtil.java | 109 +++ .../rest/web/filters/NullServletContext.java | 336 +++++++ .../rest/web/filters/RestUtil.java | 108 +++ .../rest/web/filters/SSOAuthentication.java | 74 ++ .../filters/SSOAuthenticationProperties.java | 78 ++ .../rest/web/listeners/LoginProcessor.java | 158 ++++ .../notification/rest/web/model/User.java | 129 +++ .../rest/web/resources/AdminResource.java | 129 +++ .../rest/web/rest/NotificationREST.java | 128 +++ .../AtlasADAuthenticationProvider.java | 203 +++++ .../AtlasAbstractAuthenticationProvider.java | 149 ++++ .../AtlasAuthenticationException.java | 31 + .../AtlasAuthenticationFailureHandler.java | 55 ++ .../security/AtlasAuthenticationProvider.java | 151 ++++ .../AtlasAuthenticationSuccessHandler.java | 70 ++ .../AtlasFileAuthenticationProvider.java | 80 ++ .../AtlasLdapAuthenticationProvider.java | 299 +++++++ .../AtlasPamAuthenticationProvider.java | 172 ++++ .../web/security/AtlasSecurityConfig.java | 158 ++++ .../rest/web/security/PamLoginModule.java | 234 +++++ .../rest/web/security/PamPrincipal.java | 84 ++ .../web/security/UserAuthorityGranter.java | 35 + .../service/ActiveInstanceElectorService.java | 203 +++++ .../rest/web/service/ActiveInstanceState.java | 146 ++++ .../AtlasZookeeperSecurityProperties.java | 74 ++ .../rest/web/service/CuratorFactory.java | 202 +++++ .../rest/web/service/EmbeddedServer.java | 113 +++ .../web/service/SecureEmbeddedServer.java | 351 ++++++++ .../rest/web/service/ServiceState.java | 116 +++ .../rest/web/service/UserService.java | 43 + .../rest/web/setup/KerberosAwareListener.java | 34 + .../rest-notification-buildinfo.properties | 28 + .../webapp/WEB-INF/applicationContext.xml | 23 + .../src/main/webapp/WEB-INF/web.xml | 89 ++ 54 files changed, 8694 insertions(+) create mode 100644 rest-notification-webapp/pom.xml create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasRepositoryConfiguration.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasServerIdSelector.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestHAConfiguration.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/SecurityProperties.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasHeaderFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/HeadersUtil.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java create mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java create mode 100755 rest-notification-webapp/src/main/resources/rest-notification-buildinfo.properties create mode 100644 rest-notification-webapp/src/main/webapp/WEB-INF/applicationContext.xml create mode 100644 rest-notification-webapp/src/main/webapp/WEB-INF/web.xml diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml new file mode 100644 index 0000000000..d056daf729 --- /dev/null +++ b/rest-notification-webapp/pom.xml @@ -0,0 +1,243 @@ + + + 4.0.0 + + org.apache.atlas + apache-atlas + 2.4.0.7.3.2.10000-SNAPSHOT + + org.apache.atlas + rest-notification-webapp + war + + Rest Notification Webapp + + + + + org.slf4j + log4j-over-slf4j + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + junit + junit + 4.11 + test + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + + + org.springframework + spring-web + ${spring.version} + + + org.springframework + spring-aop + + + org.aspectj + aspectjrt + ${aspectjrt.version} + + + org.aspectj + aspectjweaver + ${aspectjweaver.version} + + + org.apache.atlas + atlas-intg + + + com.google.guava + guava + + + org.apache.atlas + atlas-notification + + + org.apache.atlas + atlas-client-v2 + + + org.apache.atlas + atlas-authorization + ${version} + + + org.testng + testng + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-ldap + + + org.springframework.ldap + spring-ldap-core + + + + + org.springframework.ldap + spring-ldap-core + ${spring-ldap-core.version} + + + org.springframework.security + spring-security-web + + + + org.apache.commons + commons-configuration2 + ${commons-configuration2.version} + + + org.apache.commons + commons-text + + + + + + org.apache.commons + commons-text + ${commons-text.version} + + + + com.googlecode.json-simple + json-simple + + + + + org.kohsuke + libpam4j + + + + org.glassfish.jersey.core + jersey-client + ${jersey2.version} + + + + org.glassfish.jersey.core + jersey-server + ${jersey2.version} + + + + org.glassfish.jersey.core + jersey-common + ${jersey2.version} + + + + org.glassfish.jersey.ext + jersey-spring5 + ${jersey2.version} + + + + org.glassfish.jersey.containers + jersey-container-servlet + ${jersey2.version} + + + + org.glassfish.jersey.media + jersey-media-multipart + ${jersey2.version} + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + ${maven-compiler-version} + + + maven-surefire-plugin + 2.22.1 + + + maven-war-plugin + 3.2.2 + + true + true + + + + true + + + + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + + + + diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasRepositoryConfiguration.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasRepositoryConfiguration.java new file mode 100644 index 0000000000..0afad37752 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasRepositoryConfiguration.java @@ -0,0 +1,88 @@ +/** + * 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.atlas.notification.rest; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.List; + +/** + * Atlas configuration for repository project + * + */ +public class AtlasRepositoryConfiguration { + + private static Logger LOG = LoggerFactory.getLogger(AtlasRepositoryConfiguration.class); + + public static final String AUDIT_EXCLUDED_OPERATIONS = "atlas.audit.excludes"; + public static final String SEPARATOR = ":"; + + + private static List skippedOperations = null; + + + /** + * Get the list of operations which are configured to be skipped from auditing + * Valid format is HttpMethod:URL eg: GET:Version + * @return list of string + * @throws AtlasException + */ + public static List getAuditExcludedOperations(Configuration config) throws AtlasException { + if (skippedOperations == null) { + if (config == null) { + try { + config = ApplicationProperties.get(); + } catch (AtlasException e) { + LOG.error(" Error reading operations for auditing ", e); + throw e; + } + } + + skippedOperations = new ArrayList<>(); + + String[] skipAuditForOperations = config.getStringArray(AUDIT_EXCLUDED_OPERATIONS); + + if (skipAuditForOperations != null && skipAuditForOperations.length > 0) { + for (String skippedOperation : skipAuditForOperations) { + String[] excludedOperations = skippedOperation.trim().toLowerCase().split(SEPARATOR); + if (excludedOperations!= null && excludedOperations.length == 2) { + skippedOperations.add(skippedOperation.toLowerCase()); + } else { + LOG.error("Invalid format for skipped operation {}. Valid format is HttpMethod:URL eg: GET:Version", skippedOperation); + } + } + } + } + + return skippedOperations; + } + + public static boolean isExcludedFromAudit(Configuration config, String httpMethod, String httpUrl) throws AtlasException { + if (getAuditExcludedOperations(config).size() > 0) { + return getAuditExcludedOperations(config).contains(httpMethod.toLowerCase() + SEPARATOR + httpUrl.toLowerCase()); + } else { + return false; + } + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasServerIdSelector.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasServerIdSelector.java new file mode 100644 index 0000000000..4d5566213b --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/AtlasServerIdSelector.java @@ -0,0 +1,82 @@ +/** + * 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.atlas.notification.rest; + +import org.apache.atlas.AtlasConstants; +import org.apache.atlas.AtlasException; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.net.NetUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; + +public class AtlasServerIdSelector { + + private static final Logger LOG = LoggerFactory.getLogger(AtlasServerIdSelector.class); + + /** + * Return the ID corresponding to this Atlas instance. + * + * The match is done by looking for an ID configured in {@link RestHAConfiguration #ATLAS_SERVER_IDS} key + * that has a host:port entry for the key {@link RestHAConfiguration #ATLAS_SERVER_ADDRESS_PREFIX}+ID where + * the host is a local IP address and port is set in the system property + * {@link AtlasConstants#SYSTEM_PROPERTY_APP_PORT}. + * + * @param configuration + * @return + * @throws AtlasException if no ID is found that maps to a local IP Address or port + */ + public static String selectServerId(Configuration configuration) throws AtlasException { + // ids are already trimmed by this method + String[] ids = configuration.getStringArray(RestHAConfiguration.ATLAS_REST_SERVER_IDS); + String matchingServerId = null; + int appPort = Integer.parseInt(System.getProperty(AtlasConstants.SYSTEM_PROPERTY_APP_PORT)); + for (String id : ids) { + String hostPort = configuration.getString(RestHAConfiguration.ATLAS_REST_SERVER_ADDRESS_PREFIX +id); + if (!StringUtils.isEmpty(hostPort)) { + InetSocketAddress socketAddress; + try { + socketAddress = NetUtils.createSocketAddr(hostPort); + } catch (Exception e) { + LOG.warn("Exception while trying to get socket address for {}", hostPort, e); + continue; + } + if (!socketAddress.isUnresolved() + && NetUtils.isLocalAddress(socketAddress.getAddress()) + && appPort == socketAddress.getPort()) { + LOG.info("Found matched server id {} with host port: {}", id, hostPort); + matchingServerId = id; + break; + } + } else { + LOG.info("Could not find matching address entry for id: {}", id); + } + } + if (matchingServerId == null) { + String msg = String.format("Could not find server id for this instance. " + + "Unable to find IDs matching any local host and port binding among %s", + StringUtils.join(ids, ",")); + throw new AtlasException(msg); + } + return matchingServerId; + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java new file mode 100755 index 0000000000..6b50ba27d6 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java @@ -0,0 +1,56 @@ +/** + * 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.atlas.notification.rest; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.regex.Pattern; + +/** + * Support function to parse and format date. + */ +public final class DateTimeHelper { + + public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + private static final String DATE_PATTERN = + "(2\\d\\d\\d|19\\d\\d)-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])T" + "([0-1][0-9]|2[0-3]):([0-5][0-9])Z"; + private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); + + private static ThreadLocal DATE_FORMAT = new ThreadLocal() { + @Override + public DateFormat initialValue() { + DateFormat dateFormat = new SimpleDateFormat(ISO8601_FORMAT); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat; + } + }; + + private DateTimeHelper() { + } + + public static DateFormat getDateFormat() { + return DATE_FORMAT.get(); + } + + public static String formatDateUTC(Date date) { + return (date != null) ? getDateFormat().format(date) : null; + } +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java new file mode 100644 index 0000000000..2a85d913af --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java @@ -0,0 +1,400 @@ +/** + * 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.atlas.notification.rest; + +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.RequestDispatcher; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpUpgradeHandler; +import javax.servlet.http.Part; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.Principal; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Locale; +import java.util.Map; + +public class LocalServletRequest implements HttpServletRequest { + private final String payload; + + LocalServletRequest(String payload) { + this.payload = payload; + } + + public String getPayload() { + return payload; + } + + @Override + public String getAuthType() { + throw new IllegalStateException("Not supported"); + } + + @Override + public Cookie[] getCookies() { + throw new IllegalStateException("Not supported"); + } + + @Override + public long getDateHeader(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getHeader(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Enumeration getHeaders(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Enumeration getHeaderNames() { + throw new IllegalStateException("Not supported"); + } + + @Override + public int getIntHeader(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getMethod() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getPathInfo() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getPathTranslated() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getContextPath() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getQueryString() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRemoteUser() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isUserInRole(String role) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Principal getUserPrincipal() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRequestedSessionId() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRequestURI() { + throw new IllegalStateException("Not supported"); + } + + @Override + public StringBuffer getRequestURL() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getServletPath() { + throw new IllegalStateException("Not supported"); + } + + @Override + public HttpSession getSession(boolean create) { + throw new IllegalStateException("Not supported"); + } + + @Override + public HttpSession getSession() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String changeSessionId() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isRequestedSessionIdValid() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isRequestedSessionIdFromCookie() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isRequestedSessionIdFromURL() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isRequestedSessionIdFromUrl() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public void login(String username, String password) throws ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public void logout() throws ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public Collection getParts() throws IOException, ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public Part getPart(String name) throws IOException, ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public T upgrade(Class handlerClass) throws IOException, ServletException { + throw new IllegalStateException("Not supported"); + } + + @Override + public Object getAttribute(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Enumeration getAttributeNames() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getCharacterEncoding() { + throw new IllegalStateException("Not supported"); + } + + @Override + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { + throw new IllegalStateException("Not supported"); + } + + @Override + public int getContentLength() { + throw new IllegalStateException("Not supported"); + } + + @Override + public long getContentLengthLong() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getContentType() { + throw new IllegalStateException("Not supported"); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getParameter(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Enumeration getParameterNames() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String[] getParameterValues(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Map getParameterMap() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getProtocol() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getScheme() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getServerName() { + throw new IllegalStateException("Not supported"); + } + + @Override + public int getServerPort() { + throw new IllegalStateException("Not supported"); + } + + @Override + public BufferedReader getReader() throws IOException { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRemoteAddr() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRemoteHost() { + throw new IllegalStateException("Not supported"); + } + + @Override + public void setAttribute(String name, Object o) { + throw new IllegalStateException("Not supported"); + } + + @Override + public void removeAttribute(String name) { + throw new IllegalStateException("Not supported"); + } + + @Override + public Locale getLocale() { + throw new IllegalStateException("Not supported"); + } + + @Override + public Enumeration getLocales() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isSecure() { + throw new IllegalStateException("Not supported"); + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getRealPath(String path) { + throw new IllegalStateException("Not supported"); + } + + @Override + public int getRemotePort() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getLocalName() { + throw new IllegalStateException("Not supported"); + } + + @Override + public String getLocalAddr() { + throw new IllegalStateException("Not supported"); + } + + @Override + public int getLocalPort() { + throw new IllegalStateException("Not supported"); + } + + @Override + public ServletContext getServletContext() { + throw new IllegalStateException("Not supported"); + } + + @Override + public AsyncContext startAsync() throws IllegalStateException { + throw new IllegalStateException("Not supported"); + } + + @Override + public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) + throws IllegalStateException { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isAsyncStarted() { + throw new IllegalStateException("Not supported"); + } + + @Override + public boolean isAsyncSupported() { + throw new IllegalStateException("Not supported"); + } + + @Override + public AsyncContext getAsyncContext() { + throw new IllegalStateException("Not supported"); + } + + @Override + public DispatcherType getDispatcherType() { + throw new IllegalStateException("Not supported"); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestHAConfiguration.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestHAConfiguration.java new file mode 100644 index 0000000000..18cfa7bd7d --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestHAConfiguration.java @@ -0,0 +1,208 @@ +/** + * 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.atlas.notification.rest; + +import org.apache.atlas.security.SecurityProperties; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * A wrapper for getting configuration entries related to HighAvailability. + */ +public final class RestHAConfiguration { + + + public static final String ATLAS_REST_SERVER_ZK_ROOT_DEFAULT = "/apache_atlas_rest"; + + private RestHAConfiguration() { + } + + public static final String ATLAS_REST_SERVER_HA_PREFIX = "atlas.rest.server.ha."; + public static final String ZOOKEEPER_PREFIX = "zookeeper."; + public static final String ATLAS_REST_SERVER_HA_ZK_ROOT_KEY = ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "zkroot"; + public static final String ATLAS_REST_SERVER_HA_ENABLED_KEY = ATLAS_REST_SERVER_HA_PREFIX + "enabled"; + public static final String ATLAS_REST_SERVER_ADDRESS_PREFIX = "atlas.rest.server.address."; + public static final String ATLAS_REST_SERVER_IDS = "atlas.rest.server.ids"; + public static final String HA_ZOOKEEPER_CONNECT = ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "connect"; + public static final int DEFAULT_ZOOKEEPER_CONNECT_SLEEPTIME_MILLIS = 1000; + public static final String HA_ZOOKEEPER_RETRY_SLEEPTIME_MILLIS = + ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "retry.sleeptime.ms"; + public static final String HA_ZOOKEEPER_NUM_RETRIES = ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "num.retries"; + public static final int DEFAULT_ZOOKEEPER_CONNECT_NUM_RETRIES = 3; + public static final String HA_ZOOKEEPER_SESSION_TIMEOUT_MS = + ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "session.timeout.ms"; + public static final int DEFAULT_ZOOKEEPER_SESSION_TIMEOUT_MILLIS = 20000; + public static final String HA_ZOOKEEPER_ACL = ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "acl"; + public static final String HA_ZOOKEEPER_AUTH = ATLAS_REST_SERVER_HA_PREFIX + ZOOKEEPER_PREFIX + "auth"; + + /** + * Return whether HA is enabled or not. + * + * @param configuration underlying configuration instance + * @return + */ + public static boolean isHAEnabled(Configuration configuration) { + boolean ret = false; + + if (configuration.containsKey(RestHAConfiguration.ATLAS_REST_SERVER_HA_ENABLED_KEY)) { + ret = configuration.getBoolean(ATLAS_REST_SERVER_HA_ENABLED_KEY); + } else { + String[] ids = configuration.getStringArray(RestHAConfiguration.ATLAS_REST_SERVER_IDS); + + ret = ids != null && ids.length > 1; + } + + return ret; + } + + /** + * Get the web server address that a server instance with the passed ID is bound to. + *

+ * This method uses the property {@link SecurityProperties#TLS_ENABLED} to determine whether + * the URL is http or https. + * + * @param configuration underlying configuration + * @param serverId serverId whose host:port property is picked to build the web server address. + * @return + */ + public static String getBoundAddressForId(Configuration configuration, String serverId) { + String hostPort = configuration.getString(ATLAS_REST_SERVER_ADDRESS_PREFIX + serverId); + boolean isSecure = configuration.getBoolean(SecurityProperties.TLS_ENABLED); + String protocol = (isSecure) ? "https://" : "http://"; + return protocol + hostPort; + } + + public static List getServerInstances(Configuration configuration) { + String[] serverIds = configuration.getStringArray(ATLAS_REST_SERVER_IDS); + List serverInstances = new ArrayList<>(serverIds.length); + for (String serverId : serverIds) { + serverInstances.add(getBoundAddressForId(configuration, serverId)); + } + return serverInstances; + } + + /** + * A collection of Zookeeper specific configuration that is used by High Availability code. + */ + public static class ZookeeperProperties { + private String connectString; + private String zkRoot; + private int retriesSleepTimeMillis; + private int numRetries; + private int sessionTimeout; + private String acl; + private String auth; + + public ZookeeperProperties(String connectString, String zkRoot, int retriesSleepTimeMillis, int numRetries, + int sessionTimeout, String acl, String auth) { + this.connectString = connectString; + this.zkRoot = zkRoot; + this.retriesSleepTimeMillis = retriesSleepTimeMillis; + this.numRetries = numRetries; + this.sessionTimeout = sessionTimeout; + this.acl = acl; + this.auth = auth; + } + + public String getConnectString() { + return connectString; + } + + public int getRetriesSleepTimeMillis() { + return retriesSleepTimeMillis; + } + + public int getNumRetries() { + return numRetries; + } + + public int getSessionTimeout() { + return sessionTimeout; + } + + public String getAcl() { + return acl; + } + + public String getAuth() { + return auth; + } + + public String getZkRoot() { + return zkRoot; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ZookeeperProperties that = (ZookeeperProperties) o; + return retriesSleepTimeMillis == that.retriesSleepTimeMillis && + numRetries == that.numRetries && + sessionTimeout == that.sessionTimeout && + Objects.equals(connectString, that.connectString) && + Objects.equals(zkRoot, that.zkRoot) && + Objects.equals(acl, that.acl) && + Objects.equals(auth, that.auth); + } + + @Override + public int hashCode() { + return Objects.hash(connectString, zkRoot, retriesSleepTimeMillis, numRetries, sessionTimeout, acl, auth); + } + + public boolean hasAcl() { + return getAcl() != null; + } + + public boolean hasAuth() { + return getAuth() != null; + } + } + + public static ZookeeperProperties getZookeeperProperties(Configuration configuration) { + String[] zkServers; + if (configuration.containsKey(HA_ZOOKEEPER_CONNECT)) { + zkServers = configuration.getStringArray(HA_ZOOKEEPER_CONNECT); + } else { + zkServers = configuration.getStringArray("atlas.kafka." + ZOOKEEPER_PREFIX + "connect"); + } + + String zkRoot = configuration.getString(ATLAS_REST_SERVER_HA_ZK_ROOT_KEY, ATLAS_REST_SERVER_ZK_ROOT_DEFAULT); + int retriesSleepTimeMillis = configuration.getInt(HA_ZOOKEEPER_RETRY_SLEEPTIME_MILLIS, + DEFAULT_ZOOKEEPER_CONNECT_SLEEPTIME_MILLIS); + + int numRetries = configuration.getInt(HA_ZOOKEEPER_NUM_RETRIES, DEFAULT_ZOOKEEPER_CONNECT_NUM_RETRIES); + + int sessionTimeout = configuration.getInt(HA_ZOOKEEPER_SESSION_TIMEOUT_MS, + DEFAULT_ZOOKEEPER_SESSION_TIMEOUT_MILLIS); + + String acl = configuration.getString(HA_ZOOKEEPER_ACL); + String auth = configuration.getString(HA_ZOOKEEPER_AUTH); + + return new ZookeeperProperties(StringUtils.join(zkServers, ','), + zkRoot, + retriesSleepTimeMillis, numRetries, + sessionTimeout, acl, auth); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java new file mode 100644 index 0000000000..3f1711c8b5 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java @@ -0,0 +1,208 @@ +/** + * 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.atlas.notification.rest; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasConstants; +import org.apache.atlas.notification.rest.web.service.EmbeddedServer; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.PropertiesConfiguration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Iterator; + +public class RestNotificationMain { + private static final Logger LOG = LoggerFactory.getLogger(RestNotificationMain.class); + private static final String APP_PATH = "app"; + private static final String APP_PORT = "port"; + private static final String REST_NOTIFICATION_HOME = "atlas.rest.notification.home"; + private static final String REST_NOTIFICATION_DATA = "atlas.rest.notification.data"; + private static final String REST_NOTIFICATION_LOG_DIR = "atlas.rest.notification.log.dir"; + private static final String REST_SERVER_HTTPS_PORT = "atlas.rest.server.https.port"; + private static final String REST_SERVER_HTTP_PORT = "atlas.rest.server.http.port"; + + private static EmbeddedServer server; + + /** + * Prevent users from constructing this. + */ + private RestNotificationMain() { + } + + protected static CommandLine parseArgs(String[] args) throws ParseException { + Options options = new Options(); + Option opt; + + opt = new Option(APP_PATH, true, "Application Path"); + opt.setRequired(false); + options.addOption(opt); + + opt = new Option(APP_PORT, true, "Application Port"); + opt.setRequired(false); + options.addOption(opt); + + return new DefaultParser().parse(options, args); + } + + public static void main(String[] args) throws Exception { + CommandLine cmd = parseArgs(args); + PropertiesConfiguration buildConfiguration = new PropertiesConfiguration("rest-notification-buildinfo.properties"); + String appPath = "rest-notification-webapp/target/rest-notification-webapp" /*\\+ getProjectVersion(buildConfiguration)*/; + + if (cmd.hasOption(APP_PATH)) { + appPath = cmd.getOptionValue(APP_PATH); + } + + setApplicationHome(); + Configuration configuration = ApplicationProperties.get(); + final String enableTLSFlag = configuration.getString(SecurityProperties.TLS_ENABLED); + final String appHost = configuration.getString(SecurityProperties.BIND_ADDRESS, EmbeddedServer.REST_DEFAULT_BIND_ADDRESS); + + if (!isLocalAddress(InetAddress.getByName(appHost))) { + String msg = + "Failed to start Rest Notification server. Address " + appHost + + " does not belong to this host. Correct configuration parameter: " + + SecurityProperties.BIND_ADDRESS; + LOG.error(msg); + throw new IOException(msg); + } + + final int appPort = getApplicationPort(cmd, enableTLSFlag, configuration); + System.setProperty(AtlasConstants.SYSTEM_PROPERTY_APP_PORT, String.valueOf(appPort)); + final boolean enableTLS = isTLSEnabled(enableTLSFlag, appPort); + configuration.setProperty(SecurityProperties.TLS_ENABLED, String.valueOf(enableTLS)); + + showStartupInfo(buildConfiguration, enableTLS, appPort); + + server = EmbeddedServer.newServer(appHost, appPort, appPath, enableTLS); + installLogBridge(); + + server.start(); + } + + public static String getProjectVersion(PropertiesConfiguration buildConfiguration) { + return buildConfiguration.getString("project.version"); + } + + private static void setApplicationHome() { + if (System.getProperty(REST_NOTIFICATION_HOME) == null) { + System.setProperty(REST_NOTIFICATION_HOME, "target"); + } + if (System.getProperty(REST_NOTIFICATION_DATA) == null) { + System.setProperty(REST_NOTIFICATION_DATA, "target/data"); + } + if (System.getProperty(REST_NOTIFICATION_LOG_DIR) == null) { + System.setProperty(REST_NOTIFICATION_LOG_DIR, "target/logs"); + } + } + + private static boolean isLocalAddress(InetAddress addr) { + // Check if the address is any local or loop back + boolean local = addr.isAnyLocalAddress() || addr.isLoopbackAddress(); + + // Check if the address is defined on any interface + if (!local) { + try { + local = NetworkInterface.getByInetAddress(addr) != null; + } catch (SocketException e) { + local = false; + } + } + return local; + } + + + static int getApplicationPort(CommandLine cmd, String enableTLSFlag, Configuration configuration) { + String optionValue = cmd.hasOption(APP_PORT) ? cmd.getOptionValue(APP_PORT) : null; + + final int appPort; + + if (StringUtils.isNotEmpty(optionValue)) { + appPort = Integer.valueOf(optionValue); + } else { + // default : atlas.enableTLS is true + appPort = getPortValue(configuration, enableTLSFlag); + } + + return appPort; + } + + + private static int getPortValue(Configuration configuration, String enableTLSFlag) { + int appPort; + + assert configuration != null; + appPort = StringUtils.isEmpty(enableTLSFlag) || enableTLSFlag.equals("true") ? + configuration.getInt(REST_SERVER_HTTPS_PORT, 41443) : + configuration.getInt(REST_SERVER_HTTP_PORT, 41000); + return appPort; + } + + private static boolean isTLSEnabled(String enableTLSFlag, int appPort) { + return Boolean.valueOf(StringUtils.isEmpty(enableTLSFlag) ? + System.getProperty(org.apache.atlas.security.SecurityProperties.TLS_ENABLED, (appPort % 1000) == 443 ? "true" : "false") : enableTLSFlag); + } + + + private static void showStartupInfo(PropertiesConfiguration buildConfiguration, boolean enableTLS, int appPort) { + StringBuilder buffer = new StringBuilder(); + buffer.append("\n############################################"); + buffer.append("############################################"); + buffer.append("\n Rest Notification Server (STARTUP)"); + buffer.append("\n"); + try { + final Iterator keys = buildConfiguration.getKeys(); + while (keys.hasNext()) { + String key = keys.next(); + buffer.append('\n').append('\t').append(key). + append(":\t").append(buildConfiguration.getProperty(key)); + } + } catch (Throwable e) { + buffer.append("*** Unable to get build info ***"); + } + buffer.append("\n############################################"); + buffer.append("############################################"); + LOG.info(buffer.toString()); + LOG.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + LOG.info("Server starting with TLS ? {} on port {}", enableTLS, appPort); + LOG.info("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); + } + + private static void installLogBridge() { + // Optionally remove existing handlers attached to j.u.l root logger + SLF4JBridgeHandler.removeHandlersForRootLogger(); // (since SLF4J 1.6.5) + + // add SLF4JBridgeHandler to j.u.l's root logger, should be done once during + // the initialization phase of your application + SLF4JBridgeHandler.install(); + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/SecurityProperties.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/SecurityProperties.java new file mode 100644 index 0000000000..22d266e411 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/SecurityProperties.java @@ -0,0 +1,32 @@ +/** + * 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.atlas.notification.rest; + +public final class SecurityProperties { + + private SecurityProperties() { + } + + public static final String TLS_ENABLED = "atlas.rest.notification.enableTLS"; + public static final String BIND_ADDRESS = "atlas.rest.notification.server.bind.address"; + public static final String KEYSTORE_PASSWORD_KEY = "keystore.password"; + public static final String TRUSTSTORE_PASSWORD_KEY = "truststore.password"; + public static final String SERVER_CERT_PASSWORD_KEY = "password"; + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java new file mode 100755 index 0000000000..686b9ff0b1 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java @@ -0,0 +1,223 @@ +/** + * 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.atlas.notification.rest; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.atlas.AtlasClient; +import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.utils.AtlasJson; +import org.apache.atlas.utils.ParamChecker; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.util.UriUtils; + +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.Charset; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Utility functions for dealing with servlets. + */ +public final class Servlets { + + private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); + + private Servlets() { + /* singleton */ + } + + public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; + public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; + + private static final int QUERY_PARAM_MAX_LENGTH = AtlasConfiguration.QUERY_PARAM_MAX_LENGTH.getInt(); + + /** + * Returns the user of the given request. + * + * @param httpRequest an HTTP servlet request + * @return the user + */ + public static String getUserFromRequest(HttpServletRequest httpRequest) { + String user = httpRequest.getRemoteUser(); + if (!StringUtils.isEmpty(user)) { + return user; + } + + user = httpRequest.getParameter("user.name"); // available in query-param + if (!StringUtils.isEmpty(user)) { + return user; + } + + user = httpRequest.getHeader("Remote-User"); // backwards-compatibility + if (!StringUtils.isEmpty(user)) { + return user; + } + + user = getDoAsUser(httpRequest); + if (!StringUtils.isEmpty(user)) { + return user; + } + + return null; + } + + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); + private static final String DO_AS = "doAs"; + + public static String getDoAsUser(HttpServletRequest request) { + if (StringUtils.isNoneEmpty(request.getQueryString())) { + List list = URLEncodedUtils.parse(request.getQueryString(), UTF8_CHARSET); + if (list != null) { + for (NameValuePair nv : list) { + if (DO_AS.equals(nv.getName())) { + return nv.getValue(); + } + } + } + } + return null; + } + + /** + * Returns the URI of the given request. + * + * @param httpRequest an HTTP servlet request + * @return the URI, including the query string + */ + public static String getRequestURI(HttpServletRequest httpRequest) { + final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURI()); + if (httpRequest.getQueryString() != null) { + url.append('?').append(httpRequest.getQueryString()); + } + + return url.toString(); + } + + /** + * Returns the full URL of the given request. + * + * @param httpRequest an HTTP servlet request + * @return the full URL, including the query string + */ + public static String getRequestURL(HttpServletRequest httpRequest) { + final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURL()); + if (httpRequest.getQueryString() != null) { + url.append('?').append(httpRequest.getQueryString()); + } + + return url.toString(); + } + + public static Response getErrorResponse(AtlasBaseException e) { + String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); + Response response = getErrorResponse(message, e.getAtlasErrorCode().getHttpCode()); + + return response; + } + + public static Response getErrorResponse(Throwable e, Response.Status status) { + String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); + Response response = getErrorResponse(message, status); + + return response; + } + + public static Response getErrorResponse(String message, Response.Status status) { + Object errorEntity = escapeJsonString(message); + ObjectNode errorJson = AtlasJson.createV1ObjectNode(AtlasClient.ERROR, errorEntity); + + return Response.status(status).entity(errorJson).type(JSON_MEDIA_TYPE).build(); + } + + public static String getRequestPayload(HttpServletRequest request) throws IOException { + //request is an instance of LocalServletRequest for calls from LocalAtlasClient + if (request instanceof LocalServletRequest) { + return ((LocalServletRequest) request).getPayload(); + } + + StringWriter writer = new StringWriter(); + IOUtils.copy(request.getInputStream(), writer); + return writer.toString(); + } + + public static String getRequestId() { + return Thread.currentThread().getName(); + } + + public static String escapeJsonString(String inputStr) { + ParamChecker.notNull(inputStr, "Input String cannot be null"); + return StringEscapeUtils.escapeJson(inputStr); + } + + public static String getHostName(HttpServletRequest httpServletRequest) { + return httpServletRequest.getLocalName(); + } + + public static String getUserName(HttpServletRequest httpServletRequest) { + return httpServletRequest.getRemoteUser(); + } + + public static Map getParameterMap(HttpServletRequest request) { + Map attributes = new HashMap<>(); + + if (MapUtils.isNotEmpty(request.getParameterMap())) { + for (Map.Entry e : request.getParameterMap().entrySet()) { + String key = e.getKey(); + + if (key != null) { + String[] values = e.getValue(); + String value = values != null && values.length > 0 ? values[0] : null; + + attributes.put(key, value); + } + } + } + + return attributes; + } + + public static void validateQueryParamLength(String paramName, String paramValue) throws AtlasBaseException { + if (StringUtils.isNotEmpty(paramValue) && paramValue.length() > QUERY_PARAM_MAX_LENGTH) { + throw new AtlasBaseException(AtlasErrorCode.INVALID_QUERY_PARAM_LENGTH, paramName); + } + } + + public static String decodeQueryString(String query) throws AtlasBaseException { + try { + return UriUtils.decode(query,"UTF-8"); + } catch (Exception e) { + LOG.error("Error occurred while decoding query:" + query, e.getMessage()); + throw new AtlasBaseException(e.getMessage()); + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java new file mode 100755 index 0000000000..bc1415c0f8 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java @@ -0,0 +1,227 @@ +/* + * 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.atlas.notification.rest.util; + +import org.apache.atlas.notification.rest.web.dao.UserDao; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.Options; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.alias.CredentialProvider; +import org.apache.hadoop.security.alias.CredentialProviderFactory; + +import java.io.Console; +import java.io.IOException; +import java.util.Arrays; + + +import static org.apache.atlas.notification.rest.SecurityProperties.KEYSTORE_PASSWORD_KEY; +import static org.apache.atlas.notification.rest.SecurityProperties.SERVER_CERT_PASSWORD_KEY; +import static org.apache.atlas.notification.rest.SecurityProperties.TRUSTSTORE_PASSWORD_KEY; +/** + * A utility class for generating a credential provider containing the entries required for supporting the SSL + * implementation + * of the DGC server. + */ +public class CredentialProviderUtility { + private static final String[] KEYS = new String[] { KEYSTORE_PASSWORD_KEY, TRUSTSTORE_PASSWORD_KEY, SERVER_CERT_PASSWORD_KEY }; + public static abstract class TextDevice { + public abstract void printf(String fmt, Object... params); + + public abstract String readLine(String fmt, Object... args); + + public abstract char[] readPassword(String fmt, Object... args); + + } + + private static TextDevice DEFAULT_TEXT_DEVICE = new TextDevice() { + Console console = System.console(); + + @Override + public void printf(String fmt, Object... params) { + console.printf(fmt, params); + } + + @Override + public String readLine(String fmt, Object... args) { + return console.readLine(fmt, args); + } + + @Override + public char[] readPassword(String fmt, Object... args) { + return console.readPassword(fmt, args); + } + }; + + public static TextDevice textDevice = DEFAULT_TEXT_DEVICE; + + public static void main(String[] args) throws IOException { + try { + CommandLine cmd = new DefaultParser().parse(createOptions(), args); + boolean generatePasswordOption = cmd.hasOption("g"); + String key = cmd.getOptionValue("k"); + char[] cred = null; + String providerPath = cmd.getOptionValue("f"); + + if (cmd.hasOption("p")) { + cred = cmd.getOptionValue("p").toCharArray(); + } + + if (generatePasswordOption) { + String userName = cmd.getOptionValue("u"); + String password = cmd.getOptionValue("p"); + if (userName != null && password != null) { + String encryptedPassword = UserDao.encrypt(password); + boolean silentOption = cmd.hasOption("s"); + + if (silentOption) { + System.out.println(encryptedPassword); + } else { + System.out.println("Your encrypted password is : " + encryptedPassword); + } + } else { + System.out.println("Please provide username and password as input. Usage: cputil.py -g -u -p "); + } + + return; + } + + if (key != null && cred != null && providerPath != null) { + if (!StringUtils.isEmpty(String.valueOf(cred))) { + Configuration conf = new Configuration(false); + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, providerPath); + CredentialProvider provider = CredentialProviderFactory.getProviders(conf).get(0); + provider.createCredentialEntry(key, cred); + provider.flush(); + System.out.println("Password is stored in Credential Provider"); + } else { + System.out.println("Please enter a valid password"); + } + return; + } + } catch (Exception e) { + System.out.println("Exception while generatePassword " + e.getMessage()); + return; + } + + // prompt for the provider name + CredentialProvider provider = getCredentialProvider(textDevice); + + if(provider != null) { + for (String key : KEYS) { + char[] cred = getPassword(textDevice, key); + + // create a credential entry and store it + if (provider.getCredentialEntry(key) != null) { + String choice = textDevice.readLine("Entry for %s already exists. Overwrite? (y/n) [y]:", key); + boolean overwrite = StringUtils.isEmpty(choice) || choice.equalsIgnoreCase("y"); + + if (overwrite) { + provider.deleteCredentialEntry(key); + provider.flush(); + provider.createCredentialEntry(key, cred); + provider.flush(); + + textDevice.printf("Entry for %s was overwritten with the new value.\n", key); + } else { + textDevice.printf("Entry for %s was not overwritten.\n", key); + } + } else { + provider.createCredentialEntry(key, cred); + + provider.flush(); + } + } + } + } + + private static Options createOptions() { + Options options = new Options(); + + options.addOption("k", "ldapkey", true, "key"); + options.addOption("f", "ldapPath", true, "path"); + options.addOption("g", "generatePassword", false, "Generate Password"); + options.addOption("s", "silent", false, "Silent"); + options.addOption("u", "username", true, "UserName"); + options.addOption("p", "password", true, "Password"); + + return options; + } + + /** + * Retrieves a password from the command line. + * @param textDevice the system console. + * @param key the password key/alias. + * @return the password. + */ + private static char[] getPassword(TextDevice textDevice, String key) { + char[] ret; + + while (true) { + char[] passwd1 = textDevice.readPassword("Please enter the password value for %s:", key); + char[] passwd2 = textDevice.readPassword("Please enter the password value for %s again:", key); + boolean isMatch = Arrays.equals(passwd1, passwd2); + + if (!isMatch) { + textDevice.printf("Password entries don't match. Please try again.\n"); + } else { + if (passwd1 == null || passwd1.length == 0) { + textDevice.printf("An empty password is not valid. Please try again.\n"); + } else { + ret = passwd1; + + if (passwd2 != null) { + Arrays.fill(passwd2, ' '); + } + + break; + } + } + + if (passwd1 != null) { + Arrays.fill(passwd1, ' '); + } + + if (passwd2 != null) { + Arrays.fill(passwd2, ' '); + } + } + + return ret; + } + + /**\ + * Returns a credential provider for the entered JKS path. + * @param textDevice the system console. + * @return the Credential provider + * @throws IOException + */ + private static CredentialProvider getCredentialProvider(TextDevice textDevice) throws IOException { + String providerPath = textDevice.readLine("Please enter the full path to the credential provider:"); + + if (providerPath != null) { + Configuration conf = new Configuration(false); + + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, providerPath); + + return CredentialProviderFactory.getProviders(conf).get(0); + } + + return null; + } +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java new file mode 100644 index 0000000000..6cca308681 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java @@ -0,0 +1,266 @@ +/* + * 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.atlas.notification.rest.web.dao; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.notification.rest.web.model.User; +import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationException; +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.crypto.bcrypt.BCrypt; +import org.springframework.security.crypto.codec.Hex; +import org.springframework.security.crypto.codec.Utf8; +import org.springframework.stereotype.Repository; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + + +@Repository +public class UserDao { + private static final Logger LOG = LoggerFactory.getLogger(UserDao.class); + + private static final String DEFAULT_USER_CREDENTIALS_PROPERTIES = "users-credentials.properties"; + private static boolean v1ValidationEnabled = true; + private static boolean v2ValidationEnabled = true; + + private Properties userLogins = new Properties(); + + @PostConstruct + public void init() { + loadFileLoginsDetails(); + } + + void loadFileLoginsDetails() { + userLogins.clear(); + + InputStream inStr = null; + + try { + Configuration configuration = ApplicationProperties.get(); + + v1ValidationEnabled = configuration.getBoolean("atlas.authentication.method.file.v1-validation.enabled", true); + v2ValidationEnabled = configuration.getBoolean("atlas.authentication.method.file.v2-validation.enabled", true); + + inStr = ApplicationProperties.getFileAsInputStream(configuration, "atlas.authentication.method.file.filename", DEFAULT_USER_CREDENTIALS_PROPERTIES); + + userLogins.load(inStr); + } catch (IOException | AtlasException e) { + LOG.error("Error while reading user.properties file", e); + + throw new RuntimeException(e); + } finally { + if (inStr != null) { + try { + inStr.close(); + } catch (Exception excp) { + // ignore + } + } + } + } + + public User loadUserByUsername(final String username) throws AuthenticationException { + String userdetailsStr = userLogins.getProperty(username); + + if (userdetailsStr == null || userdetailsStr.isEmpty()) { + throw new UsernameNotFoundException("Username not found." + username); + } + + String password = ""; + String role = ""; + String[] dataArr = userdetailsStr.split("::"); + + if (dataArr != null && dataArr.length == 2) { + role = dataArr[0]; + password = dataArr[1]; + } else { + LOG.error("User role credentials is not set properly for {}", username); + + throw new AtlasAuthenticationException("User role credentials is not set properly for " + username ); + } + + List grantedAuths = new ArrayList<>(); + + if (StringUtils.hasText(role)) { + grantedAuths.add(new SimpleGrantedAuthority(role)); + } else { + LOG.error("User role credentials is not set properly for {}", username); + + throw new AtlasAuthenticationException("User role credentials is not set properly for " + username ); + } + + User userDetails = new User(username, password, grantedAuths); + + return userDetails; + } + + @VisibleForTesting + public void setUserLogins(Properties userLogins) { + this.userLogins = userLogins; + } + + public static String encrypt(String password) { + String ret = null; + + try { + ret = BCrypt.hashpw(password, BCrypt.gensalt()); + } catch (Throwable excp) { + LOG.warn("UserDao.encrypt(): failed", excp); + } + + return ret; + } + + public static boolean checkEncrypted(String password, String encryptedPwd, String userName) { + boolean ret = checkPasswordBCrypt(password, encryptedPwd); + + if (!ret && v2ValidationEnabled) { + ret = checkPasswordSHA256WithSalt(password, encryptedPwd, userName); + } + + if (!ret && v1ValidationEnabled) { + ret = checkPasswordSHA256(password, encryptedPwd); + } + + return ret; + } + + private static boolean checkPasswordBCrypt(String password, String encryptedPwd) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordBCrypt()"); + } + + boolean ret = false; + + try { + ret = BCrypt.checkpw(password, encryptedPwd); + } catch (Throwable excp) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordBCrypt(): failed", excp); + } + } + + return ret; + } + + private static boolean checkPasswordSHA256WithSalt(String password, String encryptedPwd, String salt) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordSHA256WithSalt()"); + } + + boolean ret = false; + + try { + String hash = encodePassword(password, salt); + + ret = hash != null && hash.equals(encryptedPwd); + } catch (Throwable excp) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordSHA256WithSalt(): failed", excp); + } + } + + return ret; + } + + private static boolean checkPasswordSHA256(String password, String encryptedPwd) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordSHA256()"); + } + + boolean ret = false; + + try { + String hash = getSha256Hash(password); + + ret = hash != null && hash.equals(encryptedPwd); + } catch (Throwable excp) { + if (LOG.isDebugEnabled()) { + LOG.debug("checkPasswordSHA256(): failed", excp); + } + } + + return ret; + } + + private static String getSha256Hash(String base) throws AtlasAuthenticationException { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] hash = digest.digest(base.getBytes("UTF-8")); + StringBuffer hexString = new StringBuffer(); + + for (byte aHash : hash) { + String hex = Integer.toHexString(0xff & aHash); + + if (hex.length() == 1) { + hexString.append('0'); + } + + hexString.append(hex); + } + + return hexString.toString(); + } catch (Exception ex) { + throw new AtlasAuthenticationException("Exception while encoding password.", ex); + } + } + + public static String encodePassword(String rawPass, Object salt) { + String saltedPass = mergePasswordAndSalt(rawPass, salt, false); + MessageDigest messageDigest = getMessageDigest(); + byte[] digest = messageDigest.digest(Utf8.encode(saltedPass)); + + return new String(Hex.encode(digest)); + } + + protected static final MessageDigest getMessageDigest() throws IllegalArgumentException { + try { + return MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException var2) { + throw new IllegalArgumentException("No such algorithm [SHA-256 ]"); + } + } + + protected static String mergePasswordAndSalt(String password, Object salt, boolean strict) { + if (!StringUtils.hasText(password)) { + password = ""; + } + + if (strict && salt != null && (salt.toString().lastIndexOf("{") != -1 || salt.toString().lastIndexOf("}") != -1)) { + throw new IllegalArgumentException("Cannot use { or } in salt.toString()"); + } else { + return StringUtils.hasText(salt.toString()) ? password + "{" + salt.toString() + "}" : password; + } + } + +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java new file mode 100644 index 0000000000..f3b3b46e37 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java @@ -0,0 +1,176 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; +import org.apache.atlas.notification.rest.web.service.ServiceState; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriUtils; + +import javax.inject.Inject; +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.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.HttpHeaders; +import java.io.IOException; + +/** + * A servlet {@link Filter} that redirects web requests from a passive Atlas server instance to an active one. + * + * All requests to an active instance pass through. Requests received by a passive instance are redirected + * by identifying the currently active server. Requests to servers which are in transition are returned with + * an error SERVICE_UNAVAILABLE. Identification of this state is carried out using + * {@link ServiceState} and {@link ActiveInstanceState}. + */ +@Component +public class ActiveServerFilter implements Filter { + + private static final Logger LOG = LoggerFactory.getLogger(ActiveServerFilter.class); + + private final ActiveInstanceState activeInstanceState; + private ServiceState serviceState; + + @Inject + public ActiveServerFilter(ActiveInstanceState activeInstanceState, ServiceState serviceState) { + this.activeInstanceState = activeInstanceState; + this.serviceState = serviceState; + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + LOG.info("ActiveServerFilter initialized"); + } + + /** + * Determines if this Atlas server instance is passive and redirects to active if so. + * + * @param servletRequest Request object from which the URL and other parameters are determined. + * @param servletResponse Response object to handle the redirect. + * @param filterChain Chain to pass through requests if the instance is Active. + * @throws IOException + * @throws ServletException + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, + FilterChain filterChain) throws IOException, ServletException { + if (isFilteredURI(servletRequest)) { + LOG.debug("Is a filtered URI: {}. Passing request downstream.", + ((HttpServletRequest)servletRequest).getRequestURI()); + filterChain.doFilter(servletRequest, servletResponse); + } else if (isInstanceActive()) { + LOG.debug("Active. Passing request downstream"); + filterChain.doFilter(servletRequest, servletResponse); + } else if (serviceState.isInstanceInTransition()) { + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + LOG.error("Instance in transition. Service may not be ready to return a result"); + httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } else { + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + String activeServerAddress = activeInstanceState.getActiveServerAddress(); + if (activeServerAddress == null) { + LOG.error("Could not retrieve active server address as it is null. Cannot redirect request {}", + ((HttpServletRequest)servletRequest).getRequestURI()); + httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + } else { + handleRedirect((HttpServletRequest) servletRequest, httpServletResponse, activeServerAddress); + } + } + } + + + private boolean isRootURI(ServletRequest servletRequest) { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String requestURI = httpServletRequest.getRequestURI(); + return requestURI.equals("/"); + } + + boolean isInstanceActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + + private void handleRedirect(HttpServletRequest servletRequest, HttpServletResponse httpServletResponse, + String activeServerAddress) throws IOException { + String requestURI = servletRequest.getRequestURI(); + String queryString = servletRequest.getQueryString(); + + if (queryString != null && (!queryString.isEmpty())) { + queryString = UriUtils.encodeQuery(queryString, "UTF-8"); + } + + if ((queryString != null) && (!queryString.isEmpty())) { + requestURI += "?" + queryString; + } + + if (requestURI == null) { + requestURI = "/"; + } + String redirectLocation = activeServerAddress + requestURI; + LOG.info("Not active. Redirecting to {}", redirectLocation); + // A POST/PUT/DELETE require special handling by sending HTTP 307 instead of the regular 301/302. + // Reference: http://stackoverflow.com/questions/2068418/whats-the-difference-between-a-302-and-a-307-redirect + if (isUnsafeHttpMethod(servletRequest)) { + httpServletResponse.setHeader(HttpHeaders.LOCATION, redirectLocation); + httpServletResponse.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); + } else { + httpServletResponse.sendRedirect(redirectLocation); + } + } + + private boolean isUnsafeHttpMethod(HttpServletRequest httpServletRequest) { + String method = httpServletRequest.getMethod(); + return (method.equals(HttpMethod.POST)) || + (method.equals(HttpMethod.PUT)) || + (method.equals(HttpMethod.DELETE)); + } + + @Override + public void destroy() { + + } + + final String adminUriNotFiltered[] = { "/admin/export", "/admin/import", "/admin/importfile", "/admin/audits", + "/admin/purge", "/admin/expimp/audit", "/admin/metrics", "/admin/server", "/admin/audit/", "admin/tasks", "admin/async/import", "admin/async/import/status"}; + private boolean isFilteredURI(ServletRequest servletRequest) { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String requestURI = httpServletRequest.getRequestURI(); + + if(requestURI.contains("/admin/")) { + for (String s : adminUriNotFiltered) { + if (requestURI.contains(s)) { + LOG.error("URL not supported in HA mode: {}", requestURI); + return false; + } + } + + return true; + } else { + return false; + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java new file mode 100644 index 0000000000..a02dff4823 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.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.atlas.notification.rest.web.filters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@SuppressWarnings("deprecation") +@Component +public class AtlasAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { + + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationEntryPoint.class); + + private String loginPath = "/login.jsp"; + + @Inject + public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { + super(loginFormUrl); + } + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) + throws IOException, ServletException { + + + String ajaxRequestHeader = request.getHeader("X-Requested-With"); + response.setHeader("X-Frame-Options", "DENY"); + + if ("XMLHttpRequest".equals(ajaxRequestHeader)) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + } else { + LOG.debug("redirecting to login page loginPath" + loginPath); + response.sendRedirect(loginPath); + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java new file mode 100644 index 0000000000..b4121e2682 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java @@ -0,0 +1,818 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationProvider; +import org.apache.atlas.security.SecurityProperties; +import org.apache.atlas.utils.AuthenticationUtil; +import org.apache.commons.collections.iterators.IteratorEnumeration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationConverter; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authentication.client.AuthenticatedURL; +import org.apache.hadoop.security.authentication.client.AuthenticationException; +import org.apache.hadoop.security.authentication.client.KerberosAuthenticator; +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; +import org.apache.hadoop.security.authentication.server.AuthenticationHandler; +import org.apache.hadoop.security.authentication.server.AuthenticationToken; +import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; +import org.apache.hadoop.security.authentication.util.Signer; +import org.apache.hadoop.security.authentication.util.SignerException; +import org.apache.hadoop.security.authentication.util.SignerSecretProvider; +import org.apache.hadoop.security.authorize.AuthorizationException; +import org.apache.hadoop.security.authorize.ProxyUsers; +import org.slf4j.MDC; +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 org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.security.Principal; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/** + * This enforces authentication as part of the filter before processing the request. + * todo: Subclass of {@link AuthenticationFilter}. + */ + +@Component +public class AtlasAuthenticationFilter extends AuthenticationFilter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class); + + private static final int SESSION_TIMEOUT_DISABLED_VALUE = -1; + private static final String CONFIG_KERBEROS_TOKEN_VALIDITY = "atlas.authentication.method.kerberos.token.validity"; + private static final String CONFIG_PROXY_USERS = "atlas.proxyusers"; + private static final String PREFIX = "atlas.authentication.method"; + private static final String[] DEFAULT_PROXY_USERS = new String[] { "knox" }; + private static final String CONF_PROXYUSER_PREFIX = "atlas.proxyuser"; + protected static final ServletContext nullContext = new NullServletContext(); + private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl"; + + private Signer signer; + private SignerSecretProvider secretProvider; + private final boolean isKerberos = AuthenticationUtil.isKerberosAuthenticationEnabled(); + private boolean isInitializedByTomcat; + private Set browserUserAgents; + private boolean supportKeyTabBrowserLogin = false; + private Configuration configuration; + private Properties headerProperties; + private Set atlasProxyUsers = new HashSet<>(); + private HttpServlet optionsServlet; + private boolean supportTrustedProxy = false; + private int sessionTimeout; + + private SecurityContextLogoutHandler logoutHandler; + + public AtlasAuthenticationFilter() { + LOG.info("==> AtlasAuthenticationFilter()"); + + try { + init(null); + } catch (ServletException e) { + LOG.error("Error while initializing AtlasAuthenticationFilter", e); + } + + LOG.info("<== AtlasAuthenticationFilter()"); + } + + /** + * Initialize the filter. + * + * @param filterConfig filter configuration. + * @throws ServletException thrown if the filter could not be initialized. + */ + @Override + public void init(FilterConfig filterConfig) throws ServletException { + LOG.info("==> AtlasAuthenticationFilter.init"); + + final FilterConfig globalConf = filterConfig; + final Map params = new HashMap<>(); + try { + configuration = ApplicationProperties.get(); + } catch (Exception e) { + throw new ServletException(e); + } + + if (configuration != null) { + headerProperties = ConfigurationConverter.getProperties(configuration.subset("atlas.headers")); + } + + String tokenValidityStr = null; + + if(configuration != null) { + tokenValidityStr = configuration.getString(CONFIG_KERBEROS_TOKEN_VALIDITY); + } + + if (StringUtils.isNotBlank(tokenValidityStr)) { + try { + Long tokenValidity = Long.parseLong(tokenValidityStr); + + if (tokenValidity > 0) { + params.put(AuthenticationFilter.AUTH_TOKEN_VALIDITY, tokenValidity.toString()); + } else { + throw new ServletException(tokenValidity + ": invalid value for property '" + CONFIG_KERBEROS_TOKEN_VALIDITY + "'. Must be a positive integer"); + } + } catch (NumberFormatException e) { + throw new ServletException(tokenValidityStr + ": invalid value for property '" + CONFIG_KERBEROS_TOKEN_VALIDITY + "'. Must be a positive integer", e); + } + } + + FilterConfig filterConfig1 = new FilterConfig() { + @Override + public ServletContext getServletContext() { + if (globalConf != null) { + return globalConf.getServletContext(); + } else { + return nullContext; + } + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration getInitParameterNames() { + return new IteratorEnumeration(params.keySet().iterator()); + } + + @Override + public String getInitParameter(String param) { + return params.get(param); + } + + @Override + public String getFilterName() { + return "AtlasAuthenticationFilter"; + } + }; + + super.init(filterConfig1); + + ProxyUsers.refreshSuperUserGroupsConfiguration(getProxyuserConfiguration(), CONF_PROXYUSER_PREFIX); + + optionsServlet = new HttpServlet() { + }; + optionsServlet.init(); + + if (sessionTimeout != -1) { + logoutHandler = new SecurityContextLogoutHandler(); + } + + LOG.info("<== AtlasAuthenticationFilter.init(filterConfig={})", filterConfig); + + } + + + @Override + public void initializeSecretProvider(FilterConfig filterConfig) throws ServletException { + LOG.info("==> AtlasAuthenticationFilter.initializeSecretProvider"); + + secretProvider = (SignerSecretProvider) filterConfig.getServletContext().getAttribute(AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE); + + if (secretProvider == null) { + // As tomcat cannot specify the provider object in the configuration. + // It'll go into this path + String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX); + + configPrefix = (configPrefix != null) ? configPrefix + "." : ""; + + try { + secretProvider = AuthenticationFilter.constructSecretProvider(filterConfig.getServletContext(), super.getConfiguration(configPrefix, filterConfig), false); + + this.isInitializedByTomcat = true; + } catch (Exception ex) { + throw new ServletException(ex); + } + } + + signer = new Signer(secretProvider); + + LOG.info("<== AtlasAuthenticationFilter.initializeSecretProvider(filterConfig={})", filterConfig); + } + + @Override + protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + LOG.info("==> AtlasAuthenticationFilter.getConfiguration()"); + + try { + configuration = ApplicationProperties.get(); + } catch (Exception e) { + throw new ServletException(e); + } + + Properties ret = new Properties(); + + String kerberosAuthEnabled = configuration != null ? configuration.getString("atlas.authentication.method.kerberos") : null; + + final String authMethod; + + if (kerberosAuthEnabled == null || kerberosAuthEnabled.equalsIgnoreCase("false")) { + LOG.info("No authentication method configured. Defaulting to simple authentication"); + + authMethod = "simple"; + } else if (kerberosAuthEnabled.equalsIgnoreCase("true")) { + authMethod = "kerberos"; + + if (configuration.getString("atlas.authentication.method.kerberos.name.rules") != null) { + ret.put("kerberos.name.rules", configuration.getString("atlas.authentication.method.kerberos.name.rules")); + } + + if (configuration.getString("atlas.authentication.method.kerberos.keytab") != null) { + ret.put("kerberos.keytab", configuration.getString("atlas.authentication.method.kerberos.keytab")); + } + + if (configuration.getString("atlas.authentication.method.kerberos.principal") != null) { + ret.put("kerberos.principal", configuration.getString("atlas.authentication.method.kerberos.principal")); + } + } else { + authMethod = ""; + } + + ret.put(AuthenticationFilter.AUTH_TYPE, authMethod); + ret.put(AuthenticationFilter.COOKIE_PATH, "/"); + + // add any config passed in as init parameters + Enumeration enumeration = filterConfig.getInitParameterNames(); + while (enumeration.hasMoreElements()) { + String name = enumeration.nextElement(); + + ret.put(name, filterConfig.getInitParameter(name)); + } + + //Resolve _HOST into bind address + String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS); + if (bindAddress == null) { + LOG.info("No host name configured. Defaulting to local host name."); + + try { + bindAddress = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + throw new ServletException("Unable to obtain host name", e); + } + } + + String principal = ret.getProperty(KerberosAuthenticationHandler.PRINCIPAL); + if (principal != null) { + try { + principal = SecurityUtil.getServerPrincipal(principal, bindAddress); + } catch (IOException ex) { + throw new RuntimeException("Could not resolve Kerberos principal name: " + ex.toString(), ex); + } + + ret.put(KerberosAuthenticationHandler.PRINCIPAL, principal); + } + + + LOG.debug(" AuthenticationFilterConfig: {}", ret); + sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); + LOG.info("AtlasAuthenticationFilter: {} = {}: {}", + AtlasConfiguration.SESSION_TIMEOUT_SECS.getPropertyName(), sessionTimeout, + (sessionTimeout == SESSION_TIMEOUT_DISABLED_VALUE) ? "Disabled" : "Enabled"); + + supportKeyTabBrowserLogin = configuration.getBoolean("atlas.authentication.method.kerberos.support.keytab.browser.login", false); + supportTrustedProxy = configuration.getBoolean("atlas.authentication.method.trustedproxy", false); + String agents = configuration.getString(AtlasCSRFPreventionFilter.BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT); + + if (agents == null) { + agents = AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT; + } + + String[] proxyUsers = configuration.getStringArray(CONFIG_PROXY_USERS); + + if (proxyUsers == null || proxyUsers.length == 0) { + proxyUsers = DEFAULT_PROXY_USERS; + } + + atlasProxyUsers = new HashSet<>(Arrays.asList(proxyUsers)); + + parseBrowserUserAgents(agents); + + LOG.info("<== AtlasAuthenticationFilter.getConfiguration(configPrefix={}, filterConfig={}): {}", configPrefix, filterConfig, ret); + + return ret; + } + + @Override + public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + + try { + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + HttpServletResponse httpResponse = (HttpServletResponse) response; + AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); + String action = httpRequest.getParameter("action"); + String doAsUser = request.getParameter("doAs"); + + HeadersUtil.setSecurityHeaders(responseWrapper); + + if (headerProperties != null) { + for (String headerKey : headerProperties.stringPropertyNames()) { + responseWrapper.setHeader(headerKey, headerProperties.getProperty(headerKey)); + } + } + + if (logoutHandler != null && supportTrustedProxy && StringUtils.isNotEmpty(doAsUser) && StringUtils.equals(action, RestUtil.TIMEOUT_ACTION)) { + if (existingAuth != null) { + logoutHandler.logout(httpRequest, httpResponse, existingAuth); + } + redirectTimeoutReqeust(httpRequest, httpResponse); + return; + } + + if (existingAuth == null) { + String authHeader = httpRequest.getHeader("Authorization"); + + if (authHeader != null && authHeader.startsWith("Basic")) { + filterChain.doFilter(request, response); + } else if (isKerberos) { + doKerberosAuth(request, response, filterChain); + } else { + filterChain.doFilter(request, response); + } + } else { + filterChain.doFilter(request, response); + } + } catch (NullPointerException e) { + LOG.error("Exception in AtlasAuthenticationFilter ", e); + //PseudoAuthenticationHandler.getUserName() from hadoop-auth throws NPE if user name is not specified + ((HttpServletResponse) response).sendError(Response.Status.BAD_REQUEST.getStatusCode(), + "Authentication is enabled and user is not specified. Specify user.name parameter"); + } + } + + private void redirectTimeoutReqeust(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException{ + String logoutUrl = httpRequest.getRequestURL().toString(); + + logoutUrl = StringUtils.replace(logoutUrl, httpRequest.getRequestURI(), RestUtil.LOGOUT_URL); + if (LOG.isDebugEnabled()) { + LOG.debug("logoutUrl value is " + logoutUrl); + } + String xForwardedURL = RestUtil.constructForwardableURL(httpRequest); + + + if (LOG.isDebugEnabled()) { + LOG.debug("xForwardedURL = " + xForwardedURL); + } + String redirectUrl = RestUtil.constructRedirectURL(httpRequest, logoutUrl, xForwardedURL, ORIGINAL_URL_QUERY_PARAM); + + if (LOG.isDebugEnabled()) { + LOG.debug("Redirect URL = " + redirectUrl); + LOG.debug("session id = " + httpRequest.getRequestedSessionId()); + } + + httpResponse.sendRedirect(redirectUrl); + } + + + /** + * This method is copied from hadoop auth lib, code added for error handling and fallback to other auth methods + * + * If the request has a valid authentication token it allows the request to continue to the target resource, + * otherwise it triggers an authentication sequence using the configured {@link AuthenticationHandler}. + * + * @param request the request object. + * @param response the response object. + * @param filterChain the filter chain object. + * + * @throws IOException thrown if an IO error occurred. + * @throws ServletException thrown if a processing error occurred. + */ + private void doKerberosAuth(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { + KerberosFilterChainWrapper filterChainWrapper = new KerberosFilterChainWrapper(request, response, filterChain); + boolean unauthorizedResponse = true; + int errCode = HttpServletResponse.SC_UNAUTHORIZED; + AuthenticationException authenticationEx = null; + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + boolean isHttps = "https".equals(httpRequest.getScheme()); + AuthenticationHandler authHandler = getAuthenticationHandler(); + String doAsUser = supportTrustedProxy ? Servlets.getDoAsUser(httpRequest) : null; + + try { + boolean newToken = false; + AuthenticationToken token; + + try { + token = getToken(httpRequest); + } catch (AuthenticationException ex) { + LOG.warn("AuthenticationToken ignored: {}", ex); + // will be sent back in a 401 unless filter authenticates + authenticationEx = ex; + token = null; + } + + if (authHandler.managementOperation(token, httpRequest, httpResponse)) { + if (token == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest)); + } + + token = authHandler.authenticate(httpRequest, httpResponse); + + if (token != null && token.getExpires() != 0 && token != AuthenticationToken.ANONYMOUS) { + token.setExpires(System.currentTimeMillis() + getValidity() * 1000); + } + + newToken = true; + } + + if (token != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName()); + } + + unauthorizedResponse = false; + + final AuthenticationToken authToken = token; + + httpRequest = new HttpServletRequestWrapper(httpRequest) { + @Override + public String getAuthType() { + return authToken.getType(); + } + + @Override + public String getRemoteUser() { + return authToken.getUserName(); + } + + @Override + public Principal getUserPrincipal() { + return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null; + } + }; + + // Create the proxy user if doAsUser exists + + if (supportTrustedProxy && doAsUser != null && !doAsUser.equals(httpRequest.getRemoteUser())) { + LOG.debug("doAsUser is {}", doAsUser); + + UserGroupInformation requestUgi = (token != null) ? UserGroupInformation.createRemoteUser(token.getUserName()) : null; + + if (requestUgi != null) { + requestUgi = UserGroupInformation.createProxyUser(doAsUser, requestUgi); + + try { + ProxyUsers.authorize(requestUgi, request.getRemoteAddr()); + request.setAttribute("proxyUser", doAsUser); + } catch (AuthorizationException ex) { + LOG.warn("Proxy user AuthorizationException", ex); + + httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); + filterChain.doFilter(request, response); + + return; + + } + } + } else if(StringUtils.isNotBlank(httpRequest.getRemoteUser()) && atlasProxyUsers.contains(httpRequest.getRemoteUser())){ + LOG.info("Ignoring kerberos login from proxy user "+ httpRequest.getRemoteUser()); + + httpResponse.setHeader(KerberosAuthenticator.WWW_AUTHENTICATE, ""); + httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + filterChain.doFilter(request, response); + + return; + } + + if (newToken && !token.isExpired() && token != AuthenticationToken.ANONYMOUS) { + String signedToken = signer.sign(token.toString()); + + createAtlasAuthCookie(httpResponse, signedToken, getCookieDomain(), getCookiePath(), token.getExpires(), isHttps); + } + + filterChainWrapper.doFilter(httpRequest, httpResponse); + } + } else { + unauthorizedResponse = false; + } + } catch (AuthenticationException ex) { + LOG.warn("Authentication exception: {}", ex.getMessage(), ex); + + // exception from the filter itself is fatal + errCode = HttpServletResponse.SC_FORBIDDEN; + authenticationEx = ex; + } + + if (unauthorizedResponse) { + if (!httpResponse.isCommitted()) { + createAtlasAuthCookie(httpResponse, "", getCookieDomain(), getCookiePath(), 0, isHttps); + + // If response code is 401. Then WWW-Authenticate Header should be + // present.. reset to 403 if not found.. + if (errCode == HttpServletResponse.SC_UNAUTHORIZED && !httpResponse.containsHeader(KerberosAuthenticator.WWW_AUTHENTICATE)) { + errCode = HttpServletResponse.SC_FORBIDDEN; + } + + boolean isKerberosOnBrowser = supportKeyTabBrowserLogin || doAsUser != null; + + if (authenticationEx == null) { // added this code for atlas error handling and fallback + if (!isKerberosOnBrowser && isBrowser(httpRequest.getHeader("User-Agent"))) { + filterChain.doFilter(request, response); + } else { + boolean chk = true; + Collection headerNames = httpResponse.getHeaderNames(); + + for (String headerName : headerNames) { + String value = httpResponse.getHeader(headerName); + + if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("ATLASSESSIONID")) { + chk = false; + break; + } + } + + String authHeader = httpRequest.getHeader("Authorization"); + + if (authHeader == null && chk) { + filterChain.doFilter(request, response); + } else if (authHeader != null && authHeader.startsWith("Basic")) { + filterChain.doFilter(request, response); + } + } + } else { + httpResponse.sendError(errCode, authenticationEx.getMessage()); + } + } + } + } + + + @Override + public void destroy() { + + if ((this.secretProvider != null) && (this.isInitializedByTomcat)) { + this.secretProvider.destroy(); + this.secretProvider = null; + } + optionsServlet.destroy(); + super.destroy(); + } + + + private static String readUserFromCookie(HttpServletResponse response1) { + String userName = null; + boolean isCookieSet = response1.containsHeader("Set-Cookie"); + + if (isCookieSet) { + Collection authUserName = response1.getHeaders("Set-Cookie"); + + if (authUserName != null) { + for (String cookie : authUserName) { + if (!StringUtils.isEmpty(cookie)) { + if (cookie.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase()) && cookie.contains("u=")) { + String[] split = cookie.split(";"); + + if (split != null) { + for (String s : split) { + if (!StringUtils.isEmpty(s) && s.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase())) { + int ustr = s.indexOf("u="); + + if (ustr != -1) { + int andStr = s.indexOf("&", ustr); + + if (andStr != -1) { + try { + userName = s.substring(ustr + 2, andStr); + break; + } catch (Exception e) { + userName = null; + } + } + } + } + } + } + } + } + } + } + } + + return userName; + } + + private void createAtlasAuthCookie(HttpServletResponse resp, String token, String domain, String path, long expires, boolean isSecure) { + StringBuilder sb = (new StringBuilder(AuthenticatedURL.AUTH_COOKIE)).append("="); + + if (token != null && token.length() > 0) { + sb.append("\"").append(token).append("\""); + } + + sb.append("; Version=1"); + + if (path != null) { + sb.append("; Path=").append(path); + } + + if (domain != null) { + sb.append("; Domain=").append(domain); + } + + if (expires >= 0L) { + SimpleDateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + sb.append("; Expires=").append(df.format(new Date(expires))); + } + + if (isSecure) { + sb.append("; Secure"); + } + + sb.append("; HttpOnly"); + resp.addHeader("Set-Cookie", sb.toString()); + } + + @Override + protected AuthenticationToken getToken(HttpServletRequest request) + throws IOException, AuthenticationException { + AuthenticationToken token = null; + String tokenStr = null; + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals(AuthenticatedURL.AUTH_COOKIE)) { + tokenStr = cookie.getValue(); + try { + tokenStr = this.signer.verifyAndExtract(tokenStr); + } catch (SignerException ex) { + throw new AuthenticationException(ex); + } + } + } + } + + if (tokenStr != null) { + token = AuthenticationToken.parse(tokenStr); + if (token != null) { + AuthenticationHandler authHandler = getAuthenticationHandler(); + if (!token.getType().equals(authHandler.getType())) { + throw new AuthenticationException("Invalid AuthenticationToken type"); + } + if (token.isExpired()) { + throw new AuthenticationException("AuthenticationToken expired"); + } + } + } + return token; + } + + void parseBrowserUserAgents(String userAgents) { + String[] agentsArray = userAgents.split(","); + browserUserAgents = new HashSet<>(); + for (String patternString : agentsArray) { + browserUserAgents.add(Pattern.compile(patternString)); + } + } + + boolean isBrowser(String userAgent) { + if (userAgent != null) { + for (Pattern pattern : browserUserAgents) { + Matcher matcher = pattern.matcher(userAgent); + + if (matcher.matches()) { + return true; + } + } + } + + return false; + } + + + private class KerberosFilterChainWrapper implements FilterChain { + private final ServletRequest request; + private final ServletResponse response; + private final FilterChain filterChain; + + KerberosFilterChainWrapper(ServletRequest request, ServletResponse response, FilterChain filterChain) { + this.request = request; + this.response = response; + this.filterChain = filterChain; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; + final Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + String loggedInUser = readUserFromCookie(httpResponse); + String userName = loggedInUser; + + if (!StringUtils.isEmpty((String) httpRequest.getAttribute("proxyUser"))) { + userName = (String) httpRequest.getAttribute("proxyUser"); + } else if (StringUtils.isEmpty(userName) && !StringUtils.isEmpty(httpRequest.getRemoteUser())) { + userName = httpRequest.getRemoteUser(); + } + + if ((existingAuth == null || !existingAuth.isAuthenticated()) && !StringUtils.isEmpty(userName)) { + final List grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + final UserDetails principal = new User(userName, "", grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); + final WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); + + ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); + + SecurityContextHolder.getContext().setAuthentication(finalAuthentication); + if (sessionTimeout != SESSION_TIMEOUT_DISABLED_VALUE) { + httpRequest.getSession().setMaxInactiveInterval(sessionTimeout); + } + + request.setAttribute("atlas.http.authentication.type", true); + + if (!StringUtils.equals(loggedInUser, userName)) { + LOG.info("Logged into Atlas as = {}, by proxyUser = {}", userName, loggedInUser); + } else { + LOG.info("Logged into Atlas as = {}", userName); + } + } + + // OPTIONS method is sent from quick start jersey atlas client + if (httpRequest.getMethod().equals("OPTIONS")) { + optionsServlet.service(request, response); + } else { + try { + String requestUser = httpRequest.getRemoteUser(); + String requestTrace = requestUser + ":" + httpRequest.getMethod() + httpRequest.getRequestURI(); + MDC.put("request", requestTrace); + + LOG.info("Request from authenticated user: {}, URL={}", requestUser, Servlets.getRequestURI(httpRequest)); + + filterChain.doFilter(servletRequest, servletResponse); + } finally { + MDC.remove("request"); + } + } + } + } + + private org.apache.hadoop.conf.Configuration getProxyuserConfiguration() { + org.apache.hadoop.conf.Configuration ret = new org.apache.hadoop.conf.Configuration(false); + + if(configuration!=null) { + Properties props = ConfigurationConverter.getProperties(configuration.subset(CONF_PROXYUSER_PREFIX)); + + for (String key : props.stringPropertyNames()) { + ret.set(CONF_PROXYUSER_PREFIX + "." + key, props.getProperty(key)); + } + } + + return ret; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java new file mode 100644 index 0000000000..0252638747 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java @@ -0,0 +1,267 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +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.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import java.io.IOException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Component +public class AtlasCSRFPreventionFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasCSRFPreventionFilter.class); + private static Configuration configuration; + + static { + try { + configuration = ApplicationProperties.get(); + LOG.info("Configuration obtained :: "+configuration); + } catch (AtlasException e) { + LOG.error(e.getMessage(), e); + } + } + + public static final boolean isCSRF_ENABLED = configuration.getBoolean("atlas.rest-csrf.enabled", true); + public static final String BROWSER_USER_AGENT_PARAM = "atlas.rest-csrf.browser-useragents-regex"; + public static final String BROWSER_USER_AGENTS_DEFAULT = "^Mozilla.*,^Opera.*,^Chrome"; + public static final String CUSTOM_METHODS_TO_IGNORE_PARAM = "atlas.rest-csrf.methods-to-ignore"; + public static final String METHODS_TO_IGNORE_DEFAULT = "GET,OPTIONS,HEAD,TRACE"; + public static final String CUSTOM_HEADER_PARAM = "atlas.rest-csrf.custom-header"; + public static final String HEADER_DEFAULT = "X-XSRF-HEADER"; + public static final String HEADER_USER_AGENT = "User-Agent"; + public static final String CSRF_TOKEN = "_csrfToken"; + + + private String headerName = HEADER_DEFAULT; + private Set methodsToIgnore = null; + private Set browserUserAgents; + + public AtlasCSRFPreventionFilter() { + try { + if (isCSRF_ENABLED){ + init(null); + } + } catch (Exception e) { + LOG.error("Error while initializing Filter ", e); + } + } + + public void init(FilterConfig filterConfig) throws ServletException { + String customHeader = configuration.getString(CUSTOM_HEADER_PARAM, HEADER_DEFAULT); + if (customHeader != null) { + headerName = customHeader; + } + + String customMethodsToIgnore = configuration.getString(CUSTOM_METHODS_TO_IGNORE_PARAM, METHODS_TO_IGNORE_DEFAULT); + if (customMethodsToIgnore != null) { + parseMethodsToIgnore(customMethodsToIgnore); + } else { + parseMethodsToIgnore(METHODS_TO_IGNORE_DEFAULT); + } + String agents = configuration.getString(BROWSER_USER_AGENT_PARAM, BROWSER_USER_AGENTS_DEFAULT); + if (agents == null) { + agents = BROWSER_USER_AGENTS_DEFAULT; + } + parseBrowserUserAgents(agents); + LOG.info("Adding cross-site request forgery (CSRF) protection"); + } + + void parseMethodsToIgnore(String mti) { + String[] methods = mti.split(","); + methodsToIgnore = new HashSet<>(); + Collections.addAll(methodsToIgnore, methods); + } + + void parseBrowserUserAgents(String userAgents) { + String[] agentsArray = userAgents.split(","); + browserUserAgents = new HashSet<>(); + for (String patternString : agentsArray) { + browserUserAgents.add(Pattern.compile(patternString)); + } + } + + protected boolean isBrowser(String userAgent) { + if (userAgent == null) { + return false; + } + if (browserUserAgents != null){ + for (Pattern pattern : browserUserAgents) { + Matcher matcher = pattern.matcher(userAgent); + if (matcher.matches()) { + return true; + } + } + } + return false; + } + + public interface HttpInteraction { + /** + * Returns the value of a header. + * + * @param header + * name of header + * @return value of header + */ + String getHeader(String header); + + /** + * Returns the method. + * + * @return method + */ + String getMethod(); + + /** + * Called by the filter after it decides that the request may proceed. + * + * @throws IOException + * if there is an I/O error + * @throws ServletException + * if the implementation relies on the servlet API and a + * servlet API call has failed + */ + void proceed() throws IOException, ServletException; + + /** + * Called by the filter after it decides that the request is a potential + * CSRF attack and therefore must be rejected. + * + * @param code + * status code to send + * @param message + * response message + * @throws IOException + * if there is an I/O error + */ + void sendError(int code, String message) throws IOException; + } + + public void handleHttpInteraction(HttpInteraction httpInteraction) throws IOException, ServletException { + HttpSession session = ((ServletFilterHttpInteraction) httpInteraction).getSession(); + String csrfToken = StringUtils.EMPTY; + + if (session != null) { + csrfToken = (String) session.getAttribute(CSRF_TOKEN); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Session is null"); + } + } + + String clientCsrfToken = httpInteraction.getHeader(headerName); + + if (!isBrowser(httpInteraction.getHeader(HEADER_USER_AGENT)) || methodsToIgnore.contains(httpInteraction.getMethod()) + || (clientCsrfToken != null && clientCsrfToken.equals(csrfToken))) { + httpInteraction.proceed(); + } else { + httpInteraction.sendError(HttpServletResponse.SC_BAD_REQUEST,"Missing header or invalid Header value for CSRF Vulnerability Protection"); + } + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final HttpServletResponse httpResponse = (HttpServletResponse) response; + AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); + HeadersUtil.setSecurityHeaders(responseWrapper); + + if (isCSRF_ENABLED){ + handleHttpInteraction(new ServletFilterHttpInteraction(httpRequest, httpResponse, chain)); + }else{ + chain.doFilter(request, response); + } + } + + public void destroy() { + } + + private static final class ServletFilterHttpInteraction implements + HttpInteraction { + + private final FilterChain chain; + private final HttpServletRequest httpRequest; + private final HttpServletResponse httpResponse; + + /** + * Creates a new ServletFilterHttpInteraction. + * + * @param httpRequest + * request to process + * @param httpResponse + * response to process + * @param chain + * filter chain to forward to if HTTP interaction is allowed + */ + public ServletFilterHttpInteraction(HttpServletRequest httpRequest, + HttpServletResponse httpResponse, FilterChain chain) { + this.httpRequest = httpRequest; + this.httpResponse = httpResponse; + this.chain = chain; + } + + @Override + public String getHeader(String header) { + return httpRequest.getHeader(header); + } + + @Override + public String getMethod() { + return httpRequest.getMethod(); + } + + @Override + public void proceed() throws IOException, ServletException { + chain.doFilter(httpRequest, httpResponse); + } + + public HttpSession getSession() { + return httpRequest.getSession(); + } + + @Override + public void sendError(int code, String message) throws IOException { + JSONObject json = new JSONObject(); + json.put("msgDesc", message); + httpResponse.setContentType("application/json"); + httpResponse.setStatus(code); + httpResponse.setCharacterEncoding("UTF-8"); + httpResponse.getWriter().write(json.toJSONString()); + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java new file mode 100644 index 0000000000..0b4950e709 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.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.atlas.notification.rest.web.filters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.LinkedHashMap; + +public class AtlasDelegatingAuthenticationEntryPoint extends DelegatingAuthenticationEntryPoint { + + public static final String SESSION_TIMEOUT = "Session Timeout"; + private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); + + public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { + super(entryPoints); + if (LOG.isDebugEnabled()) { + LOG.info("AtlasDelegatingAuthenticationEntryPoint-AjaxAwareAuthenticationEntryPoint(): constructor"); + } + } + + public void commence(HttpServletRequest request, HttpServletResponse response, + AuthenticationException authException) throws IOException { + + String ajaxRequestHeader = request.getHeader(HeadersUtil.X_REQUESTED_WITH_KEY); + response.setHeader(HeadersUtil.X_FRAME_OPTIONS_KEY, HeadersUtil.X_FRAME_OPTIONS_VAL); + + if (ajaxRequestHeader != null + && HeadersUtil.X_REQUESTED_WITH_VALUE.equalsIgnoreCase(ajaxRequestHeader)) { + if (LOG.isDebugEnabled()) { + LOG.debug("commence() AJAX request. Authentication required. Returning " + + HttpServletResponse.SC_UNAUTHORIZED + ". URL=" + request.getRequestURI()); + } + response.sendError(HeadersUtil.SC_AUTHENTICATION_TIMEOUT, SESSION_TIMEOUT); + } else { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, + authException.getMessage()); + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasHeaderFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasHeaderFilter.java new file mode 100644 index 0000000000..75485e4f49 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasHeaderFilter.java @@ -0,0 +1,54 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +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.HttpServletResponse; +import java.io.IOException; + +public class AtlasHeaderFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasHeaderFilter.class); + + @Override + public void init(FilterConfig filterConfig) { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) + throws IOException, ServletException { + setHeaders((HttpServletResponse) response); + filterChain.doFilter(request, response); + } + + public void setHeaders(HttpServletResponse httpResponse) { + AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); + HeadersUtil.setSecurityHeaders(responseWrapper); + } + + @Override + public void destroy() { + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java new file mode 100644 index 0000000000..5980880d70 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -0,0 +1,594 @@ + +/* + * 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.atlas.notification.rest.web.filters; + +import com.google.common.annotations.VisibleForTesting; +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; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationProvider; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.apache.http.client.utils.URIBuilder; +import org.json.simple.JSONObject; +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 org.springframework.stereotype.Component; + +import javax.inject.Inject; +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 java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.net.URLEncoder; +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.Date; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +@Component("ssoAuthenticationFilter") +public class AtlasKnoxSSOAuthenticationFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); + + public static final String BROWSER_USERAGENT = "atlas.sso.knox.browser.useragent"; + public static final String JWT_AUTH_PROVIDER_URL = "atlas.sso.knox.providerurl"; + public static final String JWT_PUBLIC_KEY = "atlas.sso.knox.publicKey"; + public static final String JWT_COOKIE_NAME = "atlas.sso.knox.cookiename"; + public static final String JWT_ORIGINAL_URL_QUERY_PARAM = "atlas.sso.knox.query.param.originalurl"; + public static final String JWT_COOKIE_NAME_DEFAULT = "hadoop-jwt"; + public static final String JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT = "originalUrl"; + public static final String DEFAULT_BROWSER_USERAGENT = "Mozilla,Opera,Chrome"; + public static final String PROXY_ATLAS_URL_PATH = "/atlas"; + + private final AtlasAuthenticationProvider authenticationProvider; + + private SSOAuthenticationProperties jwtProperties; + + private String originalUrlQueryParam = "originalUrl"; + private String authenticationProviderUrl = null; + private RSAPublicKey publicKey = null; + private String cookieName = "hadoop-jwt"; + private Configuration configuration = null; + private boolean ssoEnabled = false; + private JWSVerifier verifier = null; + @VisibleForTesting + private final int MAX_LOGIN_URL_LENGTH = 2043; + + @Inject + public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider) { + this.authenticationProvider = authenticationProvider; + try { + configuration = ApplicationProperties.get(); + } catch (Exception e) { + LOG.error("Error while getting application properties", e); + } + if (configuration != null) { + ssoEnabled = configuration.getBoolean("atlas.sso.knox.enabled", false); + jwtProperties = loadJwtProperties(); + } + setJwtProperties(); + } + + public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider, + SSOAuthenticationProperties jwtProperties) { + this.authenticationProvider = authenticationProvider; + this.jwtProperties = jwtProperties; + setJwtProperties(); + } + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + /* + * doFilter of AtlasKnoxSSOAuthenticationFilter is the first in the filter list so in this it check for the request + * if the request is from browser and sso is enabled then it process the request against knox sso + * else if it's ssoenable and the request is with local login string then it show's the appropriate msg + * else if ssoenable is false then it contiunes with further filters as it was before sso + */ + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + + HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; + + AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); + HeadersUtil.setSecurityHeaders(responseWrapper); + + if (!ssoEnabled) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; + if (LOG.isDebugEnabled()) { + LOG.debug("Knox doFilter {}", httpRequest.getRequestURI()); + } + + if (httpRequest.getSession() != null && httpRequest.getSession().getAttribute("locallogin") != null) { + servletRequest.setAttribute("ssoEnabled", false); + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + if (jwtProperties == null || isAuthenticated()) { + filterChain.doFilter(servletRequest, servletResponse); + return; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Knox ssoEnabled {} {}", ssoEnabled, httpRequest.getRequestURI()); + } + //if jwt properties are loaded and is current not authenticated then it will go for sso authentication + //Note : Need to remove !isAuthenticated() after knoxsso solve the bug from cross-origin script + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + String serializedJWT = getJWTFromCookie(httpRequest); + // if we get the hadoop-jwt token from the cookies then will process it further + 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 = AtlasAuthenticationProvider.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); + authenticationProvider.setSsoEnabled(ssoEnabled); + Authentication authentication = authenticationProvider.authenticate(finalAuthentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + + filterChain.doFilter(servletRequest, httpServletResponse); + } else { // if the token is not valid then redirect to knox sso + redirectToKnox(httpRequest, httpServletResponse, filterChain); + } + } catch (ParseException e) { + LOG.warn("Unable to parse the JWT token", e); + redirectToKnox(httpRequest, httpServletResponse, filterChain); + } + } else { + redirectToKnox(httpRequest, httpServletResponse, filterChain); + } + + } + + private void redirectToKnox(HttpServletRequest httpRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException { + + if (!isWebUserAgent(httpRequest.getHeader("User-Agent"))) { + filterChain.doFilter(httpRequest, httpServletResponse); + return; + } + + String ajaxRequestHeader = httpRequest.getHeader("X-Requested-With"); + + if ("XMLHttpRequest".equals(ajaxRequestHeader)) { + String ssourl = constructLoginURL(httpRequest, true); + JSONObject json = new JSONObject(); + json.put("knoxssoredirectURL", URLEncoder.encode(ssourl, "UTF-8")); + httpServletResponse.setContentType("application/json"); + httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, json.toString()); + + } else { + String ssourl = constructLoginURL(httpRequest, false); + httpServletResponse.sendRedirect(ssourl); + } + + } + + private boolean isWebUserAgent(String userAgent) { + boolean isWeb = false; + if (jwtProperties != null) { + String userAgentList[] = jwtProperties.getUserAgentList(); + if (userAgentList != null && userAgentList.length > 0) { + for (String ua : userAgentList) { + if (StringUtils.startsWithIgnoreCase(userAgent, ua)) { + isWeb = true; + break; + } + } + } + } + return isWeb; + } + + + private void setJwtProperties() { + if (jwtProperties != null) { + authenticationProviderUrl = jwtProperties.getAuthenticationProviderUrl(); + publicKey = jwtProperties.getPublicKey(); + cookieName = jwtProperties.getCookieName(); + originalUrlQueryParam = jwtProperties.getOriginalUrlQueryParam(); + if (publicKey != null) { + verifier = new RSASSAVerifier(publicKey); + } + } + } + + /** + * Do not try to validate JWT if user already authenticated via other + * provider + * + * @return true, if JWT validation required + */ + private boolean isAuthenticated() { + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + return !(!(existingAuth != null && existingAuth.isAuthenticated()) || existingAuth instanceof SSOAuthentication); + } + + /** + * Encapsulate the acquisition of the JWT token from HTTP cookies within the + * request. + * + * @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 (cookieName != null && cookies != null) { + for (Cookie cookie : cookies) { + if (cookieName.equals(cookie.getName())) { + if (LOG.isDebugEnabled()) { + LOG.debug("{} cookie has been found and is being processed", cookieName); + } + serializedJWT = cookie.getValue(); + break; + } + } + } + return serializedJWT; + } + + /** + * 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, boolean isXMLRequest) { + String delimiter = "?"; + if (authenticationProviderUrl.contains("?")) { + delimiter = "&"; + } + + String xForwardedURL = constructForwardableURL(parseXForwardHeader(request), request.getRequestURI()); + + StringBuilder knoxLoginURL = new StringBuilder(); + knoxLoginURL.append(authenticationProviderUrl) + .append(delimiter) + .append(originalUrlQueryParam).append("="); + + if (isXMLRequest) { + String atlasApplicationURL = ""; + String referalURL = request.getHeader("referer"); + + if (referalURL == null) { + atlasApplicationURL = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); + } else { + atlasApplicationURL = referalURL; + } + + if (StringUtils.trimToNull(xForwardedURL) != null) { + safeAppend(knoxLoginURL, xForwardedURL, atlasApplicationURL); + } else { + safeAppend(knoxLoginURL, atlasApplicationURL); + } + } else { + if (StringUtils.trimToNull(xForwardedURL) != null) { + safeAppend(knoxLoginURL, xForwardedURL, getOriginalQueryString(request)); + } else { + safeAppend(knoxLoginURL, request.getRequestURL().toString(), getOriginalQueryString(request)); + } + } + return knoxLoginURL.toString(); + } + + private String getOriginalQueryString(HttpServletRequest request) { + String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; + } + + + private Map parseXForwardHeader(HttpServletRequest httpRequest) { + String xForwardedProto = ""; + String xForwardedHost = ""; + String xForwardedContext = ""; + Map xFwdHeaderMap = null; + Enumeration names = httpRequest.getHeaderNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + Enumeration values = httpRequest.getHeaders(name); + String value = ""; + if (values != null) { + while (values.hasMoreElements()) { + value = (String) values.nextElement(); + } + } + if (StringUtils.trimToNull(name) != null + && StringUtils.trimToNull(value) != null) { + if (name.equalsIgnoreCase("x-forwarded-proto")) { + xForwardedProto = value; + } else if (name.equalsIgnoreCase("x-forwarded-host")) { + xForwardedHost = value; + } else if (name.equalsIgnoreCase("x-forwarded-context")) { + xForwardedContext = value; + } + } + } + + if (StringUtils.isNotEmpty(xForwardedProto) && StringUtils.isNotEmpty(xForwardedHost) + && StringUtils.isNotEmpty(xForwardedContext)) { + xFwdHeaderMap = new HashMap(); + xFwdHeaderMap.put("x-forwarded-proto", xForwardedProto); + xFwdHeaderMap.put("x-forwarded-host", xForwardedHost); + xFwdHeaderMap.put("x-forwarded-context", xForwardedContext); + } + + return xFwdHeaderMap; + } + + + private String constructForwardableURL(Map xFwdHeaderMap, String requestURI) { + + if (LOG.isDebugEnabled()) { + LOG.debug(" constructForwardableURL ==>>" + xFwdHeaderMap + " requestURI " + requestURI); + } + + String xForwardedURL = null; + + if (xFwdHeaderMap != null) { + String xForwardedProto = xFwdHeaderMap.get("x-forwarded-proto"); + String xForwardedHost = xFwdHeaderMap.get("x-forwarded-host"); + String xForwardedContext = xFwdHeaderMap.get("x-forwarded-context"); + + if (StringUtils.isNotBlank(xForwardedProto) + && StringUtils.isNotBlank(xForwardedHost) + && StringUtils.isNotBlank(xForwardedContext)) { + try { + if (LOG.isDebugEnabled()) { + LOG.debug(" Atlas url with proxy path ==>" + xForwardedProto + "://" + + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + requestURI); + } + + URIBuilder builder = new URIBuilder(); + builder.setScheme(xForwardedProto) + .setHost(xForwardedHost) + .setPath(xForwardedContext + PROXY_ATLAS_URL_PATH + requestURI); + + xForwardedURL = builder.build().toString(); + } catch (URISyntaxException ue) { + LOG.error(" URISyntaxException while build xforward url ", ue); + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug(" xForwardedURL ==>> " + xForwardedURL); + } + + return xForwardedURL; + } + + + @VisibleForTesting + void safeAppend(StringBuilder sb, String... strings) { + for (String s : strings) { + if ((sb.length() + s.length()) < MAX_LOGIN_URL_LENGTH) { + sb.append(s); + } + } + } + + /** + * This method provides a single method for validating the JWT for use in + * request processing. It provides for the override of specific aspects of + * this implementation through submethods used within but also allows for + * the override of the entire token validation algorithm. + * + * @param jwtToken the token to validate + * @return true if valid + */ + protected boolean validateToken(SignedJWT jwtToken) { + boolean isValid = validateSignature(jwtToken); + + if (isValid) { + isValid = validateExpiration(jwtToken); + if (!isValid) { + LOG.warn("Expiration time validation of JWT token failed."); + } + } else { + LOG.warn("Signature of JWT token could not be verified. Please check the public key"); + } + return isValid; + } + + /** + * 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 validateSignature(SignedJWT jwtToken) { + boolean valid = false; + if (JWSObject.State.SIGNED == jwtToken.getState()) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token is in a SIGNED state"); + } + if (jwtToken.getSignature() != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token signature is not null"); + } + try { + if (verifier != null && jwtToken.verify(verifier)) { + valid = true; + if (LOG.isDebugEnabled()) { + 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); + } + } + } + return valid; + } + + /** + * Validate that the expiration time of the JWT token has not been violated. + * If it has then throw an AuthenticationException. Override this method in + * subclasses in order to customize the expiration validation behavior. + * + * @param jwtToken the token that contains the expiration date to validate + * @return valid true if the token has not expired; false otherwise + */ + protected boolean validateExpiration(SignedJWT jwtToken) { + boolean valid = false; + try { + Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); + if (expires == null || new Date().before(expires)) { + if (LOG.isDebugEnabled()) { + LOG.debug("SSO token expiration date has been successfully validated"); + } + valid = true; + } else { + LOG.warn("SSO expiration date validation failed."); + } + } catch (ParseException pe) { + LOG.warn("SSO expiration date validation failed.", pe); + } + return valid; + } + + @Override + public void destroy() { + } + + public SSOAuthenticationProperties loadJwtProperties() { + String providerUrl = configuration.getString(JWT_AUTH_PROVIDER_URL); + if (providerUrl != null && configuration.getBoolean("atlas.sso.knox.enabled", false)) { + SSOAuthenticationProperties jwtProperties = new SSOAuthenticationProperties(); + String publicKeyPathStr = configuration.getString(JWT_PUBLIC_KEY); + if (publicKeyPathStr == null) { + LOG.error("Public key pem not specified for SSO auth provider {}. SSO auth will be disabled"); + return null; + } + jwtProperties.setAuthenticationProviderUrl(providerUrl); + jwtProperties.setCookieName(configuration.getString(JWT_COOKIE_NAME, JWT_COOKIE_NAME_DEFAULT)); + jwtProperties.setOriginalUrlQueryParam(configuration.getString(JWT_ORIGINAL_URL_QUERY_PARAM, JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT)); + String[] userAgent = configuration.getStringArray(BROWSER_USERAGENT); + if (userAgent != null && userAgent.length > 0) { + jwtProperties.setUserAgentList(userAgent); + } else { + jwtProperties.setUserAgentList(DEFAULT_BROWSER_USERAGENT.split(",")); + } + try { + RSAPublicKey publicKey = parseRSAPublicKey(publicKeyPathStr); + jwtProperties.setPublicKey(publicKey); + } catch (IOException e) { + LOG.error("Unable to read public certificate file. JWT auth will be disabled.", e); + } catch (CertificateException e) { + LOG.error("Unable to parse public certificate file. JWT auth will be disabled.", e); + } catch (ServletException e) { + LOG.error("ServletException while processing the properties", e); + } + return jwtProperties; + } else { + return null; + } + } + + /* + * public static RSAPublicKey getPublicKeyFromFile(String filePath) throws + * IOException, CertificateException { + * FileUtils.readFileToString(new File(filePath)); + * getPublicKeyFromString(pemString); } + */ + + 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_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/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java new file mode 100644 index 0000000000..8e470b0d67 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.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.atlas.notification.rest.web.filters; + +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; + + +public class AtlasResponseRequestWrapper extends HttpServletResponseWrapper { + public AtlasResponseRequestWrapper(HttpServletResponse response) { + super(response); + } +} + + + + + diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java new file mode 100755 index 0000000000..15ad94c0a7 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java @@ -0,0 +1,206 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.apache.atlas.AtlasClient; +import org.apache.atlas.AtlasException; +import org.apache.atlas.DeleteType; +import org.apache.atlas.RequestContext; +import org.apache.atlas.authorize.AtlasAuthorizationUtils; +import org.apache.atlas.notification.rest.AtlasRepositoryConfiguration; +import org.apache.atlas.notification.rest.DateTimeHelper; +import org.apache.atlas.notification.rest.Servlets; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +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.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Date; +import java.util.Set; +import java.util.UUID; + +import static org.apache.atlas.AtlasConfiguration.REST_API_CREATE_SHELL_ENTITY_FOR_NON_EXISTING_REF; +import static org.apache.atlas.AtlasConfiguration.REST_API_ENABLE_DELETE_TYPE_OVERRIDE; + +/** + * This records audit information as part of the filter after processing the request + * and also introduces a UUID into request and response for tracing requests in logs. + */ +@Component +public class AuditFilter implements Filter { + private static final Logger LOG = LoggerFactory.getLogger(AuditFilter.class); + private static final Logger AUDIT_LOG = LoggerFactory.getLogger("AUDIT"); + + private boolean deleteTypeOverrideEnabled = false; + private boolean createShellEntityForNonExistingReference = false; + + @Override + public void init(FilterConfig filterConfig) throws ServletException { + LOG.info("AuditFilter initialization started"); + + deleteTypeOverrideEnabled = REST_API_ENABLE_DELETE_TYPE_OVERRIDE.getBoolean(); + createShellEntityForNonExistingReference = REST_API_CREATE_SHELL_ENTITY_FOR_NON_EXISTING_REF.getBoolean(); + + LOG.info("REST_API_ENABLE_DELETE_TYPE_OVERRIDE={}", deleteTypeOverrideEnabled); + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) + throws IOException, ServletException { + final long startTime = System.currentTimeMillis(); + final Date requestTime = new Date(); + final HttpServletRequest httpRequest = (HttpServletRequest) request; + final HttpServletResponse httpResponse = (HttpServletResponse) response; + final String requestId = UUID.randomUUID().toString(); + final Thread currentThread = Thread.currentThread(); + final String oldName = currentThread.getName(); + final String user = AtlasAuthorizationUtils.getCurrentUserName(); + final Set userGroups = AtlasAuthorizationUtils.getCurrentUserGroups(); + final String deleteType = httpRequest.getParameter("deleteType"); + final boolean skipFailedEntities = Boolean.parseBoolean(httpRequest.getParameter("skipFailedEntities")); + + try { + currentThread.setName(formatName(oldName, requestId)); + + RequestContext.clear(); + RequestContext requestContext = RequestContext.get(); + requestContext.setUser(user, userGroups); + requestContext.setClientIPAddress(AtlasAuthorizationUtils.getRequestIpAddress(httpRequest)); + requestContext.setCreateShellEntityForNonExistingReference(createShellEntityForNonExistingReference); + requestContext.setForwardedAddresses(AtlasAuthorizationUtils.getForwardedAddressesFromRequest(httpRequest)); + requestContext.setSkipFailedEntities(skipFailedEntities); + + if (StringUtils.isNotEmpty(deleteType)) { + if (deleteTypeOverrideEnabled) { + requestContext.setDeleteType(DeleteType.from(deleteType)); + } else { + LOG.warn("Override of deleteType is not enabled. Ignoring parameter deleteType={}, in request from user={}", deleteType, user); + } + } + + filterChain.doFilter(request, response); + } finally { + long timeTaken = System.currentTimeMillis() - startTime; + + recordAudit(httpRequest, requestTime, user, httpResponse.getStatus(), timeTaken); + + // put the request id into the response so users can trace logs for this request + httpResponse.setHeader(AtlasClient.REQUEST_ID, requestId); + currentThread.setName(oldName); + RequestContext.clear(); + } + } + + private String formatName(String oldName, String requestId) { + return oldName + " - " + requestId; + } + + private void recordAudit(HttpServletRequest httpRequest, Date when, String who, int httpStatus, long timeTaken) { + final String fromAddress = httpRequest.getRemoteAddr(); + final String whatRequest = httpRequest.getMethod(); + final String whatURL = Servlets.getRequestURL(httpRequest); + final String whatUrlPath = httpRequest.getRequestURL().toString(); //url path without query string + + if (!isOperationExcludedFromAudit(whatRequest, whatUrlPath.toLowerCase(), null)) { + audit(new AuditLog(who, fromAddress, whatRequest, whatURL, when, httpStatus, timeTaken)); + } else { + if(LOG.isDebugEnabled()) { + LOG.debug(" Skipping Audit for {} ", whatURL); + } + } + } + + public static void audit(AuditLog auditLog) { + if (AUDIT_LOG.isInfoEnabled() && auditLog != null) { + AUDIT_LOG.info(auditLog.toString()); + } + } + + boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { + try { + return AtlasRepositoryConfiguration.isExcludedFromAudit(config, requestHttpMethod, requestOperation); + } catch (AtlasException e) { + return false; + } + } + + @Override + public void destroy() { + // do nothing + } + + public static class AuditLog { + private static final char FIELD_SEP = '|'; + + private final String userName; + private final String fromAddress; + private final String requestMethod; + private final String requestUrl; + private final Date requestTime; + private int httpStatus; + private long timeTaken; + + public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl) { + this(userName, fromAddress, requestMethod, requestUrl, new Date()); + } + + public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl, Date requestTime) { + this(userName, fromAddress, requestMethod, requestUrl, requestTime, HttpServletResponse.SC_OK, 0); + } + + public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl, Date requestTime, int httpStatus, long timeTaken) { + this.userName = userName; + this.fromAddress = fromAddress; + this.requestMethod = requestMethod; + this.requestUrl = requestUrl; + this.requestTime = requestTime; + this.httpStatus = httpStatus; + this.timeTaken = timeTaken; + } + + public void setHttpStatus(int httpStatus) { this.httpStatus = httpStatus; } + + public void setTimeTaken(long timeTaken) { this.timeTaken = timeTaken; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + sb.append(DateTimeHelper.formatDateUTC(requestTime)) + .append(FIELD_SEP).append(userName) + .append(FIELD_SEP).append(fromAddress) + .append(FIELD_SEP).append(requestMethod) + .append(FIELD_SEP).append(requestUrl) + .append(FIELD_SEP).append(httpStatus) + .append(FIELD_SEP).append(timeTaken); + + return sb.toString(); + } + } +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/HeadersUtil.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/HeadersUtil.java new file mode 100644 index 0000000000..527c484119 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/HeadersUtil.java @@ -0,0 +1,109 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import com.google.common.annotations.VisibleForTesting; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +@Component +public class HeadersUtil { + public static final Logger LOG = LoggerFactory.getLogger(HeadersUtil.class); + + public static final String X_FRAME_OPTIONS_KEY = "X-Frame-Options"; + public static final String X_CONTENT_TYPE_OPTIONS_KEY = "X-Content-Type-Options"; + public static final String X_XSS_PROTECTION_KEY = "X-XSS-Protection"; + public static final String STRICT_TRANSPORT_SEC_KEY = "Strict-Transport-Security"; + public static final String CONTENT_SEC_POLICY_KEY = "Content-Security-Policy"; + public static final String X_FRAME_OPTIONS_VAL = "DENY"; + public static final String X_CONTENT_TYPE_OPTIONS_VAL = "nosniff"; + public static final String X_XSS_PROTECTION_VAL = "1; mode=block"; + public static final String STRICT_TRANSPORT_SEC_VAL = "max-age=31536000; includeSubDomains"; + public static final String CONTENT_SEC_POLICY_VAL = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; connect-src 'self'; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline';font-src 'self' data:"; + public static final String SERVER_KEY = "Server"; + public static final String USER_AGENT_KEY = "User-Agent"; + public static final String USER_AGENT_VALUE = "Mozilla"; + public static final String X_REQUESTED_WITH_KEY = "X-REQUESTED-WITH"; + public static final String X_REQUESTED_WITH_VALUE = "XMLHttpRequest"; + public static final int SC_AUTHENTICATION_TIMEOUT = 419; + public static final String CONFIG_PREFIX_HTTP_RESPONSE_HEADER = "atlas.headers"; + + private static final Map HEADER_MAP = new HashMap<>(); + + private HeadersUtil() { + // to block instantiation + } + public static String getHeaderMap(String header) { + return HEADER_MAP.get(header); + } + + public static Map getAllHeaders() { + return new HashMap<>(HEADER_MAP); + } + + public static void setHeaderMapAttributes(AtlasResponseRequestWrapper responseWrapper, String headerKey) { + responseWrapper.setHeader(headerKey, HEADER_MAP.get(headerKey)); + } + + public static void setSecurityHeaders(AtlasResponseRequestWrapper responseWrapper) { + HEADER_MAP.forEach((key, value) -> responseWrapper.setHeader(key, value)); + } + + @VisibleForTesting + public static void initializeHttpResponseHeaders(Properties configuredHeaders) { + HEADER_MAP.clear(); + + HEADER_MAP.put(X_FRAME_OPTIONS_KEY, X_FRAME_OPTIONS_VAL); + HEADER_MAP.put(X_CONTENT_TYPE_OPTIONS_KEY, X_CONTENT_TYPE_OPTIONS_VAL); + HEADER_MAP.put(X_XSS_PROTECTION_KEY, X_XSS_PROTECTION_VAL); + HEADER_MAP.put(STRICT_TRANSPORT_SEC_KEY, STRICT_TRANSPORT_SEC_VAL); + HEADER_MAP.put(CONTENT_SEC_POLICY_KEY, CONTENT_SEC_POLICY_VAL); + HEADER_MAP.put(SERVER_KEY, AtlasConfiguration.HTTP_HEADER_SERVER_VALUE.getString()); + + if (configuredHeaders != null) { + configuredHeaders.stringPropertyNames().forEach(name -> HEADER_MAP.put(name, configuredHeaders.getProperty(name))); + } + } + + static { + Properties configuredHeaders = null; + + try { + Configuration baseConfig = ApplicationProperties.get(); + Configuration headerConfig = ApplicationProperties.getSubsetConfiguration(baseConfig, CONFIG_PREFIX_HTTP_RESPONSE_HEADER); + + configuredHeaders = Optional.ofNullable(headerConfig) + .map(ConfigurationConverter::getProperties) + .orElseGet(Properties::new); + } catch (Exception e) { + LOG.info("Failed to load custom headers: {}", e.getMessage()); + } + + initializeHttpResponseHeaders(configuredHeaders); + } +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java new file mode 100644 index 0000000000..c17a2fa571 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java @@ -0,0 +1,336 @@ +/** + * Licensed 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. See accompanying LICENSE file. + */ + +package org.apache.atlas.notification.rest.web.filters; + +import javax.servlet.Filter; +import javax.servlet.FilterRegistration; +import javax.servlet.FilterRegistration.Dynamic; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.descriptor.JspConfigDescriptor; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.Map; +import java.util.Set; + + +/** + */ +public class NullServletContext implements ServletContext { + + + + public void setSessionTrackingModes( + Set sessionTrackingModes) { + } + + + public boolean setInitParameter(String name, String value) { + return false; + } + + + public void setAttribute(String name, Object object) { + } + + + public void removeAttribute(String name) { + } + + + public void log(String message, Throwable throwable) { + } + + + public void log(Exception exception, String msg) { + } + + + public void log(String msg) { + } + + + public String getVirtualServerName() { + return null; + } + + @Override + public int getSessionTimeout() { + return 0; + } + + @Override + public void setSessionTimeout(int i) { + + } + + @Override + public String getRequestCharacterEncoding() { + return ""; + } + + @Override + public void setRequestCharacterEncoding(String s) { + + } + + @Override + public String getResponseCharacterEncoding() { + return ""; + } + + @Override + public void setResponseCharacterEncoding(String s) { + + } + + + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + + public Enumeration getServlets() { + return null; + } + + + public Map getServletRegistrations() { + return null; + } + + + public ServletRegistration getServletRegistration(String servletName) { + return null; + } + + + public Enumeration getServletNames() { + return null; + } + + + public String getServletContextName() { + return null; + } + + + public Servlet getServlet(String name) throws ServletException { + return null; + } + + + public String getServerInfo() { + return null; + } + + + public Set getResourcePaths(String path) { + return null; + } + + + public InputStream getResourceAsStream(String path) { + return null; + } + + + public URL getResource(String path) throws MalformedURLException { + return null; + } + + + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + + public String getRealPath(String path) { + return null; + } + + + public RequestDispatcher getNamedDispatcher(String name) { + return null; + } + + + public int getMinorVersion() { + return 0; + } + + + public String getMimeType(String file) { + return null; + } + + + public int getMajorVersion() { + return 0; + } + + + public JspConfigDescriptor getJspConfigDescriptor() { + return null; + } + + + public Enumeration getInitParameterNames() { + return null; + } + + + public String getInitParameter(String name) { + return null; + } + + + public Map getFilterRegistrations() { + return null; + } + + + public FilterRegistration getFilterRegistration(String filterName) { + return null; + } + + + public Set getEffectiveSessionTrackingModes() { + return null; + } + + + public int getEffectiveMinorVersion() { + return 0; + } + + + public int getEffectiveMajorVersion() { + return 0; + } + + + public Set getDefaultSessionTrackingModes() { + return null; + } + + + public String getContextPath() { + return null; + } + + + public ServletContext getContext(String uripath) { + return null; + } + + + public ClassLoader getClassLoader() { + return null; + } + + + public Enumeration getAttributeNames() { + return null; + } + + + public Object getAttribute(String name) { + return null; + } + + + public void declareRoles(String... roleNames) { + } + + + public T createServlet(Class clazz) + throws ServletException { + return null; + } + + + public T createListener(Class clazz) + throws ServletException { + return null; + } + + + public T createFilter(Class clazz) + throws ServletException { + return null; + } + + + public ServletRegistration.Dynamic addServlet( + String servletName, Class servletClass) { + return null; + } + + @Override + public ServletRegistration.Dynamic addJspFile(String s, String s1) { + return null; + } + + + public ServletRegistration.Dynamic addServlet( + String servletName, Servlet servlet) { + return null; + } + + + public ServletRegistration.Dynamic addServlet( + String servletName, String className) { + return null; + } + + + public void addListener(Class listenerClass) { + } + + + public void addListener(T t) { + } + + + public void addListener(String className) { + } + + + public Dynamic addFilter(String filterName, + Class filterClass) { + return null; + } + + + public Dynamic addFilter(String filterName, Filter filter) { + return null; + } + + + public Dynamic addFilter(String filterName, String className) { + return null; + } + + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java new file mode 100644 index 0000000000..4a4c4ae765 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java @@ -0,0 +1,108 @@ +/** + * 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.atlas.notification.rest.web.filters; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; + +public class RestUtil { + + private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); + public static final String TIMEOUT_ACTION = "timeout"; + public static final String LOGOUT_URL = "/logout.html"; + private static final String PROXY_ATLAS_URL_PATH = "/atlas"; + private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; + private static final String X_FORWARDED_HOST = "x-forwarded-host"; + private static final String X_FORWARDED_CONTEXT = "x-forwarded-context"; + public static final String DELIMITTER = "://"; + + public static String constructForwardableURL(HttpServletRequest httpRequest) { + String xForwardedProto = ""; + String xForwardedHost = ""; + String xForwardedContext = ""; + Enumeration headerNames = httpRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String name = (String) headerNames.nextElement(); + Enumeration values = httpRequest.getHeaders(name); + String value = ""; + if (values != null) { + while (values.hasMoreElements()) { + value = (String) values.nextElement(); + } + } + if (StringUtils.trimToNull(name) != null && StringUtils.trimToNull(value) != null) { + if (name.equalsIgnoreCase(X_FORWARDED_PROTO)) { + xForwardedProto = value; + } else if (name.equalsIgnoreCase(X_FORWARDED_HOST)) { + xForwardedHost = value; + } else if (name.equalsIgnoreCase(X_FORWARDED_CONTEXT)) { + xForwardedContext = value; + } + } + } + if (xForwardedHost.contains(",")) { + if (LOG.isDebugEnabled()) { + LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); + } + xForwardedHost = xForwardedHost.split(",")[0].trim(); + } + String xForwardedURL = ""; + if (StringUtils.trimToNull(xForwardedProto) != null) { + //if header contains x-forwarded-host and x-forwarded-context + if (StringUtils.trimToNull(xForwardedHost) != null && StringUtils.trimToNull(xForwardedContext) != null) { + xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + httpRequest.getRequestURI(); + } else if (StringUtils.trimToNull(xForwardedHost) != null) { + //if header contains x-forwarded-host and does not contains x-forwarded-context + xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + httpRequest.getRequestURI(); + } else { + //if header does not contains x-forwarded-host and x-forwarded-context + //preserve the x-forwarded-proto value coming from the request. + String requestURL = httpRequest.getRequestURL().toString(); + if (StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { + requestURL = requestURL.replaceFirst("http", xForwardedProto); + } + xForwardedURL = requestURL; + } + } + return xForwardedURL; + } + + public static String constructRedirectURL(HttpServletRequest request, String redirectUrl, String xForwardedURL, String originalUrlQueryParam) { + String delimiter = "?"; + if (redirectUrl.contains("?")) { + delimiter = "&"; + } + String loginURL = redirectUrl + delimiter + originalUrlQueryParam + "="; + if (StringUtils.trimToNull(xForwardedURL) != null) { + loginURL += xForwardedURL; + } else { + loginURL += request.getRequestURL().append(getOriginalQueryString(request)); + } + return loginURL; + } + + private static String getOriginalQueryString(HttpServletRequest request) { + String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java new file mode 100644 index 0000000000..d43f3f1d68 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java @@ -0,0 +1,74 @@ +/* + * 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.atlas.notification.rest.web.filters; + +import com.nimbusds.jwt.SignedJWT; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +/** + * Internal token which describes JWT authentication + */ +public class SSOAuthentication implements Authentication { + + private final SignedJWT token; + private boolean authenticated = false; + + public SSOAuthentication(SignedJWT token) { + this.token = token; + } + + @Override + public SignedJWT getCredentials() { + return token; + } + + @Override + public Object getDetails() { + return null; + } + + @Override + public boolean isAuthenticated() { + return authenticated; + } + + @Override + public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { + this.authenticated = authenticated; + } + + @Override + public String getName() { + return null; + } + + @Override + public Collection getAuthorities() { + return null; + } + + @Override + public Object getPrincipal() { + return null; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java new file mode 100644 index 0000000000..433d3d0cc2 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java @@ -0,0 +1,78 @@ +/* + * 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.atlas.notification.rest.web.filters; + +import java.security.interfaces.RSAPublicKey; + +public class SSOAuthenticationProperties { + + private String authenticationProviderUrl = null; + private RSAPublicKey publicKey = null; + private String cookieName = "hadoop-jwt"; + private String originalUrlQueryParam = null; + private String[] userAgentList = null; + + public String getAuthenticationProviderUrl() { + return authenticationProviderUrl; + } + + public void setAuthenticationProviderUrl(String authenticationProviderUrl) { + this.authenticationProviderUrl = authenticationProviderUrl; + } + + public RSAPublicKey getPublicKey() { + return publicKey; + } + + public void setPublicKey(RSAPublicKey publicKey) { + this.publicKey = publicKey; + } + + public String getCookieName() { + return cookieName; + } + + public void setCookieName(String cookieName) { + this.cookieName = cookieName; + } + + public String getOriginalUrlQueryParam() { + return originalUrlQueryParam; + } + + public void setOriginalUrlQueryParam(String originalUrlQueryParam) { + this.originalUrlQueryParam = originalUrlQueryParam; + } + + /** + * @return the userAgentList + */ + public String[] getUserAgentList() { + return userAgentList; + } + + /** + * @param userAgentList the userAgentList to set + */ + public void setUserAgentList(String[] userAgentList) { + this.userAgentList = userAgentList; + } +} + diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java new file mode 100644 index 0000000000..7b131f273b --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java @@ -0,0 +1,158 @@ +/* + * 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.atlas.notification.rest.web.listeners; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.security.SecurityProperties; +import org.apache.commons.configuration.ConfigurationException; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.util.Shell; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; + +/** + * A class capable of performing a simple or kerberos login. + */ +public class LoginProcessor { + + private static final Logger LOG = LoggerFactory.getLogger(LoginProcessor.class); + public static final String ATLAS_AUTHENTICATION_PREFIX = "atlas.authentication."; + public static final String AUTHENTICATION_KERBEROS_METHOD = ATLAS_AUTHENTICATION_PREFIX + "method.kerberos"; + public static final String AUTHENTICATION_PRINCIPAL = ATLAS_AUTHENTICATION_PREFIX + "principal"; + public static final String AUTHENTICATION_KEYTAB = ATLAS_AUTHENTICATION_PREFIX + "keytab"; + + /** + * Perform a SIMPLE login based on established OS identity or a kerberos based login using the configured + * principal and keytab (via atlas-application.properties). + */ + public void login() { + // first, let's see if we're running in a hadoop cluster and have the env configured + boolean isHadoopCluster = isHadoopCluster(); + Configuration hadoopConfig = isHadoopCluster ? getHadoopConfiguration() : new Configuration(false); + org.apache.commons.configuration.Configuration configuration = getApplicationConfiguration(); + if (!isHadoopCluster) { + // need to read the configured authentication choice and create the UGI configuration + setupHadoopConfiguration(hadoopConfig, configuration); + } + doServiceLogin(hadoopConfig, configuration); + } + + protected void doServiceLogin(Configuration hadoopConfig, + org.apache.commons.configuration.Configuration configuration) { + UserGroupInformation.setConfiguration(hadoopConfig); + + UserGroupInformation ugi = null; + UserGroupInformation.AuthenticationMethod authenticationMethod = + SecurityUtil.getAuthenticationMethod(hadoopConfig); + try { + if (authenticationMethod == UserGroupInformation.AuthenticationMethod.SIMPLE) { + UserGroupInformation.loginUserFromSubject(null); + } else if (authenticationMethod == UserGroupInformation.AuthenticationMethod.KERBEROS) { + String bindAddress = getHostname(configuration); + UserGroupInformation.loginUserFromKeytab( + getServerPrincipal(configuration.getString(AUTHENTICATION_PRINCIPAL), bindAddress), + configuration.getString(AUTHENTICATION_KEYTAB)); + } + LOG.info("Logged in user {}", UserGroupInformation.getLoginUser()); + } catch (IOException e) { + throw new IllegalStateException(String.format("Unable to perform %s login.", authenticationMethod), e); + } + } + + private String getHostname(org.apache.commons.configuration.Configuration configuration) { + String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS); + if (bindAddress == null) { + LOG.info("No host name configured. Defaulting to local host name."); + try { + bindAddress = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + throw new IllegalStateException(e); + } + } + return bindAddress; + } + + protected void setupHadoopConfiguration(Configuration hadoopConfig, org.apache.commons.configuration.Configuration + configuration) { + String authMethod = ""; + String kerberosAuthNEnabled = configuration != null ? configuration.getString(AUTHENTICATION_KERBEROS_METHOD) : null; + // getString may return null, and would like to log the nature of the default setting + if (kerberosAuthNEnabled == null || kerberosAuthNEnabled.equalsIgnoreCase("false")) { + LOG.info("No authentication method configured. Defaulting to simple authentication"); + authMethod = "simple"; + } else if (kerberosAuthNEnabled.equalsIgnoreCase("true")) { + authMethod = "kerberos"; + } + SecurityUtil + .setAuthenticationMethod(UserGroupInformation.AuthenticationMethod.valueOf(authMethod.toUpperCase()), + hadoopConfig); + } + + /** + * Return a server (service) principal. The token "_HOST" in the principal will be replaced with the local host + * name (e.g. dgi/_HOST will be changed to dgi/localHostName) + * @param principal the input principal containing an option "_HOST" token + * @return the service principal. + * @throws IOException + */ + private String getServerPrincipal(String principal, String host) throws IOException { + return SecurityUtil.getServerPrincipal(principal, host); + } + + /** + * Returns a Hadoop configuration instance. + * @return the configuration. + */ + protected Configuration getHadoopConfiguration() { + return new Configuration(); + } + + /** + * Returns the metadata application configuration. + * @return the metadata configuration. + * @throws ConfigurationException + */ + protected org.apache.commons.configuration.Configuration getApplicationConfiguration() { + try { + return ApplicationProperties.get(); + } catch (AtlasException e) { + LOG.warn("Error reading application configuration", e); + } + return null; + } + + /** + * Uses a hadoop shell to discern whether a hadoop cluster is available/configured. + * @return true if a hadoop cluster is detected. + */ + protected boolean isHadoopCluster() { + boolean isHadoopCluster = false; + try { + isHadoopCluster = Shell.getHadoopHome() != null; + } catch (IOException e) { + // ignore - false is default setting + } + return isHadoopCluster; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java new file mode 100644 index 0000000000..8c6336fd2e --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java @@ -0,0 +1,129 @@ +/* + * 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 compliRance 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.atlas.notification.rest.web.model; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +public class User implements UserDetails { + private static final long serialVersionUID = 1L; + + private String username; + private String password; + private List authorities; + private boolean accountNonExpired = true; + private boolean accountNonLocked = true; + private boolean credentialsNonExpired = true; + private boolean enabled = true; + + public User(String userName2, String userPassword, + List grantedAuths) { + this.username = userName2; + this.password = userPassword; + this.authorities = grantedAuths; + + } + + public User() { + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + public void setAuthorities(List authorities) { + this.authorities = authorities; + } + + @Override + public boolean isAccountNonExpired() { + return this.accountNonExpired; + } + + public void setAccountNonExpired(boolean accountNonExpired) { + this.accountNonExpired = accountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + return this.accountNonLocked; + } + + public void setAccountNonLocked(boolean accountNonLocked) { + this.accountNonLocked = accountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + return this.credentialsNonExpired; + } + + public void setCredentialsNonExpired(boolean credentialsNonExpired) { + this.credentialsNonExpired = credentialsNonExpired; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("User [username="); + builder.append(username); + builder.append(", authorities="); + builder.append(authorities); + builder.append(", accountNonExpired="); + builder.append(accountNonExpired); + builder.append(", accountNonLocked="); + builder.append(accountNonLocked); + builder.append(", credentialsNonExpired="); + builder.append(credentialsNonExpired); + builder.append(", enabled="); + builder.append(enabled); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java new file mode 100755 index 0000000000..a9d78ac1e5 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java @@ -0,0 +1,129 @@ +/** + * 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.atlas.notification.rest.web.resources; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasClient; +import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.notification.rest.web.service.ServiceState; +import org.apache.atlas.utils.AtlasJson; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.HashMap; +import java.util.Map; + +/** + * Jersey Resource for admin operations. + */ +@Path("api/atlas/admin") +@Singleton +@Service +@EnableScheduling +public class AdminResource { + private static final Logger LOG = LoggerFactory.getLogger(AdminResource.class); + + + private static Configuration atlasProperties; + private final ServiceState serviceState; + + static { + try { + atlasProperties = ApplicationProperties.get(); + } catch (Exception e) { + LOG.info("Failed to load application properties", e); + } + } + + @Inject + public AdminResource(ServiceState serviceState) { + this.serviceState = serviceState; + } + + /** + * Fetches the thread stack dump for this application. + * + * @return json representing the thread stack dump. + */ + @GET + @Path("stack") + @Produces(MediaType.TEXT_PLAIN) + public String getThreadDump() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> AdminResource.getThreadDump()"); + } + + ThreadGroup topThreadGroup = Thread.currentThread().getThreadGroup(); + + while (topThreadGroup.getParent() != null) { + topThreadGroup = topThreadGroup.getParent(); + } + Thread[] threads = new Thread[topThreadGroup.activeCount()]; + + int nr = topThreadGroup.enumerate(threads); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < nr; i++) { + builder.append(threads[i].getName()).append("\nState: "). + append(threads[i].getState()).append("\n"); + String stackTrace = StringUtils.join(threads[i].getStackTrace(), "\n"); + builder.append(stackTrace); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== AdminResource.getThreadDump()"); + } + + return builder.toString(); + } + + + @GET + @Path("status") + @Produces(Servlets.JSON_MEDIA_TYPE) + public Response getStatus() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> AdminResource.getStatus()"); + } + + Map responseData = new HashMap() {{ + put(AtlasClient.STATUS, serviceState.getState().toString()); + }}; + + Response response = Response.ok(AtlasJson.toV1Json(responseData)).build(); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== AdminResource.getStatus()"); + } + + return response; + } + + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java new file mode 100644 index 0000000000..0f973f4521 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java @@ -0,0 +1,128 @@ +/** + * 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.atlas.notification.rest.web.rest; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.authorize.AtlasAdminAccessRequest; +import org.apache.atlas.authorize.AtlasAuthorizationUtils; +import org.apache.atlas.authorize.AtlasPrivilege; +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.hook.AtlasHook; +import org.apache.atlas.kafka.KafkaNotification; +import org.apache.atlas.notification.NotificationException; +import org.apache.atlas.notification.NotificationInterface; +import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.utils.AtlasJson; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Path("api/atlas/v2/notification") +@Singleton +@Service +@Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) +@Produces({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) +public class NotificationREST { + private static final Logger LOG = LoggerFactory.getLogger(NotificationREST.class); + public static final String ATLAS_HOOK_TOPIC = AtlasConfiguration.NOTIFICATION_HOOK_TOPIC_NAME.getString(); + public static final String ATLAS_ENTITIES_TOPIC = AtlasConfiguration.NOTIFICATION_ENTITIES_TOPIC_NAME.getString(); + private static final String[] ATLAS_HOOK_CONSUMER_TOPICS = AtlasConfiguration.NOTIFICATION_HOOK_CONSUMER_TOPIC_NAMES.getStringArray(ATLAS_HOOK_TOPIC); + private static final String[] ATLAS_ENTITIES_CONSUMER_TOPICS = AtlasConfiguration.NOTIFICATION_ENTITIES_CONSUMER_TOPIC_NAMES.getStringArray(ATLAS_ENTITIES_TOPIC); + private static final Set TOPICS = new HashSet<>(); + + private final NotificationInterface notificationInterface; + + static { + TOPICS.addAll(Arrays.asList(ATLAS_HOOK_CONSUMER_TOPICS)); + TOPICS.addAll(Arrays.asList(ATLAS_ENTITIES_CONSUMER_TOPICS)); + } + + @Inject + public NotificationREST(NotificationInterface notificationInterface) { + this.notificationInterface = notificationInterface; + } + + /** + * Publish notifications on Kafka topic + * + * @param topicName - nameOfTheQueue + * @throws AtlasBaseException + */ + @POST + @Path("/topic/{topicName}") + @Consumes({Servlets.JSON_MEDIA_TYPE, MediaType.APPLICATION_JSON}) + public void handleNotifications(@PathParam("topicName") String topicName, @Context HttpServletRequest request) throws AtlasBaseException, IOException { + LOG.debug("Handling notifications for topic {}", topicName); + + if (!TOPICS.contains(topicName)) { + throw new AtlasBaseException(AtlasErrorCode.INVALID_TOPIC_NAME, topicName); + } + + String messagesAsJson = Servlets.getRequestPayload(request); + List messages = getMessagesToNotify(messagesAsJson); + + try { + KafkaNotification notifier = (KafkaNotification) notificationInterface; + notifier.sendInternal(topicName, messages, AtlasHook.isHookMsgsSortEnabled); + + } catch (NotificationException exception) { + List failedMessages = exception.getFailedMessages(); + String concatenatedMessage = StringUtils.join(failedMessages, "\n"); + + throw new AtlasBaseException(AtlasErrorCode.NOTIFICATION_EXCEPTION, exception, concatenatedMessage); + } + + } + + private List getMessagesToNotify(String messagesAsJson) { + List messages = new ArrayList<>(); + + try { + ArrayNode messageNodes = AtlasJson.parseToV1ArrayNode(messagesAsJson); + for (JsonNode messageNode : messageNodes) { + messages.add(AtlasJson.toV1Json(messageNode)); + } + } catch (IOException e) { + messages.add(messagesAsJson); + } + + return messages; + } + +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java new file mode 100644 index 0000000000..ccebd0b724 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java @@ -0,0 +1,203 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.notification.rest.web.model.User; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.authentication.BindAuthenticator; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Properties; + +@Component +public class AtlasADAuthenticationProvider extends AtlasAbstractAuthenticationProvider { + private static Logger LOG = LoggerFactory.getLogger(AtlasADAuthenticationProvider.class); + + private String adURL; + private String adDomain; + private String adBindDN; + private String adBindPassword; + private String adUserSearchFilter; + private String adBase; + private String adReferral; + private String adDefaultRole; + private boolean groupsFromUGI; + + @PostConstruct + public void setup() { + setADProperties(); + } + + @Override + public Authentication authenticate(Authentication authentication) { + Authentication auth = getADBindAuthentication(authentication); + if (auth != null && auth.isAuthenticated()) { + return auth; + } else { + auth = getADAuthentication(authentication); + if (auth != null && auth.isAuthenticated()) { + return auth; + } + } + if (auth == null) { + throw new AtlasAuthenticationException("AD Authentication Failed"); + } + return auth; + } + + private Authentication getADBindAuthentication (Authentication authentication) { + try { + String userName = authentication.getName(); + String userPassword = ""; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource(adURL); + ldapContextSource.setUserDn(adBindDN); + ldapContextSource.setPassword(adBindPassword); + ldapContextSource.setReferral(adReferral); + ldapContextSource.setCacheEnvironmentProperties(true); + ldapContextSource.setAnonymousReadOnly(false); + ldapContextSource.setPooled(true); + ldapContextSource.afterPropertiesSet(); + + FilterBasedLdapUserSearch userSearch=new FilterBasedLdapUserSearch(adBase, adUserSearchFilter,ldapContextSource); + userSearch.setSearchSubtree(true); + + BindAuthenticator bindAuthenticator = new BindAuthenticator(ldapContextSource); + bindAuthenticator.setUserSearch(userSearch); + bindAuthenticator.afterPropertiesSet(); + + LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator); + + if (userName != null && userPassword != null + && !userName.trim().isEmpty() + && !userPassword.trim().isEmpty()) { + final List grantedAuths = getAuthorities(userName); + final UserDetails principal = new User(userName, userPassword, + grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, userPassword, grantedAuths); + authentication = ldapAuthenticationProvider.authenticate(finalAuthentication); + if (groupsFromUGI) { + authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); + } + return authentication; + } else { + LOG.error("AD Authentication Failed userName or userPassword is null or empty"); + return null; + } + } catch (Exception e) { + LOG.error("AD Authentication Failed:", e); + return null; + } + } + + private Authentication getADAuthentication(Authentication authentication) { + try { + String userName = authentication.getName(); + String userPassword = ""; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + ActiveDirectoryLdapAuthenticationProvider adAuthenticationProvider = + new ActiveDirectoryLdapAuthenticationProvider(adDomain, adURL); + adAuthenticationProvider.setConvertSubErrorCodesToExceptions(true); + adAuthenticationProvider.setUseAuthenticationRequestCredentials(true); + adAuthenticationProvider.setSearchFilter(adUserSearchFilter); + + if (userName != null && userPassword != null + && !userName.trim().isEmpty() + && !userPassword.trim().isEmpty()) { + final List grantedAuths = getAuthorities(userName); + final UserDetails principal = new User(userName, userPassword, + grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, userPassword, grantedAuths); + authentication = adAuthenticationProvider.authenticate(finalAuthentication); + if(groupsFromUGI) { + authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); + } + return authentication; + } else { + LOG.error("AD Authentication Failed userName or userPassword is null or empty"); + return null; + } + } catch (Exception e) { + LOG.error("AD Authentication Failed:", e); + return null; + } + } + + private void setADProperties() { + try { + + Configuration configuration = ApplicationProperties.get(); + Properties properties = ConfigurationConverter.getProperties(configuration.subset("atlas.authentication.method.ldap.ad")); + this.adDomain = properties.getProperty("domain"); + this.adURL = properties.getProperty("url"); + this.adBindDN = properties.getProperty("bind.dn"); + this.adBindPassword = properties.getProperty("bind.password"); + this.adUserSearchFilter = properties.getProperty("user.searchfilter"); + if (adUserSearchFilter==null || adUserSearchFilter.trim().isEmpty()) { + adUserSearchFilter="(sAMAccountName={0})"; + } + this.adBase = properties.getProperty("base.dn"); + this.adReferral = properties.getProperty("referral"); + this.adDefaultRole = properties.getProperty("default.role"); + + this.groupsFromUGI = configuration.getBoolean("atlas.authentication.method.ldap.ugi-groups", true); + + if(LOG.isDebugEnabled()) { + LOG.debug("AtlasADAuthenticationProvider{" + + "adURL='" + adURL + '\'' + + ", adDomain='" + adDomain + '\'' + + ", adBindDN='" + adBindDN + '\'' + + ", adUserSearchFilter='" + adUserSearchFilter + '\'' + + ", adBase='" + adBase + '\'' + + ", adReferral='" + adReferral + '\'' + + ", adDefaultRole='" + adDefaultRole + '\'' + + ", groupsFromUGI=" + groupsFromUGI + + '}'); + } + + + } catch (Exception e) { + LOG.error("Exception while setADProperties", e); + } + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java new file mode 100644 index 0000000000..e88c60d944 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java @@ -0,0 +1,149 @@ +/* + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.utils.AuthenticationUtil; +import org.apache.commons.collections.CollectionUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.Groups; +import org.apache.hadoop.security.UserGroupInformation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public abstract class AtlasAbstractAuthenticationProvider implements AuthenticationProvider { + private static final Logger LOG = LoggerFactory.getLogger(AtlasAbstractAuthenticationProvider.class); + + @Override + public boolean supports(Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); + } + + /** + * + * @param authentication + * @return + */ + public Authentication getAuthenticationWithGrantedAuthority( + Authentication authentication) { + UsernamePasswordAuthenticationToken result = null; + if (authentication != null && authentication.isAuthenticated()) { + final List grantedAuths = getAuthorities(authentication + .getName()); + final UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString(), + grantedAuths); + result = new UsernamePasswordAuthenticationToken(userDetails, + authentication.getCredentials(), grantedAuths); + result.setDetails(authentication.getDetails()); + return result; + } + return authentication; + } + + /** + * This method will be modified when actual roles are introduced. + * + */ + protected List getAuthorities(String username) { + final List grantedAuths = new ArrayList<>(); + grantedAuths.add(new SimpleGrantedAuthority("DATA_SCIENTIST")); + return grantedAuths; + } + + + public Authentication getAuthenticationWithGrantedAuthorityFromUGI( + Authentication authentication) { + UsernamePasswordAuthenticationToken result = null; + if (authentication != null && authentication.isAuthenticated()) { + + List grantedAuthsUGI = getAuthoritiesFromUGI(authentication + .getName()); + + final UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString(), + grantedAuthsUGI); + result = new UsernamePasswordAuthenticationToken(userDetails, + authentication.getCredentials(), grantedAuthsUGI); + result.setDetails(authentication.getDetails()); + return result; + } + return authentication; + } + + public static List getAuthoritiesFromUGI(String userName) { + Set userGroups = new HashSet<>(); + UserGroupInformation ugi = UserGroupInformation.createRemoteUser(userName); + + if (ugi != null) { + String[] groups = ugi.getGroupNames(); + + if(LOG.isDebugEnabled()) { + LOG.debug("UserGroupInformation userGroups=" + Arrays.toString(groups)); + } + + if (groups != null) { + for (String group : groups) { + userGroups.add(group); + } + } + } + + // if group empty take groups from Hadoop LDAP-based group mapping + if (CollectionUtils.isEmpty(userGroups) || AuthenticationUtil.includeHadoopGroups()) { + try { + Configuration config = new Configuration(); + Groups gp = new Groups(config); + List groups = gp.getGroups(userName); + + if(LOG.isDebugEnabled()) { + LOG.debug("Hadoop userGroups=" + groups); + } + + if (groups != null) { + for (String group : groups) { + userGroups.add(group); + } + } + } catch (java.io.IOException e) { + LOG.error("Exception while fetching groups ", e); + } + } + + List ret = new ArrayList<>(); + + for (String userGroup : userGroups) { + ret.add(new SimpleGrantedAuthority(userGroup)); + } + + return ret; + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java new file mode 100644 index 0000000000..0e8cce29fb --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.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.atlas.notification.rest.web.security; + +import org.springframework.security.core.AuthenticationException; + +public class AtlasAuthenticationException extends AuthenticationException { + + public AtlasAuthenticationException(String message) { + super(message); + } + + public AtlasAuthenticationException(String message, Throwable cause) { + super(message, cause); + } + +} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java new file mode 100644 index 0000000000..478993fe5a --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java @@ -0,0 +1,55 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class AtlasAuthenticationFailureHandler implements AuthenticationFailureHandler { + + private static Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFailureHandler.class); + + @Override + public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, + AuthenticationException e) throws IOException, ServletException { + + + LOG.debug("Login Failure ", e); + + JSONObject json = new JSONObject(); + json.put("msgDesc", e.getMessage()); + + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(json.toJSONString()); + + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java new file mode 100644 index 0000000000..3dfb9184f1 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java @@ -0,0 +1,151 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.ApplicationProperties; +import org.apache.commons.configuration.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.inject.Inject; + +@Component +@Scope("prototype") +public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { + private static final Logger LOG = LoggerFactory + .getLogger(AtlasAuthenticationProvider.class); + + private boolean fileAuthenticationMethodEnabled = true; + private boolean pamAuthenticationEnabled = false; + private String ldapType = "NONE"; + public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file"; + public static final String LDAP_AUTH_METHOD = "atlas.authentication.method.ldap"; + public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; + public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; + + + + private boolean ssoEnabled = false; + + final AtlasLdapAuthenticationProvider ldapAuthenticationProvider; + + final AtlasFileAuthenticationProvider fileAuthenticationProvider; + + final AtlasADAuthenticationProvider adAuthenticationProvider; + + final AtlasPamAuthenticationProvider pamAuthenticationProvider; + + @Inject + public AtlasAuthenticationProvider(AtlasLdapAuthenticationProvider ldapAuthenticationProvider, + AtlasFileAuthenticationProvider fileAuthenticationProvider, + AtlasADAuthenticationProvider adAuthenticationProvider, + AtlasPamAuthenticationProvider pamAuthenticationProvider) { + this.ldapAuthenticationProvider = ldapAuthenticationProvider; + this.fileAuthenticationProvider = fileAuthenticationProvider; + this.adAuthenticationProvider = adAuthenticationProvider; + this.pamAuthenticationProvider = pamAuthenticationProvider; + } + + @PostConstruct + void setAuthenticationMethod() { + try { + Configuration configuration = ApplicationProperties.get(); + + this.fileAuthenticationMethodEnabled = configuration.getBoolean(FILE_AUTH_METHOD, true); + + this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false); + + boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false); + + if (ldapAuthenticationEnabled) { + this.ldapType = configuration.getString(LDAP_TYPE, "NONE"); + } else { + this.ldapType = "NONE"; + } + } catch (Exception e) { + LOG.error("Error while getting atlas.login.method application properties", e); + } + } + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + + if(ssoEnabled){ + if (authentication != null){ + authentication = getSSOAuthentication(authentication); + if(authentication!=null && authentication.isAuthenticated()){ + return authentication; + } + } + } else { + + if (ldapType.equalsIgnoreCase("LDAP")) { + try { + authentication = ldapAuthenticationProvider.authenticate(authentication); + } catch (Exception ex) { + LOG.error("Error while LDAP authentication", ex); + } + } else if (ldapType.equalsIgnoreCase("AD")) { + try { + authentication = adAuthenticationProvider.authenticate(authentication); + } catch (Exception ex) { + LOG.error("Error while AD authentication", ex); + } + } else if (pamAuthenticationEnabled) { + try { + authentication = pamAuthenticationProvider.authenticate(authentication); + } catch (Exception ex) { + LOG.error("Error while PAM authentication", ex); + } + } + } + + if (authentication != null) { + if (authentication.isAuthenticated()) { + return authentication; + } else if (fileAuthenticationMethodEnabled) { // If the LDAP/AD/PAM authentication fails try the local filebased login method + authentication = fileAuthenticationProvider.authenticate(authentication); + + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } + } + } + + LOG.error("Authentication failed."); + throw new AtlasAuthenticationException("Authentication failed."); + } + + public boolean isSsoEnabled() { + return ssoEnabled; + } + + public void setSsoEnabled(boolean ssoEnabled) { + this.ssoEnabled = ssoEnabled; + } + + private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException{ + return authentication; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java new file mode 100644 index 0000000000..288b4b4e8b --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java @@ -0,0 +1,70 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.AtlasConfiguration; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + + +@Component +public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + + private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); + private int sessionTimeout = 3600; + public static final String LOCALLOGIN = "locallogin"; + + @PostConstruct + public void setup() { + sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + + LOG.debug("Login Success " + authentication.getPrincipal()); + + JSONObject json = new JSONObject(); + json.put("msgDesc", "Success"); + + if (request.getSession() != null) { // incase of form based login mark it as local login in session + request.getSession().setAttribute(LOCALLOGIN,"true"); + request.getServletContext().setAttribute(request.getSession().getId(), LOCALLOGIN); + + if (this.sessionTimeout != -1) { + request.getSession().setMaxInactiveInterval(sessionTimeout); + } + } + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_OK); + response.setCharacterEncoding("UTF-8"); + response.getWriter().write(json.toJSONString()); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java new file mode 100644 index 0000000000..266a1bafc1 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java @@ -0,0 +1,80 @@ +/* + * 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 compliRance 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.atlas.notification.rest.web.security; + + +import org.apache.atlas.notification.rest.web.dao.UserDao; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.util.Collection; + + +@Component +public class AtlasFileAuthenticationProvider extends AtlasAbstractAuthenticationProvider { + + private static Logger logger = LoggerFactory.getLogger(AtlasFileAuthenticationProvider.class); + + private final UserDetailsService userDetailsService; + + @Inject + public AtlasFileAuthenticationProvider(UserDetailsService userDetailsService) { + this.userDetailsService = userDetailsService; + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + String username = authentication.getName(); + String password = (String) authentication.getCredentials(); + + if (username == null || username.isEmpty()) { + logger.error("Username can't be null or empty."); + + throw new BadCredentialsException("Username can't be null or empty."); + } + + if (password == null || password.isEmpty()) { + logger.error("Password can't be null or empty."); + + throw new BadCredentialsException("Password can't be null or empty."); + } + + UserDetails user = userDetailsService.loadUserByUsername(username); + boolean isValidPassword = UserDao.checkEncrypted(password, user.getPassword(), username); + + if (!isValidPassword) { + logger.error("Wrong password " + username); + + throw new BadCredentialsException("Wrong password"); + } + + Collection authorities = user.getAuthorities(); + + authentication = new UsernamePasswordAuthenticationToken(username, password, authorities); + + return authentication; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java new file mode 100644 index 0000000000..d2725e3d8f --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java @@ -0,0 +1,299 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.notification.rest.web.model.User; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.configuration.ConfigurationConverter; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.ldap.core.support.LdapContextSource; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.ldap.DefaultSpringSecurityContextSource; +import org.springframework.security.ldap.authentication.BindAuthenticator; +import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; +import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; +import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Properties; + +@Component +public class AtlasLdapAuthenticationProvider extends + AtlasAbstractAuthenticationProvider { + private static Logger LOG = LoggerFactory.getLogger(AtlasLdapAuthenticationProvider.class); + private boolean isDebugEnabled = LOG.isDebugEnabled(); + + private String ldapURL; + private String ldapUserDNPattern; + private String ldapGroupSearchBase; + private String ldapGroupSearchFilter; + private String ldapGroupRoleAttribute; + private String ldapBindDN; + private String ldapBindPassword; + private String ldapDefaultRole; + private String ldapUserSearchFilter; + private String ldapReferral; + private String ldapBase; + private boolean groupsFromUGI; + + @PostConstruct + public void setup() { + setLdapProperties(); + } + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + try { + authentication = getLdapBindAuthentication(authentication); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } else { + authentication = getLdapAuthentication(authentication); + if (authentication != null && authentication.isAuthenticated()) { + return authentication; + } + } + } catch (Exception e) { + throw new AtlasAuthenticationException(e.getMessage(), e.getCause()); + } + return authentication; + } + + private Authentication getLdapBindAuthentication( + Authentication authentication) { + try { + if (isDebugEnabled) { + LOG.debug("==> AtlasLdapAuthenticationProvider getLdapBindAuthentication"); + } + String userName = authentication.getName(); + String userPassword = ""; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + LdapContextSource ldapContextSource = getLdapContextSource(); + + DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = getDefaultLdapAuthoritiesPopulator(ldapContextSource); + + if (ldapUserSearchFilter == null + || ldapUserSearchFilter.trim().isEmpty()) { + ldapUserSearchFilter = "(uid={0})"; + } + + FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch( + ldapBase, ldapUserSearchFilter, ldapContextSource); + userSearch.setSearchSubtree(true); + + BindAuthenticator bindAuthenticator = getBindAuthenticator( + userSearch, ldapContextSource); + + LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider( + bindAuthenticator, defaultLdapAuthoritiesPopulator); + + if (userName != null && userPassword != null + && !userName.trim().isEmpty() + && !userPassword.trim().isEmpty()) { + final List grantedAuths = getAuthorities(userName); + final UserDetails principal = new User(userName, userPassword, + grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, userPassword, grantedAuths); + authentication = ldapAuthenticationProvider.authenticate(finalAuthentication); + if(groupsFromUGI) { + authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); + } + return authentication; + } else { + LOG.error("LDAP Authentication::userName or userPassword is null or empty for userName " + + userName); + } + } catch (Exception e) { + LOG.error(" getLdapBindAuthentication LDAP Authentication Failed:", e); + } + if (isDebugEnabled) { + LOG.debug("<== AtlasLdapAuthenticationProvider getLdapBindAuthentication"); + } + return authentication; + } + + private Authentication getLdapAuthentication(Authentication authentication) { + + if (isDebugEnabled) { + LOG.debug("==> AtlasLdapAuthenticationProvider getLdapAuthentication"); + } + + try { + // taking the user-name and password from the authentication + // object. + String userName = authentication.getName(); + String userPassword = ""; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + // populating LDAP context source with LDAP URL and user-DN-pattern + LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource( + ldapURL); + + ldapContextSource.setCacheEnvironmentProperties(false); + ldapContextSource.setAnonymousReadOnly(true); + + // Creating BindAuthenticator using Ldap Context Source. + BindAuthenticator bindAuthenticator = new BindAuthenticator( + ldapContextSource); + //String[] userDnPatterns = new String[] { rangerLdapUserDNPattern }; + String[] userDnPatterns = ldapUserDNPattern.split(";"); + bindAuthenticator.setUserDnPatterns(userDnPatterns); + + LdapAuthenticationProvider ldapAuthenticationProvider = null; + + if (!StringUtils.isEmpty(ldapGroupSearchBase) && !StringUtils.isEmpty(ldapGroupSearchFilter)) { + // Creating LDAP authorities populator using Ldap context source and + // Ldap group search base. + // populating LDAP authorities populator with group search + // base,group role attribute, group search filter. + DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator( + ldapContextSource, ldapGroupSearchBase); + defaultLdapAuthoritiesPopulator.setGroupRoleAttribute(ldapGroupRoleAttribute); + defaultLdapAuthoritiesPopulator.setGroupSearchFilter(ldapGroupSearchFilter); + defaultLdapAuthoritiesPopulator.setIgnorePartialResultException(true); + + // Creating Ldap authentication provider using BindAuthenticator and Ldap authentication populator + ldapAuthenticationProvider = new LdapAuthenticationProvider( + bindAuthenticator, defaultLdapAuthoritiesPopulator); + } else { + ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator); + } + + // getting user authenticated + if (userName != null && userPassword != null + && !userName.trim().isEmpty() + && !userPassword.trim().isEmpty()) { + final List grantedAuths = getAuthorities(userName); + + final UserDetails principal = new User(userName, userPassword, + grantedAuths); + + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, userPassword, grantedAuths); + + authentication = ldapAuthenticationProvider + .authenticate(finalAuthentication); + if (groupsFromUGI) { + authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); + } + return authentication; + } else { + return authentication; + } + } catch (Exception e) { + LOG.error("getLdapAuthentication LDAP Authentication Failed:", e); + } + if (isDebugEnabled) { + LOG.debug("<== AtlasLdapAuthenticationProvider getLdapAuthentication"); + } + return authentication; + } + + private void setLdapProperties() { + try { + Configuration configuration = ApplicationProperties.get(); + Properties properties = ConfigurationConverter.getProperties(configuration.subset("atlas.authentication.method.ldap")); + ldapURL = properties.getProperty("url"); + ldapUserDNPattern = properties.getProperty("userDNpattern"); + ldapGroupSearchBase = properties.getProperty("groupSearchBase"); + ldapGroupSearchFilter = properties.getProperty("groupSearchFilter"); + ldapGroupRoleAttribute = properties.getProperty("groupRoleAttribute"); + ldapBindDN = properties.getProperty("bind.dn"); + ldapBindPassword = properties.getProperty("bind.password"); + ldapDefaultRole = properties.getProperty("default.role"); + ldapUserSearchFilter = properties.getProperty("user.searchfilter"); + ldapReferral = properties.getProperty("referral"); + ldapBase = properties.getProperty("base.dn"); + groupsFromUGI = configuration.getBoolean("atlas.authentication.method.ldap.ugi-groups", true); + + if(LOG.isDebugEnabled()) { + LOG.debug("AtlasLdapAuthenticationProvider{" + + "ldapURL='" + ldapURL + '\'' + + ", ldapUserDNPattern='" + ldapUserDNPattern + '\'' + + ", ldapGroupSearchBase='" + ldapGroupSearchBase + '\'' + + ", ldapGroupSearchFilter='" + ldapGroupSearchFilter + '\'' + + ", ldapGroupRoleAttribute='" + ldapGroupRoleAttribute + '\'' + + ", ldapBindDN='" + ldapBindDN + '\'' + + ", ldapDefaultRole='" + ldapDefaultRole + '\'' + + ", ldapUserSearchFilter='" + ldapUserSearchFilter + '\'' + + ", ldapReferral='" + ldapReferral + '\'' + + ", ldapBase='" + ldapBase + '\'' + + ", groupsFromUGI=" + groupsFromUGI + + '}'); + } + + } catch (Exception e) { + LOG.error("Exception while setLdapProperties", e); + } + + } + + private LdapContextSource getLdapContextSource() throws Exception { + LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource( + ldapURL); + ldapContextSource.setUserDn(ldapBindDN); + ldapContextSource.setPassword(ldapBindPassword); + ldapContextSource.setReferral(ldapReferral); + ldapContextSource.setCacheEnvironmentProperties(false); + ldapContextSource.setAnonymousReadOnly(false); + ldapContextSource.setPooled(true); + ldapContextSource.afterPropertiesSet(); + return ldapContextSource; + } + + private DefaultLdapAuthoritiesPopulator getDefaultLdapAuthoritiesPopulator( + LdapContextSource ldapContextSource) { + DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator( + ldapContextSource, ldapGroupSearchBase); + defaultLdapAuthoritiesPopulator + .setGroupRoleAttribute(ldapGroupRoleAttribute); + defaultLdapAuthoritiesPopulator + .setGroupSearchFilter(ldapGroupSearchFilter); + defaultLdapAuthoritiesPopulator.setIgnorePartialResultException(true); + return defaultLdapAuthoritiesPopulator; + } + + private BindAuthenticator getBindAuthenticator( + FilterBasedLdapUserSearch userSearch, + LdapContextSource ldapContextSource) throws Exception { + BindAuthenticator bindAuthenticator = new BindAuthenticator( + ldapContextSource); + bindAuthenticator.setUserSearch(userSearch); + String[] userDnPatterns = new String[] { ldapUserDNPattern }; + bindAuthenticator.setUserDnPatterns(userDnPatterns); + bindAuthenticator.afterPropertiesSet(); + return bindAuthenticator; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java new file mode 100644 index 0000000000..1617b33650 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java @@ -0,0 +1,172 @@ +/* + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.notification.rest.web.model.User; +import org.apache.commons.configuration.ConfigurationConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider; +import org.springframework.security.authentication.jaas.memory.InMemoryConfiguration; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +@Component +public class AtlasPamAuthenticationProvider extends AtlasAbstractAuthenticationProvider { + + private static Logger LOG = LoggerFactory.getLogger(AtlasPamAuthenticationProvider.class); + private boolean isDebugEnabled = LOG.isDebugEnabled(); + private static String loginModuleName = "org.apache.atlas.notification.rest.web.security.PamLoginModule"; + private static AppConfigurationEntry.LoginModuleControlFlag controlFlag = + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; + private Map options = new HashMap(); + private boolean groupsFromUGI; + private DefaultJaasAuthenticationProvider jaasAuthenticationProvider = + new DefaultJaasAuthenticationProvider(); + + @PostConstruct + public void setup() { + setPamProperties(); + init(); + } + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + Authentication auth = getPamAuthentication(authentication); + if (auth != null && auth.isAuthenticated()) { + return auth; + } else { + throw new AtlasAuthenticationException("PAM Authentication Failed"); + } + } + + private Authentication getPamAuthentication(Authentication authentication) { + if (isDebugEnabled) { + LOG.debug("==> AtlasPamAuthenticationProvider getPamAuthentication"); + } + try { + String userName = authentication.getName(); + String userPassword = ""; + if (authentication.getCredentials() != null) { + userPassword = authentication.getCredentials().toString(); + } + + // getting user authenticated + if (userName != null && userPassword != null + && !userName.trim().isEmpty() + && !userPassword.trim().isEmpty()) { + final List grantedAuths = getAuthorities(userName); + + final UserDetails principal = new User(userName, userPassword, + grantedAuths); + + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( + principal, userPassword, grantedAuths); + + authentication = jaasAuthenticationProvider + .authenticate(finalAuthentication); + + if(groupsFromUGI) { + authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); + } else { + authentication = getAuthenticationWithGrantedAuthority(authentication); + } + return authentication; + } else { + return authentication; + } + + } catch (Exception e) { + LOG.debug("Pam Authentication Failed:", e); + } + if (isDebugEnabled) { + LOG.debug("<== AtlasPamAuthenticationProvider getPamAuthentication : " + jaasAuthenticationProvider); + } + return authentication; + } + + private void setPamProperties() { + try { + this.groupsFromUGI = ApplicationProperties.get().getBoolean("atlas.authentication.method.pam.ugi-groups", true); + Properties properties = ConfigurationConverter.getProperties(ApplicationProperties.get() + .subset("atlas.authentication.method.pam")); + for (String key : properties.stringPropertyNames()) { + String value = properties.getProperty(key); + options.put(key, value); + } + if (!options.containsKey("service")) { + options.put("service", "atlas-login"); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("AtlasPAMAuthenticationProvider{groupsFromUGI= "+ groupsFromUGI +'\'' + + ", options=" + options + + '}'); + } + + } catch (Exception e) { + LOG.error("Exception while setLdapProperties", e); + } + } + + private void init() { + try { + AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry( + loginModuleName, controlFlag, options); + AppConfigurationEntry[] appConfigurationEntries = new AppConfigurationEntry[]{appConfigurationEntry}; + Map appConfigurationEntriesOptions = + new HashMap(); + appConfigurationEntriesOptions.put("SPRINGSECURITY", + appConfigurationEntries); + Configuration configuration = new InMemoryConfiguration( + appConfigurationEntriesOptions); + jaasAuthenticationProvider.setConfiguration(configuration); + UserAuthorityGranter authorityGranter = new UserAuthorityGranter(); + UserAuthorityGranter[] authorityGranters = new UserAuthorityGranter[]{authorityGranter}; + jaasAuthenticationProvider.setAuthorityGranters(authorityGranters); + jaasAuthenticationProvider.afterPropertiesSet(); + + if(LOG.isDebugEnabled()) { + LOG.debug("AtlasPAMAuthenticationProvider{" + + "jaasAuthenticationProvider='" + jaasAuthenticationProvider + '\'' + + ", loginModuleName='" + loginModuleName + '\'' + + ", controlFlag='" + controlFlag + '\'' + + ", options='" + options + '}'); + } + + + } catch (Exception e) { + LOG.error("Failed to init PAM Authentication", e); + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java new file mode 100644 index 0000000000..84c0d05fdf --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java @@ -0,0 +1,158 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.notification.rest.web.filters.ActiveServerFilter; +import org.apache.atlas.notification.rest.web.filters.AtlasAuthenticationEntryPoint; +import org.apache.atlas.notification.rest.web.filters.AtlasAuthenticationFilter; +import org.apache.atlas.notification.rest.web.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.notification.rest.web.filters.AtlasDelegatingAuthenticationEntryPoint; +import org.apache.atlas.notification.rest.web.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.notification.rest.web.filters.HeadersUtil; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +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.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; +import org.springframework.security.web.header.writers.StaticHeadersWriter; +import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter; +import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import javax.inject.Inject; +import java.util.LinkedHashMap; + +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasSecurityConfig.class); + + private final AtlasAuthenticationProvider authenticationProvider; + private final AtlasAuthenticationSuccessHandler successHandler; + private final AtlasAuthenticationFailureHandler failureHandler; + private final AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFilter; + private final AtlasAuthenticationFilter atlasAuthenticationFilter; + private final AtlasCSRFPreventionFilter csrfPreventionFilter; + private final AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint; + + // Our own Atlas filters need to be registered as well + private final Configuration configuration; + private final ActiveServerFilter activeServerFilter; + + @Inject + public AtlasSecurityConfig(AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFilter, + AtlasCSRFPreventionFilter atlasCSRFPreventionFilter, + AtlasAuthenticationFilter atlasAuthenticationFilter, + AtlasAuthenticationProvider authenticationProvider, + AtlasAuthenticationSuccessHandler successHandler, + AtlasAuthenticationFailureHandler failureHandler, + AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint, + Configuration configuration, + ActiveServerFilter activeServerFilter) + + { + this.ssoAuthenticationFilter = ssoAuthenticationFilter; + this.csrfPreventionFilter = atlasCSRFPreventionFilter; + this.atlasAuthenticationFilter = atlasAuthenticationFilter; + this.authenticationProvider = authenticationProvider; + this.successHandler = successHandler; + this.failureHandler = failureHandler; + this.atlasAuthenticationEntryPoint = atlasAuthenticationEntryPoint; + this.configuration = configuration; + this.activeServerFilter = activeServerFilter; + } + + public AuthenticationEntryPoint getAuthenticationEntryPoint() { + LinkedHashMap entryPointMap = new LinkedHashMap<>(); + entryPointMap.put(new RequestHeaderRequestMatcher(HeadersUtil.USER_AGENT_KEY, HeadersUtil.USER_AGENT_VALUE), atlasAuthenticationEntryPoint); + AtlasDelegatingAuthenticationEntryPoint basicAuthenticationEntryPoint = new AtlasDelegatingAuthenticationEntryPoint(entryPointMap); + return basicAuthenticationEntryPoint; + } + + public DelegatingAuthenticationEntryPoint getDelegatingAuthenticationEntryPoint() { + LinkedHashMap entryPointMap = new LinkedHashMap<>(); + entryPointMap.put(new RequestHeaderRequestMatcher("User-Agent", "Mozilla"), atlasAuthenticationEntryPoint); + DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(entryPointMap); + entryPoint.setDefaultEntryPoint(getAuthenticationEntryPoint()); + return entryPoint; + } + + @Inject + protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) { + authenticationManagerBuilder.authenticationProvider(authenticationProvider); + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.ignoring() + .antMatchers("/login.jsp", + "/css/**", + "/n/css/**", + "/img/**", + "/n/img/**", + "/libs/**", + "/n/libs/**", + "/js/**", + "/n/js/**", + "/ieerror.html", + "/migration-status.html", + "/api/atlas/admin/status"); + } + + protected void configure(HttpSecurity httpSecurity) throws Exception { + + //@formatter:off + httpSecurity + .authorizeRequests().anyRequest().authenticated() + .and() + .headers() + .addHeaderWriter(new StaticHeadersWriter(HeadersUtil.CONTENT_SEC_POLICY_KEY, HeadersUtil.getHeaderMap(HeadersUtil.CONTENT_SEC_POLICY_KEY))) + .addHeaderWriter(new StaticHeadersWriter(HeadersUtil.SERVER_KEY, HeadersUtil.getHeaderMap(HeadersUtil.SERVER_KEY))) + .and() + .servletApi() + .and() + .csrf().disable() + .sessionManagement() + .enableSessionUrlRewriting(false) + .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) + .sessionFixation() + .newSession() + .and() + .httpBasic() + .authenticationEntryPoint(getDelegatingAuthenticationEntryPoint()); + + + if (configuration.getBoolean("atlas.server.ha.enabled", false)) { + LOG.info("Atlas is in HA Mode, enabling ActiveServerFilter"); + httpSecurity.addFilterAfter(activeServerFilter, BasicAuthenticationFilter.class); + } + httpSecurity + .addFilterBefore(ssoAuthenticationFilter, BasicAuthenticationFilter.class) + .addFilterAfter(atlasAuthenticationFilter, SecurityContextHolderAwareRequestFilter.class) + .addFilterAfter(csrfPreventionFilter, AtlasAuthenticationFilter.class); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java new file mode 100644 index 0000000000..796ae4128c --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java @@ -0,0 +1,234 @@ +/* + * 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.atlas.notification.rest.web.security; + +import org.jvnet.libpam.PAM; +import org.jvnet.libpam.PAMException; +import org.jvnet.libpam.UnixUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.FailedLoginException; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import java.io.IOException; +import java.security.Principal; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class PamLoginModule extends Object implements LoginModule { + private static final Logger LOG = LoggerFactory.getLogger(PamLoginModule.class); + + public static final String SERVICE_KEY = "service"; + + private PAM pam; + private Subject subject; + private CallbackHandler callbackHandler; + private Map options; + + private String username; + private String password; + + private boolean authSucceeded; + private PamPrincipal principal; + + public PamLoginModule() + { + super(); + authSucceeded = false; + } + + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) + { + this.subject = subject; + this.callbackHandler = callbackHandler; + this.options = new HashMap<>(options); + } + + @Override + public boolean login() throws LoginException + { + initializePam(); + obtainUserAndPassword(); + return performLogin(); + } + + private void initializePam() throws LoginException + { + String service = (String) options.get(SERVICE_KEY); + if (service == null) + { + throw new LoginException("Error: PAM service was not defined"); + } + createPam(service); + } + + private void createPam(String service) throws LoginException + { + try + { + pam = new PAM(service); + } + catch (PAMException ex) + { + LoginException le = new LoginException("Error initializing PAM"); + le.initCause(ex); + throw le; + } + } + + private void obtainUserAndPassword() throws LoginException + { + if (callbackHandler == null) + { + throw new LoginException("Error: no CallbackHandler available to gather authentication information from the user"); + } + + try + { + NameCallback nameCallback = new NameCallback("username"); + PasswordCallback passwordCallback = new PasswordCallback("password", false); + + invokeCallbackHandler(nameCallback, passwordCallback); + + initUserName(nameCallback); + initPassword(passwordCallback); + + if (LOG.isDebugEnabled()) + LOG.debug("Searching for user " + nameCallback.getName()); + } + catch (IOException | UnsupportedCallbackException ex) + { + LoginException le = new LoginException("Error in callbacks"); + le.initCause(ex); + throw le; + } + } + + private void invokeCallbackHandler(NameCallback nameCallback, PasswordCallback passwordCallback) throws IOException, UnsupportedCallbackException + { + Callback[] callbacks = new Callback[2]; + callbacks[0] = nameCallback; + callbacks[1] = passwordCallback; + + callbackHandler.handle(callbacks); + } + + private void initUserName(NameCallback nameCallback) + { + username = nameCallback.getName(); + } + + private void initPassword(PasswordCallback passwordCallback) + { + char[] password = passwordCallback.getPassword(); + if (password != null) { + this.password = new String(password); + } + passwordCallback.clearPassword(); + } + + private boolean performLogin() throws LoginException + { + try + { + UnixUser user = pam.authenticate(username, password); + principal = new PamPrincipal(user); + authSucceeded = true; + + if (LOG.isDebugEnabled()) + LOG.debug("user " + username ); + return true; + } + catch (PAMException ex) + { + LoginException le = new FailedLoginException("Invalid username or password"); + le.initCause(ex); + throw le; + } + } + + @Override + public boolean commit() throws LoginException + { + if (authSucceeded == false) + { + return false; + } + + if (subject.isReadOnly()) + { + cleanup(); + throw new LoginException("Subject is read-only"); + } + + Set principals = subject.getPrincipals(); + if (principals.contains(principal) == false) + { + principals.add(principal); + } + + return true; + } + + @Override + public boolean abort() throws LoginException + { + if (authSucceeded == false) + { + return false; + } + + cleanup(); + return true; + } + + @Override + public boolean logout() throws LoginException + { + if (subject.isReadOnly()) + { + cleanup(); + throw new LoginException("Subject is read-only"); + } + + subject.getPrincipals().remove(principal); + + cleanup(); + return true; + } + + private void cleanup() + { + authSucceeded = false; + username = null; + password = null; + principal = null; + pam.dispose(); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java new file mode 100644 index 0000000000..ae669c7d2d --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java @@ -0,0 +1,84 @@ +/* + * 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.atlas.notification.rest.web.security; + +import org.jvnet.libpam.UnixUser; + +import java.security.Principal; +import java.util.Collections; +import java.util.Set; + +public class PamPrincipal extends Object implements Principal { + private String userName; + private String gecos; + private String homeDir; + private String shell; + private int uid; + private int gid; + private Set groups; + + public PamPrincipal(UnixUser user) + { + super(); + userName = user.getUserName(); + gecos = user.getGecos(); + homeDir = user.getDir(); + shell = user.getShell(); + uid = user.getUID(); + gid = user.getGID(); + groups = Collections.unmodifiableSet(user.getGroups()); + } + + @Override + public String getName() + { + return userName; + } + + public String getGecos() + { + return gecos; + } + + public String getHomeDir() + { + return homeDir; + } + + public String getShell() + { + return shell; + } + + public int getUid() + { + return uid; + } + + public int getGid() + { + return gid; + } + + public Set getGroups() + { + return groups; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java new file mode 100644 index 0000000000..77fa4c60da --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java @@ -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. + */ + +package org.apache.atlas.notification.rest.web.security; + +import org.springframework.security.authentication.jaas.AuthorityGranter; + +import java.security.Principal; +import java.util.Collections; +import java.util.Set; + +public class UserAuthorityGranter implements AuthorityGranter { + + @Override + public Set grant(Principal principal) { + Collections.singleton("DATA_SCIENTIST"); + return null; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java new file mode 100644 index 0000000000..d00ae8f52e --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java @@ -0,0 +1,203 @@ +/** + * 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.atlas.notification.rest.web.service; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.listener.ActiveStateChangeHandler; +import org.apache.atlas.notification.rest.AtlasServerIdSelector; +import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.atlas.service.Service; +import org.apache.commons.configuration.Configuration; +import org.apache.curator.framework.recipes.leader.LeaderLatch; +import org.apache.curator.framework.recipes.leader.LeaderLatchListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + + +/** + * A service that implements leader election to determine whether this Atlas server is Active. + * + * The service implements leader election through Curator's + * {@link LeaderLatch} recipe. The service also implements {@link LeaderLatchListener} to get + * notified of changes to leadership state. Upon becoming leader, this instance is treated as the + * active Atlas instance and calls {@link ActiveStateChangeHandler}s to activate them. Conversely, + * on being removed from leadership, this instance is treated as a passive instance and calls + * {@link ActiveStateChangeHandler}s to deactivate them. + */ + +@Component +// +// This should be called the last, leaving it without the @Order(Integer.MAX_VALUE) will make it get +// called after all services have their start called. +public class ActiveInstanceElectorService implements Service, LeaderLatchListener { + private static final Logger LOG = LoggerFactory.getLogger(ActiveInstanceElectorService.class); + + private final Configuration configuration; + private final ServiceState serviceState; + private final ActiveInstanceState activeInstanceState; + private Set activeStateChangeHandlerProviders; + private List activeStateChangeHandlers; + private CuratorFactory curatorFactory; + private LeaderLatch leaderLatch; + private String serverId; + + /** + * Create a new instance of {@link ActiveInstanceElectorService} + * @param activeStateChangeHandlerProviders The list of registered {@link ActiveStateChangeHandler}s that + * must be called back on state changes. + * @throws AtlasException + */ + @Inject + ActiveInstanceElectorService(Configuration configuration, + Set activeStateChangeHandlerProviders, + CuratorFactory curatorFactory, ActiveInstanceState activeInstanceState, + ServiceState serviceState) { + this.configuration = configuration; + this.activeStateChangeHandlerProviders = activeStateChangeHandlerProviders; + this.activeStateChangeHandlers = new ArrayList<>(); + this.curatorFactory = curatorFactory; + this.activeInstanceState = activeInstanceState; + this.serviceState = serviceState; + } + + /** + * Join leader election on starting up. + * + * If Atlas High Availability configuration is disabled, this operation is a no-op. + * @throws AtlasException + */ + @Override + public void start() throws AtlasException { + if (!RestHAConfiguration.isHAEnabled(configuration)) { + LOG.info("HA is not enabled, no need to start leader election service"); + return; + } + cacheActiveStateChangeHandlers(); + serverId = AtlasServerIdSelector.selectServerId(configuration); + joinElection(); + } + + private void joinElection() { + LOG.info("Starting leader election for {}", serverId); + String zkRoot = RestHAConfiguration.getZookeeperProperties(configuration).getZkRoot(); + leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); + leaderLatch.addListener(this); + try { + leaderLatch.start(); + LOG.info("Leader latch started for {}.", serverId); + } catch (Exception e) { + LOG.info("Exception while starting leader latch for {}.", serverId, e); + } + } + + /** + * Leave leader election process and clean up resources on shutting down. + * + * If Atlas High Availability configuration is disabled, this operation is a no-op. + * @throws AtlasException + */ + @Override + public void stop() { + if (!RestHAConfiguration.isHAEnabled(configuration)) { + LOG.info("HA is not enabled, no need to stop leader election service"); + return; + } + try { + leaderLatch.close(); + curatorFactory.close(); + } catch (IOException e) { + LOG.error("Error closing leader latch", e); + } + } + + /** + * Call all registered {@link ActiveStateChangeHandler}s on being elected active. + * + * In addition, shared state information about this instance becoming active is updated + * using {@link ActiveInstanceState}. + */ + @Override + public void isLeader() { + LOG.warn("Server instance with server id {} is elected as leader", serverId); + serviceState.becomingActive(); + try { + for (ActiveStateChangeHandler handler : activeStateChangeHandlers) { + handler.instanceIsActive(); + } + activeInstanceState.update(serverId); + serviceState.setActive(); + } catch (Exception e) { + LOG.error("Got exception while activating", e); + notLeader(); + rejoinElection(); + } + } + + private void cacheActiveStateChangeHandlers() { + if (activeStateChangeHandlers.size()==0) { + activeStateChangeHandlers.addAll(activeStateChangeHandlerProviders); + + LOG.info("activeStateChangeHandlers(): before reorder: " + activeStateChangeHandlers); + + Collections.sort(activeStateChangeHandlers, new Comparator() { + @Override + public int compare(ActiveStateChangeHandler lhs, ActiveStateChangeHandler rhs) { + return Integer.compare(lhs.getHandlerOrder(), rhs.getHandlerOrder()); + } + }); + + LOG.info("activeStateChangeHandlers(): after reorder: " + activeStateChangeHandlers); + } + } + + private void rejoinElection() { + try { + leaderLatch.close(); + joinElection(); + } catch (IOException e) { + LOG.error("Error rejoining election", e); + } + } + + /** + * Call all registered {@link ActiveStateChangeHandler}s on becoming passive instance. + */ + @Override + public void notLeader() { + LOG.warn("Server instance with server id {} is removed as leader", serverId); + serviceState.becomingPassive(); + for (int idx = activeStateChangeHandlers.size() - 1; idx >= 0; idx--) { + try { + activeStateChangeHandlers.get(idx).instanceIsPassive(); + } catch (AtlasException e) { + LOG.error("Error while reacting to passive state.", e); + } + } + serviceState.setPassive(); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java new file mode 100644 index 0000000000..a22988de50 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java @@ -0,0 +1,146 @@ +/** + * 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.atlas.notification.rest.web.service; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.AtlasException; +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Id; +import org.apache.zookeeper.data.Stat; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * An object that encapsulates storing and retrieving state related to an Active Atlas server. + * + * The current implementation uses Zookeeper to store and read this state from. It does this + * under a read-write lock implemented using Curator's {@link InterProcessReadWriteLock} to + * provide for safety across multiple processes. + */ +@Component +public class ActiveInstanceState { + + private final Configuration configuration; + private final CuratorFactory curatorFactory; + + public static final String APACHE_ATLAS_ACTIVE_SERVER_INFO = "/active_server_info"; + + private static final Logger LOG = LoggerFactory.getLogger(ActiveInstanceState.class); + + /** + * Create a new instance of {@link ActiveInstanceState}. + * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} + * @throws AtlasException + */ + @Inject + public ActiveInstanceState(CuratorFactory curatorFactory) throws AtlasException { + this(ApplicationProperties.get(), curatorFactory); + } + + /** + * Create a new instance of {@link ActiveInstanceState}. + * @param configuration an instance of {@link Configuration} created from Atlas configuration + * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} + * @throws AtlasException + */ + public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory) { + this.configuration = configuration; + this.curatorFactory = curatorFactory; + } + + /** + * Update state of the active server instance. + * + * This method writes this instance's Server Address to a shared node in Zookeeper. + * This information is used by other passive instances to locate the current active server. + * @throws Exception + * @param serverId ID of this server instance + */ + public void update(String serverId) throws AtlasBaseException { + try { + CuratorFramework client = curatorFactory.clientInstance(); + RestHAConfiguration.ZookeeperProperties zookeeperProperties = + RestHAConfiguration.getZookeeperProperties(configuration); + String atlasServerAddress = RestHAConfiguration.getBoundAddressForId(configuration, serverId); + + List acls = new ArrayList(); + ACL parsedACL = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl(), + ZooDefs.Ids.OPEN_ACL_UNSAFE.get(0)); + acls.add(parsedACL); + + //adding world read permission + if (StringUtils.isNotEmpty(zookeeperProperties.getAcl())) { + ACL worldReadPermissionACL = new ACL(ZooDefs.Perms.READ, new Id("world", "anyone")); + acls.add(worldReadPermissionACL); + } + + Stat serverInfo = client.checkExists().forPath(getZnodePath(zookeeperProperties)); + if (serverInfo == null) { + client.create(). + withMode(CreateMode.EPHEMERAL). + withACL(acls). + forPath(getZnodePath(zookeeperProperties)); + } + client.setData().forPath(getZnodePath(zookeeperProperties), + atlasServerAddress.getBytes(Charset.forName("UTF-8"))); + } catch (Exception e) { + throw new AtlasBaseException(AtlasErrorCode.CURATOR_FRAMEWORK_UPDATE, e, "forPath: getZnodePath"); + } + } + + private String getZnodePath(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { + return zookeeperProperties.getZkRoot()+APACHE_ATLAS_ACTIVE_SERVER_INFO; + } + + /** + * Retrieve state of the active server instance. + * + * This method reads the active server location from the shared node in Zookeeper. + * @return the active server's address and port of form http://host-or-ip:port + */ + public String getActiveServerAddress() { + CuratorFramework client = curatorFactory.clientInstance(); + String serverAddress = null; + try { + RestHAConfiguration.ZookeeperProperties zookeeperProperties = + RestHAConfiguration.getZookeeperProperties(configuration); + byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); + serverAddress = new String(bytes, Charset.forName("UTF-8")); + } catch (Exception e) { + LOG.error("Error getting active server address", e); + } + return serverAddress; + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java new file mode 100644 index 0000000000..5f9fc840a8 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java @@ -0,0 +1,74 @@ +/** + * 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.atlas.notification.rest.web.service; + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import org.apache.commons.lang.StringUtils; +import org.apache.curator.framework.AuthInfo; +import org.apache.zookeeper.ZooDefs; +import org.apache.zookeeper.data.ACL; +import org.apache.zookeeper.data.Id; + +/** + * A class that parses configuration strings into Zookeeper ACL and Auth values. + */ +public class AtlasZookeeperSecurityProperties { + + public static ACL parseAcl(String aclString, ACL defaultAcl) { + if (StringUtils.isEmpty(aclString)) { + return defaultAcl; + } + return parseAcl(aclString); + } + + /** + * Get an {@link ACL} by parsing input string. + * @param aclString A string of the form scheme:id + * @return {@link ACL} with the perms set to {@link ZooDefs.Perms#ALL} and scheme and id + * taken from configuration values. + */ + public static ACL parseAcl(String aclString) { + String[] aclComponents = getComponents(aclString, "acl", "scheme:id"); + return new ACL(ZooDefs.Perms.ALL, new Id(aclComponents[0], aclComponents[1])); + } + + private static String[] getComponents(String securityString, String variableName, String formatExample) { + Preconditions.checkArgument(!StringUtils.isEmpty(securityString), + String.format("%s cannot be null or empty. " + + "Needs to be of form %s", variableName, formatExample)); + String[] aclComponents = securityString.split(":", 2); + if (aclComponents.length != 2) { + throw new IllegalArgumentException( + String.format("Invalid %s string. " + + "Needs to be of form %s", variableName, formatExample)); + } + return aclComponents; + } + + /** + * Get an {@link AuthInfo} by parsing input string. + * @param authString A string of the form scheme:authString + * @return {@link AuthInfo} with the scheme and auth taken from configuration values. + */ + public static AuthInfo parseAuth(String authString) { + String[] authComponents = getComponents(authString, "authString", "scheme:authString"); + return new AuthInfo(authComponents[0], authComponents[1].getBytes(Charsets.UTF_8)); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java new file mode 100644 index 0000000000..08fcf10a33 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java @@ -0,0 +1,202 @@ +/** + * 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.atlas.notification.rest.web.service; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Charsets; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.curator.framework.AuthInfo; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.framework.api.ACLProvider; +import org.apache.curator.framework.recipes.leader.LeaderLatch; +import org.apache.curator.framework.recipes.locks.InterProcessMutex; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.zookeeper.data.ACL; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.inject.Singleton; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +/** + * A factory to create objects related to Curator. + * + * Allows for stubbing in tests. + */ +@Singleton +@Component +public class CuratorFactory { + + private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); + + public static final String APACHE_ATLAS_LEADER_ELECTOR_PATH = "/leader_elector_path"; + public static final String SASL_SCHEME = "sasl"; + public static final String WORLD_SCHEME = "world"; + public static final String ANYONE_ID = "anyone"; + public static final String AUTH_SCHEME = "auth"; + public static final String DIGEST_SCHEME = "digest"; + public static final String IP_SCHEME = "ip"; + public static final String SETUP_LOCK = "/setup_lock"; + + private final Configuration configuration; + private CuratorFramework curatorFramework; + + /** + * Initializes the {@link CuratorFramework} that is used for all interaction with Zookeeper. + * @throws AtlasException + */ + public CuratorFactory() throws AtlasException { + this(ApplicationProperties.get()); + } + + public CuratorFactory(Configuration configuration) { + this.configuration = configuration; + initializeCuratorFramework(); + } + + @VisibleForTesting + protected void initializeCuratorFramework() { + RestHAConfiguration.ZookeeperProperties zookeeperProperties = + RestHAConfiguration.getZookeeperProperties(configuration); + CuratorFrameworkFactory.Builder builder = getBuilder(zookeeperProperties); + enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + curatorFramework = builder.build(); + curatorFramework.start(); + } + + @VisibleForTesting + void enhanceBuilderWithSecurityParameters(RestHAConfiguration.ZookeeperProperties zookeeperProperties, + CuratorFrameworkFactory.Builder builder) { + + ACLProvider aclProvider = getAclProvider(zookeeperProperties); + + AuthInfo authInfo = null; + if (zookeeperProperties.hasAuth()) { + authInfo = AtlasZookeeperSecurityProperties.parseAuth(zookeeperProperties.getAuth()); + } + + if (aclProvider != null) { + LOG.info("Setting up acl provider."); + builder.aclProvider(aclProvider); + if (authInfo != null) { + byte[] auth = authInfo.getAuth(); + LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), + getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); + builder.authorization(authInfo.getScheme(), auth); + } + } + } + + private String getCurrentUser() { + try { + return UserGroupInformation.getCurrentUser().getUserName(); + } catch (IOException ioe) { + return "unknown"; + } + } + + private ACLProvider getAclProvider(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { + ACLProvider aclProvider = null; + if (zookeeperProperties.hasAcl()) { + final ACL acl = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl()); + LOG.info("Setting ACL for id {} with scheme {} and perms {}.", + getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), + acl.getId().getScheme(), acl.getPerms()); + LOG.info("Current logged in user: {}", getCurrentUser()); + final List acls = Arrays.asList(acl); + aclProvider = new ACLProvider() { + @Override + public List getDefaultAcl() { + return acls; + } + + @Override + public List getAclForPath(String path) { + return acls; + } + }; + } + return aclProvider; + } + + private String getIdForLogging(String scheme, String id) { + if (scheme.equalsIgnoreCase(SASL_SCHEME) || + scheme.equalsIgnoreCase(IP_SCHEME)) { + return id; + } else if (scheme.equalsIgnoreCase(WORLD_SCHEME)) { + return ANYONE_ID; + } else if (scheme.equalsIgnoreCase(AUTH_SCHEME) || + scheme.equalsIgnoreCase(DIGEST_SCHEME)) { + return id.split(":")[0]; + } + return "unknown"; + } + + private CuratorFrameworkFactory.Builder getBuilder(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { + return CuratorFrameworkFactory.builder(). + connectString(zookeeperProperties.getConnectString()). + sessionTimeoutMs(zookeeperProperties.getSessionTimeout()). + retryPolicy(new ExponentialBackoffRetry( + zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); + } + + /** + * Cleanup resources related to {@link CuratorFramework}. + * + * After this call, no further calls to any curator objects should be done. + */ + public void close() { + curatorFramework.close(); + } + + /** + * Returns a pre-created instance of {@link CuratorFramework}. + * + * This method can be called any number of times to access the {@link CuratorFramework} used in the + * application. + * @return + */ + public CuratorFramework clientInstance() { + return curatorFramework; + } + + /** + * Create a new instance {@link LeaderLatch} + * @param serverId the ID used to register this instance with curator. + * This ID should typically be obtained using + * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} + * @param zkRoot the root znode under which the leader latch node is added. + * @return + */ + public LeaderLatch leaderLatchInstance(String serverId, String zkRoot) { + return new LeaderLatch(curatorFramework, zkRoot+APACHE_ATLAS_LEADER_ELECTOR_PATH, serverId); + } + + public InterProcessMutex lockInstance(String zkRoot) { + return new InterProcessMutex(curatorFramework, zkRoot+ SETUP_LOCK); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java new file mode 100644 index 0000000000..e3eb211aad --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java @@ -0,0 +1,113 @@ +/** + * 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.atlas.notification.rest.web.service; + +import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.exception.AtlasBaseException; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.thread.ExecutorThreadPool; +import org.eclipse.jetty.webapp.WebAppContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Date; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class EmbeddedServer { + public static final Logger LOG = LoggerFactory.getLogger(EmbeddedServer.class); + public static final String REST_DEFAULT_BIND_ADDRESS = "0.0.0.0"; + public static final Date SERVER_START_TIME = new Date(); + + protected final Server server; + + public EmbeddedServer(String host, int port, String path) throws IOException { + int queueSize = AtlasConfiguration.WEBSERVER_QUEUE_SIZE.getInt(); + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(queueSize); + int minThreads = AtlasConfiguration.WEBSERVER_MIN_THREADS.getInt(); + int maxThreads = AtlasConfiguration.WEBSERVER_MAX_THREADS.getInt(); + long keepAliveTime = AtlasConfiguration.WEBSERVER_KEEPALIVE_SECONDS.getLong(); + ThreadPoolExecutor executor = new ThreadPoolExecutor(maxThreads, maxThreads, keepAliveTime, TimeUnit.SECONDS, queue); + ExecutorThreadPool pool = new ExecutorThreadPool(executor, minThreads); + + server = new Server(pool); + Connector connector = getConnector(host, port); + server.addConnector(connector); + + WebAppContext application = getWebAppContext(path); + server.setHandler(application); + + } + + protected Connector getConnector(String host, int port) throws IOException { + HttpConfiguration http_config = new HttpConfiguration(); + // this is to enable large header sizes when Kerberos is enabled with AD + //final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); + final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); + http_config.setResponseHeaderSize(bufferSize); + http_config.setRequestHeaderSize(bufferSize); + http_config.setSendServerVersion(false); + + ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config)); + connector.setPort(port); + connector.setHost(host); + return connector; + } + + protected WebAppContext getWebAppContext(String path) { + WebAppContext application = new WebAppContext(path, "/"); + application.setClassLoader(Thread.currentThread().getContextClassLoader()); + // Disable directory listing + application.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); + return application; + } + + public void start() throws AtlasBaseException { + try { + server.start(); + server.join(); + } catch (Exception e) { + throw new AtlasBaseException(AtlasErrorCode.EMBEDDED_SERVER_START, e); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + LOG.warn("Error during shutdown", e); + } + } + + public static EmbeddedServer newServer(String host, int port, String path, boolean secure) + throws IOException { + if (secure) { + return new SecureEmbeddedServer(host, port, path); + } else { + return new EmbeddedServer(host, port, path); + } + } + +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java new file mode 100755 index 0000000000..649dadca07 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java @@ -0,0 +1,351 @@ +/** + * 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.atlas.notification.rest.web.service; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.AtlasException; +import org.apache.commons.lang.StringUtils; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_DEFAULT_PROTOCOL; +import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_EXCLUDE_CIPHER_SUITES; +import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_EXCLUDE_PROTOCOLS; +import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_ENABLED_ALGORITHMS; +import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_ENABLED_PROTOCOLS; +import static org.apache.atlas.security.SecurityProperties.CLIENT_AUTH_KEY; +import static org.apache.atlas.security.SecurityProperties.DEFATULT_TRUSTORE_FILE_LOCATION; +import static org.apache.atlas.security.SecurityProperties.DEFAULT_CIPHER_SUITES; +import static org.apache.atlas.security.SecurityProperties.DEFAULT_EXCLUDE_PROTOCOLS; +import static org.apache.atlas.security.SecurityProperties.DEFAULT_KEYSTORE_FILE_LOCATION; +import static org.apache.atlas.security.SecurityProperties.KEYSTORE_FILE_KEY; +import static org.apache.atlas.security.SecurityProperties.KEYSTORE_PASSWORD_KEY; +import static org.apache.atlas.security.SecurityProperties.KEYSTORE_TYPE; +import static org.apache.atlas.security.SecurityProperties.SERVER_CERT_PASSWORD_KEY; +import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_FILE_KEY; +import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_PASSWORD_KEY; +import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_TYPE; +import static org.apache.atlas.security.SecurityUtil.getPassword; + + +/** + * This is a jetty server which requires client auth via certificates. + */ +public class SecureEmbeddedServer extends EmbeddedServer { + + private static final Logger LOG = LoggerFactory.getLogger(SecureEmbeddedServer.class); + + public static final String ATLAS_KEYSTORE_FILE_TYPE_DEFAULT = "jks"; + public static final String ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks"; + public static final String ATLAS_TLS_CONTEXT_ALGO_TYPE = "TLS"; + public static final String ATLAS_TLS_KEYMANAGER_DEFAULT_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); + public static final String ATLAS_TLS_TRUSTMANAGER_DEFAULT_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); + + + public SecureEmbeddedServer(String host, int port, String path) throws IOException { + super(host, port, path); + } + + @Override + protected Connector getConnector(String host, int port) throws IOException { + org.apache.commons.configuration.Configuration config = getConfiguration(); + + SSLContext sslContext = getSSLContext(); + if (sslContext != null) { + SSLContext.setDefault(sslContext); + } + + SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(config.getString(KEYSTORE_FILE_KEY, + System.getProperty(KEYSTORE_FILE_KEY, DEFAULT_KEYSTORE_FILE_LOCATION))); + sslContextFactory.setKeyStorePassword(getPassword(config, KEYSTORE_PASSWORD_KEY)); + sslContextFactory.setKeyManagerPassword(getPassword(config, SERVER_CERT_PASSWORD_KEY)); + sslContextFactory.setKeyStoreType(config.getString(KEYSTORE_TYPE, ATLAS_KEYSTORE_FILE_TYPE_DEFAULT)); + sslContextFactory.setTrustStorePath(config.getString(TRUSTSTORE_FILE_KEY, + System.getProperty(TRUSTSTORE_FILE_KEY, DEFATULT_TRUSTORE_FILE_LOCATION))); + sslContextFactory.setTrustStorePassword(getPassword(config, TRUSTSTORE_PASSWORD_KEY)); + sslContextFactory.setTrustStoreType(config.getString(TRUSTSTORE_TYPE, ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT)); + sslContextFactory.setWantClientAuth(config.getBoolean(CLIENT_AUTH_KEY, Boolean.getBoolean(CLIENT_AUTH_KEY))); + + List cipherList = config.getList(ATLAS_SSL_EXCLUDE_CIPHER_SUITES, DEFAULT_CIPHER_SUITES); + sslContextFactory.setExcludeCipherSuites(cipherList.toArray(new String[cipherList.size()])); + sslContextFactory.setRenegotiationAllowed(false); + + String[] excludedProtocols = config.containsKey(ATLAS_SSL_EXCLUDE_PROTOCOLS) ? + config.getStringArray(ATLAS_SSL_EXCLUDE_PROTOCOLS) : DEFAULT_EXCLUDE_PROTOCOLS; + if (excludedProtocols != null && excludedProtocols.length > 0) { + sslContextFactory.addExcludeProtocols(excludedProtocols); + } + + List rawCipherValues = config.getList(ATLAS_SSL_ENABLED_ALGORITHMS); + List enabledCiphersList = Collections.emptyList(); + + if (rawCipherValues != null && !rawCipherValues.isEmpty()) { + List parsedCiphers = new ArrayList<>(); + + for (Object rawCipherValue : rawCipherValues) { + if (rawCipherValue == null) { + continue; + } + + String value = String.valueOf(rawCipherValue).trim(); + if (value.isEmpty()) { + continue; + } + + parsedCiphers.addAll(Arrays.asList(value.split("\\s*[:,;\\s]+\\s*"))); + } + + enabledCiphersList = parsedCiphers; + } + + if (!enabledCiphersList.isEmpty()) { + sslContextFactory.setIncludeCipherSuites(enabledCiphersList.toArray(new String[enabledCiphersList.size()])); + } + + String[] enabledProtocols = config.containsKey(ATLAS_SSL_ENABLED_PROTOCOLS) ? + config.getStringArray(ATLAS_SSL_ENABLED_PROTOCOLS) : ATLAS_SSL_DEFAULT_PROTOCOL; + + if (enabledProtocols != null && enabledProtocols.length > 0) { + sslContextFactory.setIncludeProtocols(enabledProtocols); + } + + // SSL HTTP Configuration + // HTTP Configuration + HttpConfiguration http_config = new HttpConfiguration(); + http_config.setSecureScheme("https"); + final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); + http_config.setSecurePort(port); + http_config.setRequestHeaderSize(bufferSize); + http_config.setResponseHeaderSize(bufferSize); + http_config.setSendServerVersion(false); + http_config.setSendDateHeader(false); + + HttpConfiguration https_config = new HttpConfiguration(http_config); + https_config.addCustomizer(new SecureRequestCustomizer()); + https_config.setSendServerVersion(false); + + // SSL Connector + ServerConnector sslConnector = new ServerConnector(server, + new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), + new HttpConnectionFactory(https_config)); + sslConnector.setPort(port); + server.addConnector(sslConnector); + + return sslConnector; + } + + + /** + * Returns the application configuration. + * @return + */ + protected org.apache.commons.configuration.Configuration getConfiguration() { + try { + return ApplicationProperties.get(); + } catch (AtlasException e) { + throw new RuntimeException("Unable to load configuration: " + ApplicationProperties.APPLICATION_PROPERTIES); + } + } + + /** + * creates the SSLContext using the available keystore and truststore. + * @return + */ + private SSLContext getSSLContext() { + KeyManager[] kmList = getKeyManagers(); + TrustManager[] tmList = getTrustManagers(); + SSLContext sslContext = null; + if (tmList != null) { + try { + sslContext = SSLContext.getInstance(ATLAS_TLS_CONTEXT_ALGO_TYPE); + sslContext.init(kmList, tmList, new SecureRandom()); + } catch (NoSuchAlgorithmException e) { + LOG.error("SSL algorithm is not available in the environment. Reason: " + e.toString()); + } catch (KeyManagementException e) { + LOG.error("Unable to initials the SSLContext. Reason: " + e.toString()); + } + } + return sslContext; + } + + /** + * Generating the KeyManager using the provided keystore. + * @return + */ + private KeyManager[] getKeyManagers() { + KeyManager[] kmList = null; + try { + + String keyStoreFile = getConfiguration().getString(KEYSTORE_FILE_KEY, + System.getProperty(KEYSTORE_FILE_KEY, DEFAULT_KEYSTORE_FILE_LOCATION)); + String keyStoreFilepwd = getPassword(getConfiguration(), KEYSTORE_PASSWORD_KEY); + + if (StringUtils.isNotEmpty(keyStoreFile) && StringUtils.isNotEmpty(keyStoreFilepwd)) { + InputStream in = null; + + try { + in = getFileInputStream(keyStoreFile); + + if (in != null) { + KeyStore keyStore = KeyStore.getInstance(getConfiguration().getString(KEYSTORE_TYPE, ATLAS_KEYSTORE_FILE_TYPE_DEFAULT)); + + keyStore.load(in, keyStoreFilepwd.toCharArray()); + + KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ATLAS_TLS_KEYMANAGER_DEFAULT_ALGO_TYPE); + + keyManagerFactory.init(keyStore, keyStoreFilepwd.toCharArray()); + + kmList = keyManagerFactory.getKeyManagers(); + } else { + LOG.error("Unable to obtain keystore from file [" + keyStoreFile + "]"); + } + } catch (KeyStoreException e) { + LOG.error("Unable to obtain from KeyStore :" + e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + LOG.error("SSL algorithm is NOT available in the environment", e); + } catch (CertificateException e) { + LOG.error("Unable to obtain the requested certification ", e); + } catch (FileNotFoundException e) { + LOG.error("Unable to find the necessary TLS Keystore Files", e); + } catch (IOException e) { + LOG.error("Unable to read the necessary TLS Keystore Files", e); + } catch (UnrecoverableKeyException e) { + LOG.error("Unable to recover the key from keystore", e); + } finally { + close(in, keyStoreFile); + } + } + + } catch (IOException exception) { + LOG.error(exception.getMessage()); + } + return kmList; + } + + /** + * Generating the TrustManager using the provided truststore + * @return + */ + private TrustManager[] getTrustManagers() { + TrustManager[] tmList = null; + try { + String truststoreFile = getConfiguration().getString(TRUSTSTORE_FILE_KEY, + System.getProperty(TRUSTSTORE_FILE_KEY, DEFATULT_TRUSTORE_FILE_LOCATION)); + String trustStoreFilepwd = getPassword(getConfiguration(), TRUSTSTORE_PASSWORD_KEY); + + if (StringUtils.isNotEmpty(truststoreFile) && StringUtils.isNotEmpty(trustStoreFilepwd)) { + InputStream in = null; + + try { + in = getFileInputStream(truststoreFile); + + if (in != null) { + KeyStore trustStore = KeyStore.getInstance(getConfiguration().getString(TRUSTSTORE_TYPE, ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT)); + + trustStore.load(in, trustStoreFilepwd.toCharArray()); + + TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(ATLAS_TLS_TRUSTMANAGER_DEFAULT_ALGO_TYPE); + + trustManagerFactory.init(trustStore); + + tmList = trustManagerFactory.getTrustManagers(); + } else { + LOG.error("Unable to obtain truststore from file [" + truststoreFile + "]"); + } + } catch (KeyStoreException e) { + LOG.error("Unable to obtain from KeyStore", e); + } catch (NoSuchAlgorithmException e) { + LOG.error("SSL algorithm is NOT available in the environment :" + e.getMessage(), e); + } catch (CertificateException e) { + LOG.error("Unable to obtain the requested certification :" + e.getMessage(), e); + } catch (FileNotFoundException e) { + LOG.error("Unable to find the necessary TLS TrustStore File:" + truststoreFile, e); + } catch (IOException e) { + LOG.error("Unable to read the necessary TLS TrustStore Files :" + truststoreFile, e); + } finally { + close(in, truststoreFile); + } + } + + } catch (IOException exception) { + LOG.error(exception.getMessage()); + } + return tmList; + } + + private InputStream getFileInputStream(String fileName) throws IOException { + InputStream in = null; + if (StringUtils.isNotEmpty(fileName)) { + File f = new File(fileName); + if (f.exists()) { + in = new FileInputStream(f); + } else { + in = ClassLoader.getSystemResourceAsStream(fileName); + } + } + return in; + } + + /** + * Closing file-stream. + * @param str + * @param filename + */ + private void close(InputStream str, String filename) { + if (str != null) { + try { + str.close(); + } catch (IOException excp) { + LOG.error("Error while closing file: [" + filename + "]", excp); + } + } + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java new file mode 100644 index 0000000000..60373f5036 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java @@ -0,0 +1,116 @@ +/** + * 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.atlas.notification.rest.web.service; + +import com.google.common.base.Preconditions; +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.inject.Singleton; + +import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; + +/** + * A class that maintains the state of this instance. + * + * The states are maintained at a granular level, including in-transition states. The transitions are + * directed by {@link ActiveInstanceElectorService}. + */ +@Singleton +@Component +public class ServiceState { + private static final Logger LOG = LoggerFactory.getLogger(ServiceState.class); + + public enum ServiceStateValue { + ACTIVE, + PASSIVE, + BECOMING_ACTIVE, + BECOMING_PASSIVE, + MIGRATING + } + + private Configuration configuration; + private volatile ServiceStateValue state; + + public ServiceState() throws AtlasException { + this(ApplicationProperties.get()); + } + + public ServiceState(Configuration configuration) { + this.configuration = configuration; + + state = !RestHAConfiguration.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; + + if(!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { + state = ServiceStateValue.MIGRATING; + } + } + + public ServiceStateValue getState() { + return state; + } + + public void becomingActive() { + LOG.warn("Instance becoming active from {}", state); + setState(ServiceStateValue.BECOMING_ACTIVE); + } + + private void setState(ServiceStateValue newState) { + Preconditions.checkState(RestHAConfiguration.isHAEnabled(configuration), "Cannot change state as requested, as HA is not enabled for this instance."); + + state = newState; + + } + + public void setActive() { + LOG.warn("Instance is active from {}", state); + setState(ServiceStateValue.ACTIVE); + } + + public void becomingPassive() { + LOG.warn("Instance becoming passive from {}", state); + setState(ServiceStateValue.BECOMING_PASSIVE); + } + + public void setPassive() { + LOG.warn("Instance is passive from {}", state); + setState(ServiceStateValue.PASSIVE); + } + + public boolean isInstanceInTransition() { + ServiceStateValue state = getState(); + return state == ServiceStateValue.BECOMING_ACTIVE + || state == ServiceStateValue.BECOMING_PASSIVE; + } + + public void setMigration() { + LOG.warn("Instance in {}", state); + setState(ServiceStateValue.MIGRATING); + } + + public boolean isInstanceInMigration() { + return getState() == ServiceStateValue.MIGRATING; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java new file mode 100644 index 0000000000..1e2597117a --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java @@ -0,0 +1,43 @@ +/* + * 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.atlas.notification.rest.web.service; + +import org.apache.atlas.notification.rest.web.dao.UserDao; +import org.apache.atlas.notification.rest.web.model.User; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; + +import javax.inject.Inject; + +@Service +public class UserService implements UserDetailsService { + + private final UserDao userDao; + + @Inject + public UserService(UserDao userDao) { + this.userDao = userDao; + } + + @Override + public User loadUserByUsername(final String username) + throws UsernameNotFoundException { + return userDao.loadUserByUsername(username); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java new file mode 100644 index 0000000000..f9d4e10589 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.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.atlas.notification.rest.web.setup; + + +import org.apache.atlas.notification.rest.web.listeners.LoginProcessor; +import org.springframework.web.context.ContextLoaderListener; + +import javax.servlet.ServletContextEvent; + +public class KerberosAwareListener extends ContextLoaderListener { + @Override + public void contextInitialized(ServletContextEvent event) { + LoginProcessor loginProcessor = new LoginProcessor(); + loginProcessor.login(); + + super.contextInitialized(event); + } +} diff --git a/rest-notification-webapp/src/main/resources/rest-notification-buildinfo.properties b/rest-notification-webapp/src/main/resources/rest-notification-buildinfo.properties new file mode 100755 index 0000000000..2404f8fb93 --- /dev/null +++ b/rest-notification-webapp/src/main/resources/rest-notification-buildinfo.properties @@ -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. +# + +###################### +project.name=${pom.parent.name} +project.description=${pom.parent.description} +build.user=${user.name} +build.epoch=${timestamp} +project.version=${pom.version} +build.version=${pom.version} +vc.revision=${buildNumber} +vc.source.url=${scm.connection} +###################### diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/applicationContext.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/applicationContext.xml new file mode 100644 index 0000000000..2ecd8d1199 --- /dev/null +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/applicationContext.xml @@ -0,0 +1,23 @@ + + + + + + + + + \ No newline at end of file diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 0000000000..df65d79e25 --- /dev/null +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,89 @@ + + + + + Extractor Web Application + + contextConfigLocation + WEB-INF/applicationContext.xml + + + + rest-notification-servlet + + com.sun.jersey.spi.spring.container.servlet.SpringServlet + + + com.sun.jersey.config.property.packages + org.apache.atlas.notification.rest + + + com.sun.jersey.api.json.POJOMappingFeature + true + + 1 + + + + rest-notification-servlet + /rest/* + + + + springSecurityFilterChain + org.springframework.web.filter.DelegatingFilterProxy + + + + springSecurityFilterChain + /* + + + + org.springframework.web.context.request.RequestContextListener + + + + org.apache.atlas.notification.rest.web.setup.KerberosAwareListener + + + + AuditFilter + org.apache.atlas.notification.rest.web.filters.AuditFilter + + + + AuditFilter + /* + + + + HeaderFilter + org.apache.atlas.notification.rest.web.filters.AtlasHeaderFilter + + + + HeaderFilter + /api/atlas/admin/status + + + + From 1d7371f779565595080276c3354f47fa3d826187 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Mon, 13 Apr 2026 16:48:50 +0530 Subject: [PATCH 02/22] Resolving build error failure for rest-notification module,Registered rest-notification-webapp in parent pom, replacing the old private SNAPSHOT to map with parent,updated from jersy2->jersy1, Removed Servlet 4.0-only methods that are not on ServletContext in Servlet 3.1 (they were breaking @Override) etc --- pom.xml | 1 + rest-notification-webapp/pom.xml | 136 ++++++++---------- .../rest/web/filters/NullServletContext.java | 36 ----- .../src/main/webapp/WEB-INF/web.xml | 2 +- 4 files changed, 62 insertions(+), 113 deletions(-) diff --git a/pom.xml b/pom.xml index 8d27e11ed6..f03c967144 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,7 @@ notification plugin-classloader repository + rest-notification-webapp server-api test-tools tools/atlas-index-repair diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml index d056daf729..9126f3dfd1 100644 --- a/rest-notification-webapp/pom.xml +++ b/rest-notification-webapp/pom.xml @@ -1,3 +1,4 @@ + - + 4.0.0 org.apache.atlas apache-atlas - 2.4.0.7.3.2.10000-SNAPSHOT + 3.0.0-SNAPSHOT - org.apache.atlas + rest-notification-webapp war @@ -33,6 +34,7 @@ org.slf4j log4j-over-slf4j + ${slf4j.version} ch.qos.logback @@ -45,23 +47,20 @@ junit junit - 4.11 + ${junit.version} test org.eclipse.jetty jetty-server - ${jetty.version} org.eclipse.jetty jetty-webapp - ${jetty.version} org.springframework spring-web - ${spring.version} org.springframework @@ -70,12 +69,12 @@ org.aspectj aspectjrt - ${aspectjrt.version} + 1.8.9 org.aspectj aspectjweaver - ${aspectjweaver.version} + 1.8.9 org.apache.atlas @@ -96,11 +95,7 @@ org.apache.atlas atlas-authorization - ${version} - - - org.testng - testng + ${project.version} org.springframework.security @@ -129,7 +124,7 @@ org.apache.commons commons-configuration2 - ${commons-configuration2.version} + ${commons-conf2.version} org.apache.commons @@ -144,6 +139,12 @@ ${commons-text.version} + + commons-lang + commons-lang + 2.6 + + com.googlecode.json-simple json-simple @@ -153,91 +154,74 @@ org.kohsuke libpam4j + 1.11 + - org.glassfish.jersey.core + com.sun.jersey jersey-client - ${jersey2.version} + + + com.sun.jersey + jersey-core - org.glassfish.jersey.core + com.sun.jersey jersey-server - ${jersey2.version} - org.glassfish.jersey.core - jersey-common - ${jersey2.version} + com.sun.jersey + jersey-servlet - org.glassfish.jersey.ext - jersey-spring5 - ${jersey2.version} + com.sun.jersey.contribs + jersey-multipart - org.glassfish.jersey.containers - jersey-container-servlet - ${jersey2.version} + com.sun.jersey.contribs + jersey-spring - org.glassfish.jersey.media - jersey-media-multipart - ${jersey2.version} + com.sun.jersey + jersey-json + ${jersey.version} + + javax.servlet + javax.servlet-api + provided + - - - - maven-clean-plugin - 3.1.0 - - - - maven-resources-plugin - 3.0.2 - - - maven-compiler-plugin - ${maven-compiler-version} - - - maven-surefire-plugin - 2.22.1 - - - maven-war-plugin - 3.2.2 - - true - true - - - - true - - - - - - maven-install-plugin - 2.5.2 - - - maven-deploy-plugin - 2.8.2 - - - - - + + + com.github.ekryd.sortpom + sortpom-maven-plugin + + true + + + + org.apache.maven.plugins + maven-war-plugin + + true + true + + + true + + + + + diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java index c17a2fa571..452f911707 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java @@ -74,37 +74,6 @@ public String getVirtualServerName() { return null; } - @Override - public int getSessionTimeout() { - return 0; - } - - @Override - public void setSessionTimeout(int i) { - - } - - @Override - public String getRequestCharacterEncoding() { - return ""; - } - - @Override - public void setRequestCharacterEncoding(String s) { - - } - - @Override - public String getResponseCharacterEncoding() { - return ""; - } - - @Override - public void setResponseCharacterEncoding(String s) { - - } - - public SessionCookieConfig getSessionCookieConfig() { return null; } @@ -287,11 +256,6 @@ public ServletRegistration.Dynamic addServlet( return null; } - @Override - public ServletRegistration.Dynamic addJspFile(String s, String s1) { - return null; - } - public ServletRegistration.Dynamic addServlet( String servletName, Servlet servlet) { diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml index df65d79e25..ddf60b1221 100644 --- a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -20,7 +20,7 @@ --> - Extractor Web Application + Atlas Rest Notification Web Application contextConfigLocation WEB-INF/applicationContext.xml From b3359130afba741c2e60b9be7ea8d32798d61fc5 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 16 Apr 2026 12:09:20 +0530 Subject: [PATCH 03/22] Adding server-common module remove identical and nearly identical classes from webapp and rest-notification webapp, updated imports to use server-common module,updated paren pom and xml wiring's etc. --- pom.xml | 7 + rest-notification-webapp/pom.xml | 5 + .../atlas/notification/rest/Servlets.java | 223 ---------- .../notification/rest/web/dao/UserDao.java | 2 +- .../filters/AtlasAuthenticationFilter.java | 6 +- .../filters/AtlasCSRFPreventionFilter.java | 3 + .../AtlasKnoxSSOAuthenticationFilter.java | 3 + .../filters/AtlasResponseRequestWrapper.java | 33 -- .../rest/web/filters/AuditFilter.java | 4 +- .../rest/web/filters/RestUtil.java | 108 ----- .../filters/SSOAuthenticationProperties.java | 78 ---- .../rest/web/resources/AdminResource.java | 2 +- .../rest/web/rest/NotificationREST.java | 2 +- .../AtlasADAuthenticationProvider.java | 2 + .../AtlasAuthenticationException.java | 31 -- .../AtlasAuthenticationFailureHandler.java | 55 --- .../security/AtlasAuthenticationProvider.java | 2 + .../AtlasLdapAuthenticationProvider.java | 2 + .../AtlasPamAuthenticationProvider.java | 3 + .../web/security/AtlasSecurityConfig.java | 9 +- .../rest/web/security/PamLoginModule.java | 2 + .../rest/web/security/PamPrincipal.java | 84 ---- .../src/main/webapp/WEB-INF/web.xml | 2 +- server-common/pom.xml | 110 +++++ .../server/common}/LocalServletRequest.java | 3 +- .../AtlasAuthenticationEntryPoint.java | 6 +- ...lasDelegatingAuthenticationEntryPoint.java | 11 +- .../common}/filters/AtlasHeaderFilter.java | 11 +- .../filters/AtlasResponseRequestWrapper.java | 2 +- .../server/common}/filters/HeadersUtil.java | 39 +- .../common}/filters/NullServletContext.java | 95 ++--- .../server/common}/filters/RestUtil.java | 33 +- .../filters/SSOAuthenticationProperties.java | 4 +- .../AtlasAuthenticationException.java | 2 +- .../AtlasAuthenticationFailureHandler.java | 8 +- .../AtlasAuthenticationSuccessHandler.java | 22 +- .../server/common}/security/PamPrincipal.java | 19 +- .../security/UserAuthorityGranter.java | 3 +- .../server/common/util}/DateTimeHelper.java | 19 +- .../atlas/server/common}/util/Servlets.java | 41 +- webapp/pom.xml | 5 + .../org/apache/atlas/LocalServletRequest.java | 400 ------------------ .../org/apache/atlas/web/dao/UserDao.java | 2 +- .../AtlasAuthenticationEntryPoint.java | 57 --- .../filters/AtlasAuthenticationFilter.java | 9 +- .../filters/AtlasCSRFPreventionFilter.java | 3 + ...lasDelegatingAuthenticationEntryPoint.java | 58 --- .../atlas/web/filters/AtlasHeaderFilter.java | 50 --- .../AtlasKnoxSSOAuthenticationFilter.java | 4 + .../apache/atlas/web/filters/AuditFilter.java | 4 +- .../apache/atlas/web/filters/HeadersUtil.java | 108 ----- .../atlas/web/filters/NullServletContext.java | 236 ----------- .../atlas/web/resources/AdminResource.java | 2 +- .../web/resources/DataSetLineageResource.java | 2 +- .../atlas/web/resources/EntityResource.java | 2 +- .../atlas/web/resources/LineageResource.java | 2 +- .../resources/MetadataDiscoveryResource.java | 2 +- .../atlas/web/resources/TypesResource.java | 2 +- .../apache/atlas/web/rest/DiscoveryREST.java | 2 +- .../org/apache/atlas/web/rest/EntityREST.java | 2 +- .../apache/atlas/web/rest/GlossaryREST.java | 2 +- .../atlas/web/rest/IndexRecoveryREST.java | 4 +- .../apache/atlas/web/rest/LineageREST.java | 2 +- .../atlas/web/rest/NotificationREST.java | 2 +- .../atlas/web/rest/RelationshipREST.java | 2 +- .../org/apache/atlas/web/rest/TypesREST.java | 2 +- .../AtlasADAuthenticationProvider.java | 2 + .../security/AtlasAuthenticationProvider.java | 2 + .../AtlasAuthenticationSuccessHandler.java | 70 --- .../AtlasLdapAuthenticationProvider.java | 2 + .../AtlasPamAuthenticationProvider.java | 3 + .../web/security/AtlasSecurityConfig.java | 11 +- .../atlas/web/security/PamLoginModule.java | 2 + .../web/security/UserAuthorityGranter.java | 33 -- .../atlas/web/servlets/AtlasHttpServlet.java | 4 +- .../atlas/web/util/AtlasJsonProvider.java | 2 + .../apache/atlas/web/util/DateTimeHelper.java | 53 --- webapp/src/main/resources/spring-security.xml | 6 +- webapp/src/main/webapp/WEB-INF/web.xml | 2 +- .../org/apache/atlas/web/dao/UserDaoTest.java | 2 +- .../AtlasAuthenticationEntryPointTest.java | 2 + .../AtlasAuthenticationFilterTest.java | 5 +- .../web/filters/AtlasHeaderFilterTest.java | 4 + .../AtlasKnoxSSOAuthenticationFilterTest.java | 2 + .../atlas/web/filters/HeaderUtilsTest.java | 3 + .../atlas/web/filters/RestUtilTest.java | 2 + .../integration/EntityV2JerseyResourceIT.java | 2 + .../web/resources/AdminResourceTest.java | 2 +- .../apache/atlas/web/rest/EntityRESTTest.java | 2 +- .../atlas/web/rest/RelationshipRESTTest.java | 2 +- .../apache/atlas/web/rest/TypesRESTTest.java | 2 +- .../AtlasADAuthenticationProviderTest.java | 2 + ...AtlasAuthenticationFailureHandlerTest.java | 2 + .../AtlasAuthenticationProviderTest.java | 2 + ...AtlasAuthenticationSuccessHandlerTest.java | 2 + .../AtlasPamAuthenticationProviderTest.java | 3 + .../web/security/AtlasSecurityConfigTest.java | 7 +- .../web/security/FileAuthenticationTest.java | 2 + .../web/security/PamLoginModuleTest.java | 2 + .../atlas/web/security/PamPrincipalTest.java | 2 + .../security/UserAuthorityGranterTest.java | 2 + .../atlas/web/util/DateTimeHelperTest.java | 2 + .../apache/atlas/web/util/ServletsTest.java | 2 + .../test/resources/test-spring-security.xml | 6 +- 104 files changed, 405 insertions(+), 1915 deletions(-) delete mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java create mode 100644 server-common/pom.xml rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest => server-common/src/main/java/org/apache/atlas/server/common}/LocalServletRequest.java (99%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasAuthenticationEntryPoint.java (97%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasDelegatingAuthenticationEntryPoint.java (89%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasHeaderFilter.java (97%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasResponseRequestWrapper.java (95%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/HeadersUtil.java (88%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/NullServletContext.java (87%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/RestUtil.java (89%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/SSOAuthenticationProperties.java (95%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasAuthenticationException.java (95%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasAuthenticationFailureHandler.java (89%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasAuthenticationSuccessHandler.java (82%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/PamPrincipal.java (83%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/security/UserAuthorityGranter.java (95%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest => server-common/src/main/java/org/apache/atlas/server/common/util}/DateTimeHelper.java (79%) mode change 100755 => 100644 rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/util/Servlets.java (88%) mode change 100755 => 100644 delete mode 100644 webapp/src/main/java/org/apache/atlas/LocalServletRequest.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/filters/AtlasDelegatingAuthenticationEntryPoint.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java delete mode 100755 webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java diff --git a/pom.xml b/pom.xml index f03c967144..a4d6accc78 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ repository rest-notification-webapp server-api + server-common test-tools tools/atlas-index-repair tools/classification-updater @@ -566,6 +567,12 @@ ${project.version} + + org.apache.atlas + server-common + ${project.version} + + org.apache.atlas sqoop-bridge diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml index 9126f3dfd1..7885ca179a 100644 --- a/rest-notification-webapp/pom.xml +++ b/rest-notification-webapp/pom.xml @@ -80,6 +80,11 @@ org.apache.atlas atlas-intg + + + org.apache.atlas + server-common + com.google.guava guava diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java deleted file mode 100755 index 686b9ff0b1..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/Servlets.java +++ /dev/null @@ -1,223 +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.atlas.notification.rest; - -import com.fasterxml.jackson.databind.node.ObjectNode; -import org.apache.atlas.AtlasClient; -import org.apache.atlas.AtlasConfiguration; -import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.utils.AtlasJson; -import org.apache.atlas.utils.ParamChecker; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.web.util.UriUtils; - -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.io.IOException; -import java.io.StringWriter; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Utility functions for dealing with servlets. - */ -public final class Servlets { - - private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); - - private Servlets() { - /* singleton */ - } - - public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; - public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; - - private static final int QUERY_PARAM_MAX_LENGTH = AtlasConfiguration.QUERY_PARAM_MAX_LENGTH.getInt(); - - /** - * Returns the user of the given request. - * - * @param httpRequest an HTTP servlet request - * @return the user - */ - public static String getUserFromRequest(HttpServletRequest httpRequest) { - String user = httpRequest.getRemoteUser(); - if (!StringUtils.isEmpty(user)) { - return user; - } - - user = httpRequest.getParameter("user.name"); // available in query-param - if (!StringUtils.isEmpty(user)) { - return user; - } - - user = httpRequest.getHeader("Remote-User"); // backwards-compatibility - if (!StringUtils.isEmpty(user)) { - return user; - } - - user = getDoAsUser(httpRequest); - if (!StringUtils.isEmpty(user)) { - return user; - } - - return null; - } - - private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - private static final String DO_AS = "doAs"; - - public static String getDoAsUser(HttpServletRequest request) { - if (StringUtils.isNoneEmpty(request.getQueryString())) { - List list = URLEncodedUtils.parse(request.getQueryString(), UTF8_CHARSET); - if (list != null) { - for (NameValuePair nv : list) { - if (DO_AS.equals(nv.getName())) { - return nv.getValue(); - } - } - } - } - return null; - } - - /** - * Returns the URI of the given request. - * - * @param httpRequest an HTTP servlet request - * @return the URI, including the query string - */ - public static String getRequestURI(HttpServletRequest httpRequest) { - final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURI()); - if (httpRequest.getQueryString() != null) { - url.append('?').append(httpRequest.getQueryString()); - } - - return url.toString(); - } - - /** - * Returns the full URL of the given request. - * - * @param httpRequest an HTTP servlet request - * @return the full URL, including the query string - */ - public static String getRequestURL(HttpServletRequest httpRequest) { - final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURL()); - if (httpRequest.getQueryString() != null) { - url.append('?').append(httpRequest.getQueryString()); - } - - return url.toString(); - } - - public static Response getErrorResponse(AtlasBaseException e) { - String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); - Response response = getErrorResponse(message, e.getAtlasErrorCode().getHttpCode()); - - return response; - } - - public static Response getErrorResponse(Throwable e, Response.Status status) { - String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); - Response response = getErrorResponse(message, status); - - return response; - } - - public static Response getErrorResponse(String message, Response.Status status) { - Object errorEntity = escapeJsonString(message); - ObjectNode errorJson = AtlasJson.createV1ObjectNode(AtlasClient.ERROR, errorEntity); - - return Response.status(status).entity(errorJson).type(JSON_MEDIA_TYPE).build(); - } - - public static String getRequestPayload(HttpServletRequest request) throws IOException { - //request is an instance of LocalServletRequest for calls from LocalAtlasClient - if (request instanceof LocalServletRequest) { - return ((LocalServletRequest) request).getPayload(); - } - - StringWriter writer = new StringWriter(); - IOUtils.copy(request.getInputStream(), writer); - return writer.toString(); - } - - public static String getRequestId() { - return Thread.currentThread().getName(); - } - - public static String escapeJsonString(String inputStr) { - ParamChecker.notNull(inputStr, "Input String cannot be null"); - return StringEscapeUtils.escapeJson(inputStr); - } - - public static String getHostName(HttpServletRequest httpServletRequest) { - return httpServletRequest.getLocalName(); - } - - public static String getUserName(HttpServletRequest httpServletRequest) { - return httpServletRequest.getRemoteUser(); - } - - public static Map getParameterMap(HttpServletRequest request) { - Map attributes = new HashMap<>(); - - if (MapUtils.isNotEmpty(request.getParameterMap())) { - for (Map.Entry e : request.getParameterMap().entrySet()) { - String key = e.getKey(); - - if (key != null) { - String[] values = e.getValue(); - String value = values != null && values.length > 0 ? values[0] : null; - - attributes.put(key, value); - } - } - } - - return attributes; - } - - public static void validateQueryParamLength(String paramName, String paramValue) throws AtlasBaseException { - if (StringUtils.isNotEmpty(paramValue) && paramValue.length() > QUERY_PARAM_MAX_LENGTH) { - throw new AtlasBaseException(AtlasErrorCode.INVALID_QUERY_PARAM_LENGTH, paramName); - } - } - - public static String decodeQueryString(String query) throws AtlasBaseException { - try { - return UriUtils.decode(query,"UTF-8"); - } catch (Exception e) { - LOG.error("Error occurred while decoding query:" + query, e.getMessage()); - throw new AtlasBaseException(e.getMessage()); - } - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java index 6cca308681..760e313ef8 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java @@ -21,7 +21,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; import org.apache.atlas.notification.rest.web.model.User; -import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java index b4121e2682..a96ab8a536 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java @@ -20,7 +20,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConfiguration; -import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationProvider; import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; @@ -87,6 +87,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.NullServletContext; +import org.apache.atlas.server.common.filters.RestUtil; /** * This enforces authentication as part of the filter before processing the request. diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java index 0252638747..48ad2aa7b5 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java @@ -43,6 +43,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; + @Component public class AtlasCSRFPreventionFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasCSRFPreventionFilter.class); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java index 5980880d70..d1d531f275 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -71,6 +71,9 @@ import java.util.List; import java.util.Map; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; @Component("ssoAuthenticationFilter") public class AtlasKnoxSSOAuthenticationFilter implements Filter { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java deleted file mode 100644 index 8e470b0d67..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasResponseRequestWrapper.java +++ /dev/null @@ -1,33 +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.atlas.notification.rest.web.filters; - -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; - - -public class AtlasResponseRequestWrapper extends HttpServletResponseWrapper { - public AtlasResponseRequestWrapper(HttpServletResponse response) { - super(response); - } -} - - - - - diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java index 15ad94c0a7..fcbfd2fe90 100755 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java @@ -24,8 +24,8 @@ import org.apache.atlas.RequestContext; import org.apache.atlas.authorize.AtlasAuthorizationUtils; import org.apache.atlas.notification.rest.AtlasRepositoryConfiguration; -import org.apache.atlas.notification.rest.DateTimeHelper; -import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.server.common.util.DateTimeHelper; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.java deleted file mode 100644 index 4a4c4ae765..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/RestUtil.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.atlas.notification.rest.web.filters; - -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.http.HttpServletRequest; -import java.util.Enumeration; - -public class RestUtil { - - private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); - public static final String TIMEOUT_ACTION = "timeout"; - public static final String LOGOUT_URL = "/logout.html"; - private static final String PROXY_ATLAS_URL_PATH = "/atlas"; - private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; - private static final String X_FORWARDED_HOST = "x-forwarded-host"; - private static final String X_FORWARDED_CONTEXT = "x-forwarded-context"; - public static final String DELIMITTER = "://"; - - public static String constructForwardableURL(HttpServletRequest httpRequest) { - String xForwardedProto = ""; - String xForwardedHost = ""; - String xForwardedContext = ""; - Enumeration headerNames = httpRequest.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String name = (String) headerNames.nextElement(); - Enumeration values = httpRequest.getHeaders(name); - String value = ""; - if (values != null) { - while (values.hasMoreElements()) { - value = (String) values.nextElement(); - } - } - if (StringUtils.trimToNull(name) != null && StringUtils.trimToNull(value) != null) { - if (name.equalsIgnoreCase(X_FORWARDED_PROTO)) { - xForwardedProto = value; - } else if (name.equalsIgnoreCase(X_FORWARDED_HOST)) { - xForwardedHost = value; - } else if (name.equalsIgnoreCase(X_FORWARDED_CONTEXT)) { - xForwardedContext = value; - } - } - } - if (xForwardedHost.contains(",")) { - if (LOG.isDebugEnabled()) { - LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); - } - xForwardedHost = xForwardedHost.split(",")[0].trim(); - } - String xForwardedURL = ""; - if (StringUtils.trimToNull(xForwardedProto) != null) { - //if header contains x-forwarded-host and x-forwarded-context - if (StringUtils.trimToNull(xForwardedHost) != null && StringUtils.trimToNull(xForwardedContext) != null) { - xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + httpRequest.getRequestURI(); - } else if (StringUtils.trimToNull(xForwardedHost) != null) { - //if header contains x-forwarded-host and does not contains x-forwarded-context - xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + httpRequest.getRequestURI(); - } else { - //if header does not contains x-forwarded-host and x-forwarded-context - //preserve the x-forwarded-proto value coming from the request. - String requestURL = httpRequest.getRequestURL().toString(); - if (StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { - requestURL = requestURL.replaceFirst("http", xForwardedProto); - } - xForwardedURL = requestURL; - } - } - return xForwardedURL; - } - - public static String constructRedirectURL(HttpServletRequest request, String redirectUrl, String xForwardedURL, String originalUrlQueryParam) { - String delimiter = "?"; - if (redirectUrl.contains("?")) { - delimiter = "&"; - } - String loginURL = redirectUrl + delimiter + originalUrlQueryParam + "="; - if (StringUtils.trimToNull(xForwardedURL) != null) { - loginURL += xForwardedURL; - } else { - loginURL += request.getRequestURL().append(getOriginalQueryString(request)); - } - return loginURL; - } - - private static String getOriginalQueryString(HttpServletRequest request) { - String originalQueryString = request.getQueryString(); - return (originalQueryString == null) ? "" : "?" + originalQueryString; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java deleted file mode 100644 index 433d3d0cc2..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthenticationProperties.java +++ /dev/null @@ -1,78 +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.atlas.notification.rest.web.filters; - -import java.security.interfaces.RSAPublicKey; - -public class SSOAuthenticationProperties { - - private String authenticationProviderUrl = null; - private RSAPublicKey publicKey = null; - private String cookieName = "hadoop-jwt"; - private String originalUrlQueryParam = null; - private String[] userAgentList = null; - - public String getAuthenticationProviderUrl() { - return authenticationProviderUrl; - } - - public void setAuthenticationProviderUrl(String authenticationProviderUrl) { - this.authenticationProviderUrl = authenticationProviderUrl; - } - - public RSAPublicKey getPublicKey() { - return publicKey; - } - - public void setPublicKey(RSAPublicKey publicKey) { - this.publicKey = publicKey; - } - - public String getCookieName() { - return cookieName; - } - - public void setCookieName(String cookieName) { - this.cookieName = cookieName; - } - - public String getOriginalUrlQueryParam() { - return originalUrlQueryParam; - } - - public void setOriginalUrlQueryParam(String originalUrlQueryParam) { - this.originalUrlQueryParam = originalUrlQueryParam; - } - - /** - * @return the userAgentList - */ - public String[] getUserAgentList() { - return userAgentList; - } - - /** - * @param userAgentList the userAgentList to set - */ - public void setUserAgentList(String[] userAgentList) { - this.userAgentList = userAgentList; - } -} - diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java index a9d78ac1e5..0a58c550b6 100755 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java @@ -20,7 +20,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasClient; -import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.notification.rest.web.service.ServiceState; import org.apache.atlas.utils.AtlasJson; import org.apache.commons.configuration.Configuration; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java index 0f973f4521..24e030aade 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java @@ -29,7 +29,7 @@ import org.apache.atlas.kafka.KafkaNotification; import org.apache.atlas.notification.NotificationException; import org.apache.atlas.notification.NotificationInterface; -import org.apache.atlas.notification.rest.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.utils.AtlasJson; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java index ccebd0b724..ff957ef766 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java @@ -40,6 +40,8 @@ import java.util.List; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component public class AtlasADAuthenticationProvider extends AtlasAbstractAuthenticationProvider { private static Logger LOG = LoggerFactory.getLogger(AtlasADAuthenticationProvider.class); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java deleted file mode 100644 index 0e8cce29fb..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationException.java +++ /dev/null @@ -1,31 +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.atlas.notification.rest.web.security; - -import org.springframework.security.core.AuthenticationException; - -public class AtlasAuthenticationException extends AuthenticationException { - - public AtlasAuthenticationException(String message) { - super(message); - } - - public AtlasAuthenticationException(String message, Throwable cause) { - super(message, cause); - } - -} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java deleted file mode 100644 index 478993fe5a..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationFailureHandler.java +++ /dev/null @@ -1,55 +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.atlas.notification.rest.web.security; - -import org.json.simple.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.AuthenticationFailureHandler; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - - -@Component -public class AtlasAuthenticationFailureHandler implements AuthenticationFailureHandler { - - private static Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFailureHandler.class); - - @Override - public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, - AuthenticationException e) throws IOException, ServletException { - - - LOG.debug("Login Failure ", e); - - JSONObject json = new JSONObject(); - json.put("msgDesc", e.getMessage()); - - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write(json.toJSONString()); - - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java index 3dfb9184f1..26dcfc9fe8 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java @@ -29,6 +29,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java index d2725e3d8f..6ef27e5267 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java @@ -42,6 +42,8 @@ import java.util.List; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component public class AtlasLdapAuthenticationProvider extends AtlasAbstractAuthenticationProvider { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java index 1617b33650..edede097ff 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java @@ -41,6 +41,9 @@ import java.util.Map; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.UserAuthorityGranter; + @Component public class AtlasPamAuthenticationProvider extends AtlasAbstractAuthenticationProvider { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java index 84c0d05fdf..7c4fd2ba35 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java @@ -18,12 +18,12 @@ package org.apache.atlas.notification.rest.web.security; import org.apache.atlas.notification.rest.web.filters.ActiveServerFilter; -import org.apache.atlas.notification.rest.web.filters.AtlasAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; import org.apache.atlas.notification.rest.web.filters.AtlasAuthenticationFilter; import org.apache.atlas.notification.rest.web.filters.AtlasCSRFPreventionFilter; -import org.apache.atlas.notification.rest.web.filters.AtlasDelegatingAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.notification.rest.web.filters.AtlasKnoxSSOAuthenticationFilter; -import org.apache.atlas.notification.rest.web.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.HeadersUtil; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -46,6 +46,9 @@ import javax.inject.Inject; import java.util.LinkedHashMap; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; + @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java index 796ae4128c..4a630c5e2c 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java @@ -40,6 +40,8 @@ import java.util.Map; import java.util.Set; +import org.apache.atlas.server.common.security.PamPrincipal; + public class PamLoginModule extends Object implements LoginModule { private static final Logger LOG = LoggerFactory.getLogger(PamLoginModule.class); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java deleted file mode 100644 index ae669c7d2d..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamPrincipal.java +++ /dev/null @@ -1,84 +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.atlas.notification.rest.web.security; - -import org.jvnet.libpam.UnixUser; - -import java.security.Principal; -import java.util.Collections; -import java.util.Set; - -public class PamPrincipal extends Object implements Principal { - private String userName; - private String gecos; - private String homeDir; - private String shell; - private int uid; - private int gid; - private Set groups; - - public PamPrincipal(UnixUser user) - { - super(); - userName = user.getUserName(); - gecos = user.getGecos(); - homeDir = user.getDir(); - shell = user.getShell(); - uid = user.getUID(); - gid = user.getGID(); - groups = Collections.unmodifiableSet(user.getGroups()); - } - - @Override - public String getName() - { - return userName; - } - - public String getGecos() - { - return gecos; - } - - public String getHomeDir() - { - return homeDir; - } - - public String getShell() - { - return shell; - } - - public int getUid() - { - return uid; - } - - public int getGid() - { - return gid; - } - - public Set getGroups() - { - return groups; - } -} diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml index ddf60b1221..e4b3ccfc65 100644 --- a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -77,7 +77,7 @@ HeaderFilter - org.apache.atlas.notification.rest.web.filters.AtlasHeaderFilter + org.apache.atlas.server.common.filters.AtlasHeaderFilter diff --git a/server-common/pom.xml b/server-common/pom.xml new file mode 100644 index 0000000000..2fab66d465 --- /dev/null +++ b/server-common/pom.xml @@ -0,0 +1,110 @@ + + + + 4.0.0 + + + org.apache.atlas + apache-atlas + 3.0.0-SNAPSHOT + + + server-common + jar + Apache Atlas Server Common + Shared server-side infrastructure for webapp modules + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} + + + com.google.guava + guava + + + com.googlecode.json-simple + json-simple + + + com.sun.jersey + jersey-core + + + commons-configuration + commons-configuration + + + commons-io + commons-io + + + commons-lang + commons-lang + 2.6 + + + javax.inject + javax.inject + + + javax.servlet + javax.servlet-api + + + org.apache.atlas + atlas-client-v1 + + + org.apache.atlas + atlas-common + + + org.apache.atlas + atlas-intg + + + org.apache.commons + commons-lang3 + + + org.apache.httpcomponents + httpclient + + + org.kohsuke + libpam4j + 1.11 + + + org.springframework + spring-web + + + org.springframework.security + spring-security-core + + + org.springframework.security + spring-security-web + + + diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java b/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java similarity index 99% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java rename to server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java index 2a85d913af..6483af18c6 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/LocalServletRequest.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.notification.rest; +package org.apache.atlas.server.common; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; @@ -32,6 +32,7 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; + import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java similarity index 97% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java index a02dff4823..4b2cd419e1 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationEntryPoint.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.notification.rest.web.filters; +package org.apache.atlas.server.common.filters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,12 +27,12 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.io.IOException; @SuppressWarnings("deprecation") @Component public class AtlasAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationEntryPoint.class); private String loginPath = "/login.jsp"; @@ -45,8 +45,6 @@ public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { - - String ajaxRequestHeader = request.getHeader("X-Requested-With"); response.setHeader("X-Frame-Options", "DENY"); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java similarity index 89% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java index 0b4950e709..1ee31b7f20 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasDelegatingAuthenticationEntryPoint.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.notification.rest.web.filters; +package org.apache.atlas.server.common.filters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,13 +25,13 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.io.IOException; import java.util.LinkedHashMap; public class AtlasDelegatingAuthenticationEntryPoint extends DelegatingAuthenticationEntryPoint { - - public static final String SESSION_TIMEOUT = "Session Timeout"; - private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); + public static final String SESSION_TIMEOUT = "Session Timeout"; + private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { super(entryPoints); @@ -41,8 +41,7 @@ public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap HEADER_MAP = new HashMap<>(); private HeadersUtil() { // to block instantiation } + public static String getHeaderMap(String header) { return HEADER_MAP.get(header); } @@ -106,4 +107,4 @@ public static void initializeHttpResponseHeaders(Properties configuredHeaders) { initializeHttpResponseHeaders(configuredHeaders); } -} \ No newline at end of file +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java similarity index 87% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java index 452f911707..eb1595a149 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/NullServletContext.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java @@ -12,7 +12,7 @@ * limitations under the License. See accompanying LICENSE file. */ -package org.apache.atlas.notification.rest.web.filters; +package org.apache.atlas.server.common.filters; import javax.servlet.Filter; import javax.servlet.FilterRegistration; @@ -25,6 +25,7 @@ import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.descriptor.JspConfigDescriptor; + import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; @@ -33,268 +34,244 @@ import java.util.Map; import java.util.Set; - /** */ public class NullServletContext implements ServletContext { - - - public void setSessionTrackingModes( Set sessionTrackingModes) { } - public boolean setInitParameter(String name, String value) { return false; } - public void setAttribute(String name, Object object) { } - public void removeAttribute(String name) { } - public void log(String message, Throwable throwable) { } - public void log(Exception exception, String msg) { } - public void log(String msg) { } - public String getVirtualServerName() { return null; } + @Override + public int getSessionTimeout() { + return 0; + } + + @Override + public void setSessionTimeout(int i) { + } + + @Override + public String getRequestCharacterEncoding() { + return ""; + } + + @Override + public void setRequestCharacterEncoding(String s) { + } + + @Override + public String getResponseCharacterEncoding() { + return ""; + } + + @Override + public void setResponseCharacterEncoding(String s) { + } + public SessionCookieConfig getSessionCookieConfig() { return null; } - public Enumeration getServlets() { return null; } - public Map getServletRegistrations() { return null; } - public ServletRegistration getServletRegistration(String servletName) { return null; } - public Enumeration getServletNames() { return null; } - public String getServletContextName() { return null; } - public Servlet getServlet(String name) throws ServletException { return null; } - public String getServerInfo() { return null; } - public Set getResourcePaths(String path) { return null; } - public InputStream getResourceAsStream(String path) { return null; } - public URL getResource(String path) throws MalformedURLException { return null; } - public RequestDispatcher getRequestDispatcher(String path) { return null; } - public String getRealPath(String path) { return null; } - public RequestDispatcher getNamedDispatcher(String name) { return null; } - public int getMinorVersion() { return 0; } - public String getMimeType(String file) { return null; } - public int getMajorVersion() { return 0; } - public JspConfigDescriptor getJspConfigDescriptor() { return null; } - public Enumeration getInitParameterNames() { return null; } - public String getInitParameter(String name) { return null; } - public Map getFilterRegistrations() { return null; } - public FilterRegistration getFilterRegistration(String filterName) { return null; } - public Set getEffectiveSessionTrackingModes() { return null; } - public int getEffectiveMinorVersion() { return 0; } - public int getEffectiveMajorVersion() { return 0; } - public Set getDefaultSessionTrackingModes() { return null; } - public String getContextPath() { return null; } - public ServletContext getContext(String uripath) { return null; } - public ClassLoader getClassLoader() { return null; } - public Enumeration getAttributeNames() { return null; } - public Object getAttribute(String name) { return null; } - public void declareRoles(String... roleNames) { } - public T createServlet(Class clazz) throws ServletException { return null; } - public T createListener(Class clazz) throws ServletException { return null; } - public T createFilter(Class clazz) throws ServletException { return null; } - - public ServletRegistration.Dynamic addServlet( + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, Class servletClass) { return null; } + @Override + public ServletRegistration.Dynamic addJspFile(String s, String s1) { + return null; + } - public ServletRegistration.Dynamic addServlet( + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, Servlet servlet) { return null; } - - public ServletRegistration.Dynamic addServlet( + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, String className) { return null; } - public void addListener(Class listenerClass) { } - public void addListener(T t) { } - public void addListener(String className) { } - public Dynamic addFilter(String filterName, Class filterClass) { return null; } - public Dynamic addFilter(String filterName, Filter filter) { return null; } - public Dynamic addFilter(String filterName, String className) { return null; } - - } diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java similarity index 89% rename from webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java index d2f335e2c9..c711393c19 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/RestUtil.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java @@ -16,9 +16,9 @@ * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; -import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,12 +27,10 @@ import java.util.Enumeration; public class RestUtil { - private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); - - public static final String TIMEOUT_ACTION = "timeout"; - public static final String LOGOUT_URL = "/logout.html"; - public static final String DELIMITTER = "://"; - + public static final String TIMEOUT_ACTION = "timeout"; + public static final String LOGOUT_URL = "/logout.html"; + public static final String DELIMITTER = "://"; + private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); private static final String PROXY_ATLAS_URL_PATH = "/atlas"; private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; private static final String X_FORWARDED_HOST = "x-forwarded-host"; @@ -47,18 +45,15 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { String xForwardedHost = ""; String xForwardedContext = ""; Enumeration headerNames = httpRequest.getHeaderNames(); - while (headerNames.hasMoreElements()) { String name = (String) headerNames.nextElement(); Enumeration values = httpRequest.getHeaders(name); String value = ""; - if (values != null) { while (values.hasMoreElements()) { value = (String) values.nextElement(); } } - if (StringUtils.trimToNull(name) != null && StringUtils.trimToNull(value) != null) { if (name.equalsIgnoreCase(X_FORWARDED_PROTO)) { xForwardedProto = value; @@ -69,15 +64,13 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { } } } - if (xForwardedHost.contains(",")) { - LOG.debug("xForwardedHost value is {} it contains multiple hosts, selecting the first host.", xForwardedHost); - + if (LOG.isDebugEnabled()) { + LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); + } xForwardedHost = xForwardedHost.split(",")[0].trim(); } - String xForwardedURL = ""; - if (StringUtils.trimToNull(xForwardedProto) != null) { //if header contains x-forwarded-host and x-forwarded-context if (StringUtils.trimToNull(xForwardedHost) != null && StringUtils.trimToNull(xForwardedContext) != null) { @@ -89,39 +82,31 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { //if header does not contains x-forwarded-host and x-forwarded-context //preserve the x-forwarded-proto value coming from the request. String requestURL = httpRequest.getRequestURL().toString(); - if (StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { requestURL = requestURL.replaceFirst("http", xForwardedProto); } - xForwardedURL = requestURL; } } - return xForwardedURL; } public static String constructRedirectURL(HttpServletRequest request, String redirectUrl, String xForwardedURL, String originalUrlQueryParam) { String delimiter = "?"; - if (redirectUrl.contains("?")) { delimiter = "&"; } - String loginURL = redirectUrl + delimiter + originalUrlQueryParam + "="; - if (StringUtils.trimToNull(xForwardedURL) != null) { loginURL += xForwardedURL; } else { loginURL += request.getRequestURL().append(getOriginalQueryString(request)); } - return loginURL; } private static String getOriginalQueryString(HttpServletRequest request) { String originalQueryString = request.getQueryString(); - return (originalQueryString == null) ? "" : "?" + originalQueryString; } } diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java similarity index 95% rename from webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java index 890bf25433..8d16e07947 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthenticationProperties.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java @@ -17,14 +17,14 @@ * under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import java.security.interfaces.RSAPublicKey; public class SSOAuthenticationProperties { private String authenticationProviderUrl; private RSAPublicKey publicKey; - private String cookieName = "hadoop-jwt"; + private String cookieName = "hadoop-jwt"; private String originalUrlQueryParam; private String[] userAgentList; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationException.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationException.java similarity index 95% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationException.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationException.java index cda9fad622..34be0fb46f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationException.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationException.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.springframework.security.core.AuthenticationException; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java similarity index 89% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandler.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java index 514ccbc4db..307ddb4fa8 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandler.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.json.simple.JSONObject; import org.slf4j.Logger; @@ -25,6 +25,7 @@ import org.springframework.security.web.authentication.AuthenticationFailureHandler; import org.springframework.stereotype.Component; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -35,17 +36,16 @@ public class AtlasAuthenticationFailureHandler implements AuthenticationFailureH private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFailureHandler.class); @Override - public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException { + public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, + AuthenticationException e) throws IOException, ServletException { LOG.debug("Login Failure ", e); JSONObject json = new JSONObject(); - json.put("msgDesc", e.getMessage()); response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setCharacterEncoding("UTF-8"); - response.getWriter().write(json.toJSONString()); } } diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java similarity index 82% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java index 288b4b4e8b..7c76d9acd8 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationSuccessHandler.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java @@ -6,9 +6,9 @@ * 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. @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.notification.rest.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.AtlasConfiguration; import org.json.simple.JSONObject; @@ -30,15 +30,14 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; +import java.io.IOException; @Component public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { - - private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); - private int sessionTimeout = 3600; - public static final String LOCALLOGIN = "locallogin"; + public static final String LOCALLOGIN = "locallogin"; + private static final Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); + private int sessionTimeout = 3600; @PostConstruct public void setup() { @@ -47,15 +46,14 @@ public void setup() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { - + Authentication authentication) throws IOException, ServletException { LOG.debug("Login Success " + authentication.getPrincipal()); JSONObject json = new JSONObject(); json.put("msgDesc", "Success"); if (request.getSession() != null) { // incase of form based login mark it as local login in session - request.getSession().setAttribute(LOCALLOGIN,"true"); + request.getSession().setAttribute(LOCALLOGIN, "true"); request.getServletContext().setAttribute(request.getSession().getId(), LOCALLOGIN); if (this.sessionTimeout != -1) { diff --git a/webapp/src/main/java/org/apache/atlas/web/security/PamPrincipal.java b/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java similarity index 83% rename from webapp/src/main/java/org/apache/atlas/web/security/PamPrincipal.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java index 082c1c1bb7..328cc81c2d 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/PamPrincipal.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.jvnet.libpam.UnixUser; @@ -25,18 +25,17 @@ import java.util.Collections; import java.util.Set; -public class PamPrincipal implements Principal { - private final String userName; - private final String gecos; - private final String homeDir; - private final String shell; - private final int uid; - private final int gid; - private final Set groups; +public class PamPrincipal extends Object implements Principal { + private String userName; + private String gecos; + private String homeDir; + private String shell; + private int uid; + private int gid; + private Set groups; public PamPrincipal(UnixUser user) { super(); - userName = user.getUserName(); gecos = user.getGecos(); homeDir = user.getDir(); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java similarity index 95% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java index 77fa4c60da..625078839a 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/UserAuthorityGranter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.atlas.notification.rest.web.security; +package org.apache.atlas.server.common.security; import org.springframework.security.authentication.jaas.AuthorityGranter; @@ -26,7 +26,6 @@ import java.util.Set; public class UserAuthorityGranter implements AuthorityGranter { - @Override public Set grant(Principal principal) { Collections.singleton("DATA_SCIENTIST"); diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java old mode 100755 new mode 100644 similarity index 79% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java rename to server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java index 6b50ba27d6..b3629beb9e --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/DateTimeHelper.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java @@ -6,9 +6,9 @@ * 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. @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.notification.rest; +package org.apache.atlas.server.common.util; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -28,13 +28,12 @@ * Support function to parse and format date. */ public final class DateTimeHelper { - - public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - private static final String DATE_PATTERN = + public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + private static final String DATE_PATTERN = "(2\\d\\d\\d|19\\d\\d)-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])T" + "([0-1][0-9]|2[0-3]):([0-5][0-9])Z"; - private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); + private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); - private static ThreadLocal DATE_FORMAT = new ThreadLocal() { + private static final ThreadLocal DATE_FORMAT = new ThreadLocal() { @Override public DateFormat initialValue() { DateFormat dateFormat = new SimpleDateFormat(ISO8601_FORMAT); @@ -53,4 +52,4 @@ public static DateFormat getDateFormat() { public static String formatDateUTC(Date date) { return (date != null) ? getDateFormat().format(date) : null; } -} \ No newline at end of file +} diff --git a/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java b/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java old mode 100755 new mode 100644 similarity index 88% rename from webapp/src/main/java/org/apache/atlas/web/util/Servlets.java rename to server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java index d3ccc6cd48..d4c749ccd9 --- a/webapp/src/main/java/org/apache/atlas/web/util/Servlets.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java @@ -16,13 +16,13 @@ * limitations under the License. */ -package org.apache.atlas.web.util; +package org.apache.atlas.server.common.util; import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.atlas.AtlasClient; import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.LocalServletRequest; +import org.apache.atlas.server.common.LocalServletRequest; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.ParamChecker; @@ -43,7 +43,6 @@ import java.io.IOException; import java.io.StringWriter; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -52,13 +51,11 @@ * Utility functions for dealing with servlets. */ public final class Servlets { - private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); - - public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; - public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; - + public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; + public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; + private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); private static final int QUERY_PARAM_MAX_LENGTH = AtlasConfiguration.QUERY_PARAM_MAX_LENGTH.getInt(); - private static final Charset UTF8_CHARSET = StandardCharsets.UTF_8; + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); private static final String DO_AS = "doAs"; private Servlets() { @@ -68,30 +65,26 @@ private Servlets() { /** * Returns the user of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the user */ public static String getUserFromRequest(HttpServletRequest httpRequest) { String user = httpRequest.getRemoteUser(); - if (!StringUtils.isEmpty(user)) { return user; } user = httpRequest.getParameter("user.name"); // available in query-param - if (!StringUtils.isEmpty(user)) { return user; } user = httpRequest.getHeader("Remote-User"); // backwards-compatibility - if (!StringUtils.isEmpty(user)) { return user; } user = getDoAsUser(httpRequest); - if (!StringUtils.isEmpty(user)) { return user; } @@ -102,7 +95,6 @@ public static String getUserFromRequest(HttpServletRequest httpRequest) { public static String getDoAsUser(HttpServletRequest request) { if (StringUtils.isNoneEmpty(request.getQueryString())) { List list = URLEncodedUtils.parse(request.getQueryString(), UTF8_CHARSET); - if (list != null) { for (NameValuePair nv : list) { if (DO_AS.equals(nv.getName())) { @@ -111,19 +103,17 @@ public static String getDoAsUser(HttpServletRequest request) { } } } - return null; } /** * Returns the URI of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the URI, including the query string */ public static String getRequestURI(HttpServletRequest httpRequest) { final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURI()); - if (httpRequest.getQueryString() != null) { url.append('?').append(httpRequest.getQueryString()); } @@ -134,12 +124,11 @@ public static String getRequestURI(HttpServletRequest httpRequest) { /** * Returns the full URL of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the full URL, including the query string */ public static String getRequestURL(HttpServletRequest httpRequest) { final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURL()); - if (httpRequest.getQueryString() != null) { url.append('?').append(httpRequest.getQueryString()); } @@ -149,14 +138,16 @@ public static String getRequestURL(HttpServletRequest httpRequest) { public static Response getErrorResponse(AtlasBaseException e) { String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); + Response response = getErrorResponse(message, e.getAtlasErrorCode().getHttpCode()); - return getErrorResponse(message, e.getAtlasErrorCode().getHttpCode()); + return response; } public static Response getErrorResponse(Throwable e, Response.Status status) { String message = e.getMessage() == null ? "Failed with " + e.getClass().getName() : e.getMessage(); + Response response = getErrorResponse(message, status); - return getErrorResponse(message, status); + return response; } public static Response getErrorResponse(String message, Response.Status status) { @@ -173,9 +164,7 @@ public static String getRequestPayload(HttpServletRequest request) throws IOExce } StringWriter writer = new StringWriter(); - IOUtils.copy(request.getInputStream(), writer); - return writer.toString(); } @@ -185,7 +174,6 @@ public static String getRequestId() { public static String escapeJsonString(String inputStr) { ParamChecker.notNull(inputStr, "Input String cannot be null"); - return StringEscapeUtils.escapeJson(inputStr); } @@ -226,8 +214,7 @@ public static String decodeQueryString(String query) throws AtlasBaseException { try { return UriUtils.decode(query, "UTF-8"); } catch (Exception e) { - LOG.error("Error occurred while decoding query: {}{}", e.getMessage(), query); - + LOG.error("Error occurred while decoding query:" + query, e.getMessage()); throw new AtlasBaseException(e.getMessage()); } } diff --git a/webapp/pom.xml b/webapp/pom.xml index 9f5c5bb4e6..8929475f01 100755 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -213,6 +213,11 @@ atlas-server-api + + org.apache.atlas + server-common + + org.apache.commons commons-collections4 diff --git a/webapp/src/main/java/org/apache/atlas/LocalServletRequest.java b/webapp/src/main/java/org/apache/atlas/LocalServletRequest.java deleted file mode 100644 index be3bcb4ea6..0000000000 --- a/webapp/src/main/java/org/apache/atlas/LocalServletRequest.java +++ /dev/null @@ -1,400 +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.atlas; - -import javax.servlet.AsyncContext; -import javax.servlet.DispatcherType; -import javax.servlet.RequestDispatcher; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletInputStream; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import javax.servlet.http.HttpUpgradeHandler; -import javax.servlet.http.Part; - -import java.io.BufferedReader; -import java.io.IOException; -import java.security.Principal; -import java.util.Collection; -import java.util.Enumeration; -import java.util.Locale; -import java.util.Map; - -public class LocalServletRequest implements HttpServletRequest { - private final String payload; - - LocalServletRequest(String payload) { - this.payload = payload; - } - - public String getPayload() { - return payload; - } - - @Override - public String getAuthType() { - throw new IllegalStateException("Not supported"); - } - - @Override - public Cookie[] getCookies() { - throw new IllegalStateException("Not supported"); - } - - @Override - public long getDateHeader(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getHeader(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Enumeration getHeaders(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Enumeration getHeaderNames() { - throw new IllegalStateException("Not supported"); - } - - @Override - public int getIntHeader(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getMethod() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getPathInfo() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getPathTranslated() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getContextPath() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getQueryString() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRemoteUser() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isUserInRole(String role) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Principal getUserPrincipal() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRequestedSessionId() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRequestURI() { - throw new IllegalStateException("Not supported"); - } - - @Override - public StringBuffer getRequestURL() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getServletPath() { - throw new IllegalStateException("Not supported"); - } - - @Override - public HttpSession getSession(boolean create) { - throw new IllegalStateException("Not supported"); - } - - @Override - public HttpSession getSession() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String changeSessionId() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isRequestedSessionIdValid() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isRequestedSessionIdFromCookie() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isRequestedSessionIdFromURL() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isRequestedSessionIdFromUrl() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - throw new IllegalStateException("Not supported"); - } - - @Override - public void login(String username, String password) throws ServletException { - throw new IllegalStateException("Not supported"); - } - - @Override - public void logout() { - throw new IllegalStateException("Not supported"); - } - - @Override - public Collection getParts() { - throw new IllegalStateException("Not supported"); - } - - @Override - public Part getPart(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public T upgrade(Class handlerClass) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Object getAttribute(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Enumeration getAttributeNames() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getCharacterEncoding() { - throw new IllegalStateException("Not supported"); - } - - @Override - public void setCharacterEncoding(String env) { - throw new IllegalStateException("Not supported"); - } - - @Override - public int getContentLength() { - throw new IllegalStateException("Not supported"); - } - - @Override - public long getContentLengthLong() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getContentType() { - throw new IllegalStateException("Not supported"); - } - - @Override - public ServletInputStream getInputStream() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getParameter(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Enumeration getParameterNames() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String[] getParameterValues(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Map getParameterMap() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getProtocol() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getScheme() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getServerName() { - throw new IllegalStateException("Not supported"); - } - - @Override - public int getServerPort() { - throw new IllegalStateException("Not supported"); - } - - @Override - public BufferedReader getReader() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRemoteAddr() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRemoteHost() { - throw new IllegalStateException("Not supported"); - } - - @Override - public void setAttribute(String name, Object o) { - throw new IllegalStateException("Not supported"); - } - - @Override - public void removeAttribute(String name) { - throw new IllegalStateException("Not supported"); - } - - @Override - public Locale getLocale() { - throw new IllegalStateException("Not supported"); - } - - @Override - public Enumeration getLocales() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isSecure() { - throw new IllegalStateException("Not supported"); - } - - @Override - public RequestDispatcher getRequestDispatcher(String path) { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getRealPath(String path) { - throw new IllegalStateException("Not supported"); - } - - @Override - public int getRemotePort() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getLocalName() { - throw new IllegalStateException("Not supported"); - } - - @Override - public String getLocalAddr() { - throw new IllegalStateException("Not supported"); - } - - @Override - public int getLocalPort() { - throw new IllegalStateException("Not supported"); - } - - @Override - public ServletContext getServletContext() { - throw new IllegalStateException("Not supported"); - } - - @Override - public AsyncContext startAsync() throws IllegalStateException { - throw new IllegalStateException("Not supported"); - } - - @Override - public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) - throws IllegalStateException { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isAsyncStarted() { - throw new IllegalStateException("Not supported"); - } - - @Override - public boolean isAsyncSupported() { - throw new IllegalStateException("Not supported"); - } - - @Override - public AsyncContext getAsyncContext() { - throw new IllegalStateException("Not supported"); - } - - @Override - public DispatcherType getDispatcherType() { - throw new IllegalStateException("Not supported"); - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java b/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java index 89c768800a..884aa9f334 100644 --- a/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java +++ b/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java @@ -21,7 +21,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; import org.apache.atlas.web.model.User; -import org.apache.atlas.web.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java deleted file mode 100644 index 7526c18022..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java +++ /dev/null @@ -1,57 +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.atlas.web.filters; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.io.IOException; - -@Component -public class AtlasAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationEntryPoint.class); - - private static final String LOGIN_PATH = "/login.jsp"; - - @Inject - public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { - super(loginFormUrl); - } - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - String ajaxRequestHeader = request.getHeader("X-Requested-With"); - - response.setHeader("X-Frame-Options", "DENY"); - - if ("XMLHttpRequest".equals(ajaxRequestHeader)) { - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - } else { - LOG.debug("redirecting to login page loginPath {}", LOGIN_PATH); - - response.sendRedirect(LOGIN_PATH); - } - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java index 652af3a86e..3ccbe3a295 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java @@ -23,7 +23,7 @@ import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.atlas.web.security.AtlasAuthenticationProvider; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; @@ -87,7 +87,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.apache.atlas.web.filters.RestUtil.constructForwardableURL; +import static org.apache.atlas.server.common.filters.RestUtil.constructForwardableURL; + +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.NullServletContext; +import org.apache.atlas.server.common.filters.RestUtil; /** * This enforces authentication as part of the filter before processing the request. diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java index 7dc12acc37..6c1974f46f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java @@ -44,6 +44,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; + @Component public class AtlasCSRFPreventionFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasCSRFPreventionFilter.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasDelegatingAuthenticationEntryPoint.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasDelegatingAuthenticationEntryPoint.java deleted file mode 100644 index 09cb1b9977..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasDelegatingAuthenticationEntryPoint.java +++ /dev/null @@ -1,58 +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.atlas.web.filters; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; -import org.springframework.security.web.util.matcher.RequestMatcher; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.io.IOException; -import java.util.LinkedHashMap; - -public class AtlasDelegatingAuthenticationEntryPoint extends DelegatingAuthenticationEntryPoint { - private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); - - public static final String SESSION_TIMEOUT = "Session Timeout"; - - public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { - super(entryPoints); - - LOG.debug("AtlasDelegatingAuthenticationEntryPoint-AjaxAwareAuthenticationEntryPoint(): constructor"); - } - - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - String ajaxRequestHeader = request.getHeader(HeadersUtil.X_REQUESTED_WITH_KEY); - - response.setHeader(HeadersUtil.X_FRAME_OPTIONS_KEY, HeadersUtil.X_FRAME_OPTIONS_VAL); - - if (HeadersUtil.X_REQUESTED_WITH_VALUE.equalsIgnoreCase(ajaxRequestHeader)) { - if (LOG.isDebugEnabled()) { - LOG.debug("commence() AJAX request. Authentication required. Returning {}. URL={}", HttpServletResponse.SC_UNAUTHORIZED, request.getRequestURI()); - } - - response.sendError(HeadersUtil.SC_AUTHENTICATION_TIMEOUT, SESSION_TIMEOUT); - } else { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); - } - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java deleted file mode 100644 index 7fe2c42b4f..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java +++ /dev/null @@ -1,50 +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.atlas.web.filters; - -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.HttpServletResponse; - -import java.io.IOException; - -public class AtlasHeaderFilter implements Filter { - @Override - public void init(FilterConfig filterConfig) { - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - setHeaders((HttpServletResponse) response); - filterChain.doFilter(request, response); - } - - @Override - public void destroy() { - } - - public void setHeaders(HttpServletResponse httpResponse) { - AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); - - HeadersUtil.setSecurityHeaders(responseWrapper); - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java index a5dca7aaee..d786cfc90f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -72,6 +72,10 @@ import java.util.List; import java.util.Map; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; + @Component("ssoAuthenticationFilter") public class AtlasKnoxSSOAuthenticationFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java index 12d34353ba..a04fe9f4df 100755 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java @@ -24,8 +24,8 @@ import org.apache.atlas.RequestContext; import org.apache.atlas.authorize.AtlasAuthorizationUtils; import org.apache.atlas.util.AtlasRepositoryConfiguration; -import org.apache.atlas.web.util.DateTimeHelper; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.DateTimeHelper; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java b/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java deleted file mode 100644 index dbec3cdbfa..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.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.atlas.web.filters; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; - -public class HeadersUtil { - public static final Logger LOG = LoggerFactory.getLogger(HeadersUtil.class); - - public static final String X_FRAME_OPTIONS_KEY = "X-Frame-Options"; - public static final String X_CONTENT_TYPE_OPTIONS_KEY = "X-Content-Type-Options"; - public static final String X_XSS_PROTECTION_KEY = "X-XSS-Protection"; - public static final String STRICT_TRANSPORT_SEC_KEY = "Strict-Transport-Security"; - public static final String CONTENT_SEC_POLICY_KEY = "Content-Security-Policy"; - public static final String X_FRAME_OPTIONS_VAL = "DENY"; - public static final String X_CONTENT_TYPE_OPTIONS_VAL = "nosniff"; - public static final String X_XSS_PROTECTION_VAL = "1; mode=block"; - public static final String STRICT_TRANSPORT_SEC_VAL = "max-age=31536000; includeSubDomains"; - public static final String CONTENT_SEC_POLICY_VAL = "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data:; connect-src 'self'; img-src 'self' blob: data:; style-src 'self' 'unsafe-inline';font-src 'self' data:"; - public static final String SERVER_KEY = "Server"; - public static final String USER_AGENT_KEY = "User-Agent"; - public static final String USER_AGENT_VALUE = "Mozilla"; - public static final String X_REQUESTED_WITH_KEY = "X-REQUESTED-WITH"; - public static final String X_REQUESTED_WITH_VALUE = "XMLHttpRequest"; - public static final int SC_AUTHENTICATION_TIMEOUT = 419; - public static final String CONFIG_PREFIX_HTTP_RESPONSE_HEADER = "atlas.headers"; - - private static final Map HEADER_MAP = new HashMap<>(); - - private HeadersUtil() { - // to block instantiation - } - - public static String getHeaderMap(String header) { - return HEADER_MAP.get(header); - } - - public static Map getAllHeaders() { - return new HashMap<>(HEADER_MAP); - } - - public static void setHeaderMapAttributes(AtlasResponseRequestWrapper responseWrapper, String headerKey) { - responseWrapper.setHeader(headerKey, HEADER_MAP.get(headerKey)); - } - - public static void setSecurityHeaders(AtlasResponseRequestWrapper responseWrapper) { - HEADER_MAP.forEach((key, value) -> responseWrapper.setHeader(key, value)); - } - - @VisibleForTesting - public static void initializeHttpResponseHeaders(Properties configuredHeaders) { - HEADER_MAP.clear(); - - HEADER_MAP.put(X_FRAME_OPTIONS_KEY, X_FRAME_OPTIONS_VAL); - HEADER_MAP.put(X_CONTENT_TYPE_OPTIONS_KEY, X_CONTENT_TYPE_OPTIONS_VAL); - HEADER_MAP.put(X_XSS_PROTECTION_KEY, X_XSS_PROTECTION_VAL); - HEADER_MAP.put(STRICT_TRANSPORT_SEC_KEY, STRICT_TRANSPORT_SEC_VAL); - HEADER_MAP.put(CONTENT_SEC_POLICY_KEY, CONTENT_SEC_POLICY_VAL); - HEADER_MAP.put(SERVER_KEY, AtlasConfiguration.HTTP_HEADER_SERVER_VALUE.getString()); - - if (configuredHeaders != null) { - configuredHeaders.stringPropertyNames().forEach(name -> HEADER_MAP.put(name, configuredHeaders.getProperty(name))); - } - } - - static { - Properties configuredHeaders = null; - - try { - Configuration baseConfig = ApplicationProperties.get(); - Configuration headerConfig = ApplicationProperties.getSubsetConfiguration(baseConfig, CONFIG_PREFIX_HTTP_RESPONSE_HEADER); - - configuredHeaders = Optional.ofNullable(headerConfig) - .map(ConfigurationConverter::getProperties) - .orElseGet(Properties::new); - } catch (Exception e) { - LOG.info("Failed to load custom headers: {}", e.getMessage()); - } - - initializeHttpResponseHeaders(configuredHeaders); - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java b/webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java deleted file mode 100644 index 60801aac64..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java +++ /dev/null @@ -1,236 +0,0 @@ -/** - * Licensed 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. See accompanying LICENSE file. - */ - -package org.apache.atlas.web.filters; - -import javax.servlet.Filter; -import javax.servlet.FilterRegistration; -import javax.servlet.FilterRegistration.Dynamic; -import javax.servlet.RequestDispatcher; -import javax.servlet.Servlet; -import javax.servlet.ServletContext; -import javax.servlet.ServletRegistration; -import javax.servlet.SessionCookieConfig; -import javax.servlet.SessionTrackingMode; -import javax.servlet.descriptor.JspConfigDescriptor; - -import java.io.InputStream; -import java.net.URL; -import java.util.Enumeration; -import java.util.EventListener; -import java.util.Map; -import java.util.Set; - -/** - */ -public class NullServletContext implements ServletContext { - public String getContextPath() { - return null; - } - - public ServletContext getContext(String uripath) { - return null; - } - - public int getMajorVersion() { - return 0; - } - - public int getMinorVersion() { - return 0; - } - - public int getEffectiveMajorVersion() { - return 0; - } - - public int getEffectiveMinorVersion() { - return 0; - } - - public String getMimeType(String file) { - return null; - } - - public Set getResourcePaths(String path) { - return null; - } - - public URL getResource(String path) { - return null; - } - - public InputStream getResourceAsStream(String path) { - return null; - } - - public RequestDispatcher getRequestDispatcher(String path) { - return null; - } - - public RequestDispatcher getNamedDispatcher(String name) { - return null; - } - - public Servlet getServlet(String name) { - return null; - } - - public Enumeration getServlets() { - return null; - } - - public Enumeration getServletNames() { - return null; - } - - public void log(String msg) { - } - - public void log(Exception exception, String msg) { - } - - public void log(String message, Throwable throwable) { - } - - public String getRealPath(String path) { - return null; - } - - public String getServerInfo() { - return null; - } - - public String getInitParameter(String name) { - return null; - } - - public Enumeration getInitParameterNames() { - return null; - } - - public boolean setInitParameter(String name, String value) { - return false; - } - - public Object getAttribute(String name) { - return null; - } - - public Enumeration getAttributeNames() { - return null; - } - - public void setAttribute(String name, Object object) { - } - - public void removeAttribute(String name) { - } - - public String getServletContextName() { - return null; - } - - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className) { - return null; - } - - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { - return null; - } - - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { - return null; - } - - public T createServlet(Class clazz) { - return null; - } - - public ServletRegistration getServletRegistration(String servletName) { - return null; - } - - public Map getServletRegistrations() { - return null; - } - - public Dynamic addFilter(String filterName, String className) { - return null; - } - - public Dynamic addFilter(String filterName, Filter filter) { - return null; - } - - public Dynamic addFilter(String filterName, - Class filterClass) { - return null; - } - - public T createFilter(Class clazz) { - return null; - } - - public FilterRegistration getFilterRegistration(String filterName) { - return null; - } - - public Map getFilterRegistrations() { - return null; - } - - public SessionCookieConfig getSessionCookieConfig() { - return null; - } - - public void setSessionTrackingModes(Set sessionTrackingModes) { - } - - public Set getDefaultSessionTrackingModes() { - return null; - } - - public Set getEffectiveSessionTrackingModes() { - return null; - } - - public void addListener(String className) { - } - - public void addListener(T t) { - } - - public void addListener(Class listenerClass) { - } - - public T createListener(Class clazz) { - return null; - } - - public JspConfigDescriptor getJspConfigDescriptor() { - return null; - } - - public ClassLoader getClassLoader() { - return null; - } - - public void declareRoles(String... roleNames) { - } - - public String getVirtualServerName() { - return null; - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java index b092d48068..b916f96e95 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java @@ -80,7 +80,7 @@ import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; import org.apache.atlas.web.service.ServiceState; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java index 0c14be1988..ecc21aa916 100644 --- a/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java @@ -31,7 +31,7 @@ import org.apache.atlas.v1.model.lineage.DataSetLineageResponse; import org.apache.atlas.v1.model.lineage.SchemaResponse; import org.apache.atlas.web.util.LineageUtils; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java index 7d6e829a42..8e600e6b04 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java @@ -50,7 +50,7 @@ import org.apache.atlas.v1.model.instance.Referenceable; import org.apache.atlas.v1.model.instance.Struct; import org.apache.atlas.web.rest.EntityREST; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java index 61ceca3e52..fdfac61bed 100644 --- a/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java @@ -27,7 +27,7 @@ import org.apache.atlas.v1.model.lineage.LineageResponse; import org.apache.atlas.v1.model.lineage.SchemaResponse; import org.apache.atlas.web.util.LineageUtils; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java index 280e13760e..a355820576 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java @@ -33,7 +33,7 @@ import org.apache.atlas.v1.model.discovery.DSLSearchResult; import org.apache.atlas.v1.model.discovery.FullTextSearchResult; import org.apache.atlas.v1.model.instance.Referenceable; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java index 8451997fc8..5d6d22e094 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java @@ -30,7 +30,7 @@ import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.v1.model.typedef.TypesDef; import org.apache.atlas.web.rest.TypesREST; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java index 6f404a656f..ca59fd7996 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java @@ -43,7 +43,7 @@ import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java index 07f142d82a..09ac618f54 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java @@ -50,7 +50,7 @@ import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.util.FileUtils; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java index a9b7196674..2c874bc8d2 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java @@ -33,7 +33,7 @@ import org.apache.atlas.model.glossary.relations.AtlasRelatedTermHeader; import org.apache.atlas.model.instance.AtlasRelatedObjectId; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java index 78ebd52c04..8059216d96 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java @@ -28,8 +28,8 @@ import org.apache.atlas.repository.graphdb.AtlasGraph; import org.apache.atlas.repository.graphdb.AtlasVertex; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.DateTimeHelper; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.DateTimeHelper; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.springframework.format.annotation.DateTimeFormat; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java index 54b6269985..583b70a8c2 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java @@ -31,7 +31,7 @@ import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java index d0c8a736fa..f477716425 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java @@ -30,7 +30,7 @@ import org.apache.atlas.notification.NotificationException; import org.apache.atlas.notification.NotificationInterface; import org.apache.atlas.utils.AtlasJson; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java index db64802d40..80f6f565df 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java @@ -24,7 +24,7 @@ import org.apache.atlas.model.instance.AtlasRelationship.AtlasRelationshipWithExtInfo; import org.apache.atlas.repository.store.graph.AtlasRelationshipStore; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java index e42b7627b1..36d284a5cc 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java @@ -33,7 +33,7 @@ import org.apache.atlas.store.AtlasTypeDefStore; import org.apache.atlas.type.AtlasTypeUtil; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.http.annotation.Experimental; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java index 59329a0643..8b9020185a 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java @@ -41,6 +41,8 @@ import java.util.List; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component public class AtlasADAuthenticationProvider extends AtlasAbstractAuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AtlasADAuthenticationProvider.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java index 3cff9fb8ee..40c31a03e0 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java @@ -29,6 +29,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java deleted file mode 100644 index 5d6834fd19..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java +++ /dev/null @@ -1,70 +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.atlas.web.security; - -import org.apache.atlas.AtlasConfiguration; -import org.json.simple.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.AuthenticationSuccessHandler; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import java.io.IOException; - -@Component -public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationSuccessHandler.class); - - public static final String LOCALLOGIN = "locallogin"; - - private int sessionTimeout = 3600; - - @PostConstruct - public void setup() { - sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); - } - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - LOG.debug("Login Success {}", authentication.getPrincipal()); - - JSONObject json = new JSONObject(); - - json.put("msgDesc", "Success"); - - if (request.getSession() != null) { // incase of form based login mark it as local login in session - request.getSession().setAttribute(LOCALLOGIN, "true"); - request.getServletContext().setAttribute(request.getSession().getId(), LOCALLOGIN); - - if (this.sessionTimeout != -1) { - request.getSession().setMaxInactiveInterval(sessionTimeout); - } - } - - response.setContentType("application/json"); - response.setStatus(HttpServletResponse.SC_OK); - response.setCharacterEncoding("UTF-8"); - response.getWriter().write(json.toJSONString()); - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java index c401576006..ba26c838ee 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java @@ -43,6 +43,8 @@ import java.util.List; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + @Component public class AtlasLdapAuthenticationProvider extends AtlasAbstractAuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AtlasLdapAuthenticationProvider.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java index 49970129c3..4214e52a55 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java @@ -42,6 +42,9 @@ import java.util.Map; import java.util.Properties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.UserAuthorityGranter; + @Component public class AtlasPamAuthenticationProvider extends AtlasAbstractAuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AtlasPamAuthenticationProvider.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 5058ebdf53..8e6ff5fe1f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -18,12 +18,12 @@ package org.apache.atlas.web.security; import org.apache.atlas.web.filters.ActiveServerFilter; -import org.apache.atlas.web.filters.AtlasAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; import org.apache.atlas.web.filters.AtlasAuthenticationFilter; import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter; -import org.apache.atlas.web.filters.AtlasDelegatingAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter; -import org.apache.atlas.web.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.HeadersUtil; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; @@ -72,6 +72,9 @@ import javax.inject.Inject; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; + import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -80,7 +83,7 @@ import java.util.Map; import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; -import static org.apache.atlas.web.filters.HeadersUtil.SERVER_KEY; +import static org.apache.atlas.server.common.filters.HeadersUtil.SERVER_KEY; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) diff --git a/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java b/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java index 8ed9daef84..1878777b7b 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java @@ -41,6 +41,8 @@ import java.util.Map; import java.util.Set; +import org.apache.atlas.server.common.security.PamPrincipal; + public class PamLoginModule implements LoginModule { private static final Logger LOG = LoggerFactory.getLogger(PamLoginModule.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java b/webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java deleted file mode 100644 index defd54e413..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java +++ /dev/null @@ -1,33 +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.atlas.web.security; - -import org.springframework.security.authentication.jaas.AuthorityGranter; - -import java.security.Principal; -import java.util.Collections; -import java.util.Set; - -public class UserAuthorityGranter implements AuthorityGranter { - @Override - public Set grant(Principal principal) { - return Collections.singleton("DATA_SCIENTIST"); - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java b/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java index 04566a378c..988831eadb 100644 --- a/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java +++ b/webapp/src/main/java/org/apache/atlas/web/servlets/AtlasHttpServlet.java @@ -17,8 +17,8 @@ */ package org.apache.atlas.web.servlets; -import org.apache.atlas.web.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.web.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java b/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java index e675a36457..9de0f425bb 100644 --- a/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java @@ -39,6 +39,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import org.apache.atlas.server.common.util.Servlets; + @Provider @Produces(MediaType.APPLICATION_JSON) @Component diff --git a/webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java b/webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java deleted file mode 100755 index dbdcd98e42..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java +++ /dev/null @@ -1,53 +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.atlas.web.util; - -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.regex.Pattern; - -/** - * Support function to parse and format date. - */ -public final class DateTimeHelper { - public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - private static final String DATE_PATTERN = "(2\\d\\d\\d|19\\d\\d)-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])T" + "([0-1][0-9]|2[0-3]):([0-5][0-9])Z"; - private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); - - private static final ThreadLocal DATE_FORMAT = ThreadLocal.withInitial(() -> { - DateFormat dateFormat = new SimpleDateFormat(ISO8601_FORMAT); - - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - - return dateFormat; - }); - - private DateTimeHelper() { - } - - public static DateFormat getDateFormat() { - return DATE_FORMAT.get(); - } - - public static String formatDateUTC(Date date) { - return (date != null) ? getDateFormat().format(date) : null; - } -} diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml index 3020690dca..4bdace505c 100644 --- a/webapp/src/main/resources/spring-security.xml +++ b/webapp/src/main/resources/spring-security.xml @@ -64,13 +64,13 @@ + class="org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler" /> + class="org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler" /> + class="org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint"> diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml index 7bc9319205..f80d002f18 100755 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -84,7 +84,7 @@ HeaderFilter - org.apache.atlas.web.filters.AtlasHeaderFilter + org.apache.atlas.server.common.filters.AtlasHeaderFilter diff --git a/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java b/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java index f424b34809..28d8e4b938 100644 --- a/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java @@ -19,7 +19,7 @@ package org.apache.atlas.web.dao; import org.apache.atlas.web.model.User; -import org.apache.atlas.web.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java index 8d2c747489..01c27d797e 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java @@ -33,6 +33,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; + public class AtlasAuthenticationEntryPointTest { @Mock private HttpServletRequest request; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java index 9057ae5798..cf5156569b 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java @@ -24,7 +24,7 @@ import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; import org.apache.commons.configuration.PropertiesConfiguration; @@ -97,6 +97,9 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.RestUtil; + public class AtlasAuthenticationFilterTest { private static final String AUTH_COOKIE = AuthenticatedURL.AUTH_COOKIE; private static final String TIMEOUT_ACTION = "timeout"; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java index 8e27e85711..b047799cd7 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java @@ -44,6 +44,10 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.testng.Assert.assertNotNull; +import org.apache.atlas.server.common.filters.AtlasHeaderFilter; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; + public class AtlasHeaderFilterTest { @Mock private FilterConfig mockFilterConfig; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java index f5eec9f4d8..cf7556d10d 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java @@ -69,6 +69,8 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; + public class AtlasKnoxSSOAuthenticationFilterTest { @Mock private HttpServletRequest servletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java index d3927f698e..3502ab0c61 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java @@ -30,6 +30,9 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; + public class HeaderUtilsTest { private Map originalHeaders; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java index bb443cb627..196181acf0 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java @@ -28,6 +28,8 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; +import org.apache.atlas.server.common.filters.RestUtil; + public class RestUtilTest { private static Enumeration enumeration(T... values) { return Collections.enumeration(java.util.Arrays.asList(values)); diff --git a/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java b/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java index 3d2ba97d51..346f552707 100755 --- a/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java +++ b/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java @@ -79,6 +79,8 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.util.Servlets; + /** * Integration tests for Entity Jersey Resource. */ diff --git a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java index ef1c7fce1d..9b578a9986 100644 --- a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java @@ -68,7 +68,7 @@ import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; import org.apache.atlas.web.service.ServiceState; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; import org.mockito.MockedStatic; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java index 1f2c5ca40e..bbd06f7c50 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java @@ -39,7 +39,7 @@ import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.util.FileUtils; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java index bc66d2cbb8..35d44304ea 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java @@ -23,7 +23,7 @@ import org.apache.atlas.model.instance.AtlasRelationship.AtlasRelationshipWithExtInfo; import org.apache.atlas.repository.store.graph.AtlasRelationshipStore; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java index 257a4b5ec5..24189d257d 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java @@ -33,7 +33,7 @@ import org.apache.atlas.store.AtlasTypeDefStore; import org.apache.atlas.type.AtlasTypeUtil; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.Servlets; +import org.apache.atlas.server.common.util.Servlets; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java index 4cc36d5dd2..bb96de8662 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java @@ -51,6 +51,8 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + public class AtlasADAuthenticationProviderTest { @Mock private Configuration mockConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java index b351e0f440..395f0a0c7c 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java @@ -43,6 +43,8 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; + public class AtlasAuthenticationFailureHandlerTest { @Mock private HttpServletRequest mockHttpServletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java index d06b34e06c..fc20693005 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java @@ -49,6 +49,8 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + public class AtlasAuthenticationProviderTest { @Mock private AtlasLdapAuthenticationProvider mockLdapAuthenticationProvider; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java index b6dfabe11f..871995b2a8 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java @@ -52,6 +52,8 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; + public class AtlasAuthenticationSuccessHandlerTest { @Mock private HttpServletRequest mockHttpServletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java index c63d61e89a..0ed0e2a05b 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java @@ -60,6 +60,9 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.UserAuthorityGranter; + public class AtlasPamAuthenticationProviderTest { @Mock private Configuration mockConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java index 46c5e2111c..7609b58709 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java @@ -20,10 +20,10 @@ package org.apache.atlas.web.security; import org.apache.atlas.web.filters.ActiveServerFilter; -import org.apache.atlas.web.filters.AtlasAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; import org.apache.atlas.web.filters.AtlasAuthenticationFilter; import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter; -import org.apache.atlas.web.filters.AtlasDelegatingAuthenticationEntryPoint; +import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; @@ -96,6 +96,9 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; + @SuppressWarnings("deprecation") public class AtlasSecurityConfigTest { @Mock diff --git a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java index b753bebedd..4d589addc7 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java @@ -46,6 +46,8 @@ import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; + public class FileAuthenticationTest { private static final Logger LOG = LoggerFactory.getLogger(FileAuthenticationTest.class); diff --git a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java index 9a9c43fe59..5fa75ce664 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java @@ -54,6 +54,8 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.security.PamPrincipal; + public class PamLoginModuleTest { @Mock private Subject mockSubject; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java index 12ce66f036..e7c71b7f9b 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java @@ -38,6 +38,8 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.PamPrincipal; + public class PamPrincipalTest { @Mock private UnixUser mockUnixUser; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java b/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java index 79c71a754a..a1aded171d 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java @@ -32,6 +32,8 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.security.UserAuthorityGranter; + public class UserAuthorityGranterTest { @Mock private Principal mockPrincipal; diff --git a/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java b/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java index 4fa04b60a5..9b76bbe7df 100644 --- a/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java @@ -33,6 +33,8 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import org.apache.atlas.server.common.util.DateTimeHelper; + public class DateTimeHelperTest { @Test public void testConstantsAndPattern() { diff --git a/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java b/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java index c3635eca1c..a8de350eeb 100644 --- a/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java @@ -26,6 +26,8 @@ import static org.testng.Assert.assertNotNull; +import org.apache.atlas.server.common.util.Servlets; + @Test public class ServletsTest { public void testEmptyMessage() throws Exception { diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index f77c20bd68..efb59e42d9 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -82,13 +82,13 @@ + class="org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler" /> + class="org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler" /> + class="org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint"> From 0ccba6d7f158a8436a976b36b84718e9a44bc3ba Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 16 Apr 2026 12:34:29 +0530 Subject: [PATCH 04/22] Resolving error's in webapp for testCompile: unreported javax.servlet.ServletException and in rest-notification webapp NullServletContext method does not override or implement a method from a supertype, these was because javax.servlet-api 3.1 and there is no support for methods which are using servlet-4.0. --- .../common/filters/NullServletContext.java | 7 ----- .../AtlasAuthenticationEntryPointTest.java | 5 ++-- ...AtlasAuthenticationFailureHandlerTest.java | 29 ++++++++++--------- ...AtlasAuthenticationSuccessHandlerTest.java | 17 ++++++----- 4 files changed, 27 insertions(+), 31 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java index eb1595a149..51a02e02f6 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java @@ -64,30 +64,24 @@ public String getVirtualServerName() { return null; } - @Override public int getSessionTimeout() { return 0; } - @Override public void setSessionTimeout(int i) { } - @Override public String getRequestCharacterEncoding() { return ""; } - @Override public void setRequestCharacterEncoding(String s) { } - @Override public String getResponseCharacterEncoding() { return ""; } - @Override public void setResponseCharacterEncoding(String s) { } @@ -238,7 +232,6 @@ public javax.servlet.ServletRegistration.Dynamic addServlet( return null; } - @Override public ServletRegistration.Dynamic addJspFile(String s, String s1) { return null; } diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java index 01c27d797e..5d9b7e9235 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java @@ -24,6 +24,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -54,7 +55,7 @@ public void setup() { } @Test - public void testCommence_AjaxRequest() throws IOException { + public void testCommence_AjaxRequest() throws IOException, ServletException { // Arrange when(request.getHeader("X-Requested-With")).thenReturn("XMLHttpRequest"); @@ -67,7 +68,7 @@ public void testCommence_AjaxRequest() throws IOException { } @Test - public void testCommence_NormalRequest() throws IOException { + public void testCommence_NormalRequest() throws IOException, ServletException { // Arrange when(request.getHeader("X-Requested-With")).thenReturn(null); diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java index 395f0a0c7c..9b88d166cd 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java @@ -28,6 +28,7 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -62,7 +63,7 @@ public class AtlasAuthenticationFailureHandlerTest { private StringWriter stringWriter; @BeforeMethod - public void setUp() throws IOException { + public void setUp() throws IOException, ServletException { MockitoAnnotations.openMocks(this); authenticationFailureHandler = new AtlasAuthenticationFailureHandler(); stringWriter = new StringWriter(); @@ -72,7 +73,7 @@ public void setUp() throws IOException { } @Test - public void testOnAuthenticationFailure_WithValidException() throws IOException { + public void testOnAuthenticationFailure_WithValidException() throws IOException, ServletException { // Setup String exceptionMessage = "Authentication failed for user"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -94,7 +95,7 @@ public void testOnAuthenticationFailure_WithValidException() throws IOException } @Test - public void testOnAuthenticationFailure_WithNullExceptionMessage() throws IOException { + public void testOnAuthenticationFailure_WithNullExceptionMessage() throws IOException, ServletException { // Setup when(mockAuthenticationException.getMessage()).thenReturn(null); @@ -110,7 +111,7 @@ public void testOnAuthenticationFailure_WithNullExceptionMessage() throws IOExce } @Test - public void testOnAuthenticationFailure_WithEmptyExceptionMessage() throws IOException { + public void testOnAuthenticationFailure_WithEmptyExceptionMessage() throws IOException, ServletException { // Setup String exceptionMessage = ""; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -127,7 +128,7 @@ public void testOnAuthenticationFailure_WithEmptyExceptionMessage() throws IOExc } @Test - public void testOnAuthenticationFailure_WithSpecialCharactersInMessage() throws IOException { + public void testOnAuthenticationFailure_WithSpecialCharactersInMessage() throws IOException, ServletException { // Setup String exceptionMessage = "Authentication failed: Special chars !@#$%^&*(){}[]|\\:;\"'<>,.?/~`"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -145,7 +146,7 @@ public void testOnAuthenticationFailure_WithSpecialCharactersInMessage() throws } @Test - public void testOnAuthenticationFailure_WithLongExceptionMessage() throws IOException { + public void testOnAuthenticationFailure_WithLongExceptionMessage() throws IOException, ServletException { // Setup StringBuilder longMessage = new StringBuilder(); for (int i = 0; i < 1000; i++) { @@ -167,7 +168,7 @@ public void testOnAuthenticationFailure_WithLongExceptionMessage() throws IOExce } @Test - public void testOnAuthenticationFailure_WithUnicodeCharacters() throws IOException { + public void testOnAuthenticationFailure_WithUnicodeCharacters() throws IOException, ServletException { // Setup String exceptionMessage = "认证失败 - Authentication failed with 中文 characters and émojis 🔐"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -185,7 +186,7 @@ public void testOnAuthenticationFailure_WithUnicodeCharacters() throws IOExcepti } @Test - public void testOnAuthenticationFailure_VerifyJSONContent() throws IOException { + public void testOnAuthenticationFailure_VerifyJSONContent() throws IOException, ServletException { // Setup String exceptionMessage = "Test authentication failure"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -211,7 +212,7 @@ public void testOnAuthenticationFailure_VerifyJSONContent() throws IOException { } @Test - public void testOnAuthenticationFailure_VerifyJSONContentWithNullMessage() throws IOException { + public void testOnAuthenticationFailure_VerifyJSONContentWithNullMessage() throws IOException, ServletException { // Setup when(mockAuthenticationException.getMessage()).thenReturn(null); @@ -233,7 +234,7 @@ public void testOnAuthenticationFailure_VerifyJSONContentWithNullMessage() throw } @Test - public void testOnAuthenticationFailure_WithIOException() throws IOException { + public void testOnAuthenticationFailure_WithIOException() throws IOException, ServletException { // Setup String exceptionMessage = "Authentication failed"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -255,7 +256,7 @@ public void testOnAuthenticationFailure_WithIOException() throws IOException { } @Test - public void testOnAuthenticationFailure_ResponseStatusValue() throws IOException { + public void testOnAuthenticationFailure_ResponseStatusValue() throws IOException, ServletException { // Setup String exceptionMessage = "Authentication failed"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -268,7 +269,7 @@ public void testOnAuthenticationFailure_ResponseStatusValue() throws IOException } @Test - public void testOnAuthenticationFailure_MethodCallOrder() throws IOException { + public void testOnAuthenticationFailure_MethodCallOrder() throws IOException, ServletException { // Setup String exceptionMessage = "Authentication failed"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -297,7 +298,7 @@ public void testClassAnnotations() { } @Test - public void testJSONObjectCreation() throws IOException { + public void testJSONObjectCreation() throws IOException, ServletException { // Setup String exceptionMessage = "Test message"; when(mockAuthenticationException.getMessage()).thenReturn(exceptionMessage); @@ -321,7 +322,7 @@ public void testJSONObjectCreation() throws IOException { } @Test - public void testOnAuthenticationFailure_AllExecutionPaths() throws IOException { + public void testOnAuthenticationFailure_AllExecutionPaths() throws IOException, ServletException { // This test ensures all lines of code in the onAuthenticationFailure method are executed // Setup diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java index 871995b2a8..14574a5539 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java @@ -31,6 +31,7 @@ import org.testng.annotations.Test; import javax.servlet.ServletContext; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @@ -76,7 +77,7 @@ public class AtlasAuthenticationSuccessHandlerTest { private AtlasAuthenticationSuccessHandler authenticationSuccessHandler; @BeforeMethod - public void setUp() throws IOException { + public void setUp() throws IOException, ServletException { MockitoAnnotations.openMocks(this); authenticationSuccessHandler = new AtlasAuthenticationSuccessHandler(); @@ -106,7 +107,7 @@ public void testSetup_WithNegativeTimeout() throws Exception { } @Test - public void testOnAuthenticationSuccess_WithSession() throws IOException { + public void testOnAuthenticationSuccess_WithSession() throws IOException, ServletException { // Setup String sessionId = "test-session-id"; Object principal = "test-user"; @@ -139,7 +140,7 @@ public void testOnAuthenticationSuccess_WithSession() throws IOException { } @Test - public void testOnAuthenticationSuccess_WithSessionAndNegativeTimeout() throws IOException { + public void testOnAuthenticationSuccess_WithSessionAndNegativeTimeout() throws IOException, ServletException { // Setup String sessionId = "test-session-id"; Object principal = "test-user"; @@ -171,7 +172,7 @@ public void testOnAuthenticationSuccess_WithSessionAndNegativeTimeout() throws I } @Test - public void testOnAuthenticationSuccess_WithoutSession() throws IOException { + public void testOnAuthenticationSuccess_WithoutSession() throws IOException, ServletException { // Setup Object principal = "test-user"; @@ -197,7 +198,7 @@ public void testOnAuthenticationSuccess_WithoutSession() throws IOException { } @Test - public void testOnAuthenticationSuccess_VerifyJSONContent() throws IOException { + public void testOnAuthenticationSuccess_VerifyJSONContent() throws IOException, ServletException { // Setup Object principal = "test-user"; when(mockAuthentication.getPrincipal()).thenReturn(principal); @@ -227,7 +228,7 @@ public void testOnAuthenticationSuccess_VerifyJSONContent() throws IOException { } @Test - public void testOnAuthenticationSuccess_VerifyResponseStatusValue() throws IOException { + public void testOnAuthenticationSuccess_VerifyResponseStatusValue() throws IOException, ServletException { // Setup Object principal = "test-user"; when(mockAuthentication.getPrincipal()).thenReturn(principal); @@ -241,7 +242,7 @@ public void testOnAuthenticationSuccess_VerifyResponseStatusValue() throws IOExc } @Test - public void testOnAuthenticationSuccess_WithIOException() throws IOException { + public void testOnAuthenticationSuccess_WithIOException() throws IOException, ServletException { // Setup Object principal = "test-user"; when(mockAuthentication.getPrincipal()).thenReturn(principal); @@ -264,7 +265,7 @@ public void testOnAuthenticationSuccess_WithIOException() throws IOException { } @Test - public void testOnAuthenticationSuccess_WithComplexPrincipal() throws IOException { + public void testOnAuthenticationSuccess_WithComplexPrincipal() throws IOException, ServletException { // Setup with complex principal object Object complexPrincipal = new Object() { @Override From a3030c82c44d218d671aafc447254ebe11e73076 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 16 Apr 2026 13:09:22 +0530 Subject: [PATCH 05/22] Modifying class syntax to match both webapp and rest-notification webapp in server-common module. --- .../server/common/LocalServletRequest.java | 1 - .../AtlasAuthenticationEntryPoint.java | 4 +- ...lasDelegatingAuthenticationEntryPoint.java | 10 ++-- .../common/filters/AtlasHeaderFilter.java | 9 ++- .../server/common/filters/HeadersUtil.java | 37 ++++++------ .../common/filters/NullServletContext.java | 60 ++++++++++++++++++- .../atlas/server/common/filters/RestUtil.java | 41 +++++++++---- .../filters/SSOAuthenticationProperties.java | 2 +- .../AtlasAuthenticationFailureHandler.java | 5 +- .../AtlasAuthenticationSuccessHandler.java | 14 +++-- .../server/common/security/PamPrincipal.java | 17 +++--- .../common/security/UserAuthorityGranter.java | 1 + .../server/common/util/DateTimeHelper.java | 11 ++-- .../atlas/server/common/util/Servlets.java | 27 +++++++-- 14 files changed, 167 insertions(+), 72 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java b/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java index 6483af18c6..3bf79ae025 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java @@ -32,7 +32,6 @@ import javax.servlet.http.HttpSession; import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; - import java.io.BufferedReader; import java.io.IOException; import java.io.UnsupportedEncodingException; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java index 4b2cd419e1..62368c4729 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java @@ -27,12 +27,12 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; @SuppressWarnings("deprecation") @Component public class AtlasAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationEntryPoint.class); private String loginPath = "/login.jsp"; @@ -45,6 +45,8 @@ public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + + String ajaxRequestHeader = request.getHeader("X-Requested-With"); response.setHeader("X-Frame-Options", "DENY"); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java index 1ee31b7f20..1115c74206 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java @@ -25,13 +25,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; import java.util.LinkedHashMap; public class AtlasDelegatingAuthenticationEntryPoint extends DelegatingAuthenticationEntryPoint { - public static final String SESSION_TIMEOUT = "Session Timeout"; - private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); + + public static final String SESSION_TIMEOUT = "Session Timeout"; + private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); + public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { super(entryPoints); @@ -41,7 +42,8 @@ public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap HEADER_MAP = new HashMap<>(); private HeadersUtil() { // to block instantiation } - public static String getHeaderMap(String header) { return HEADER_MAP.get(header); } @@ -107,4 +106,4 @@ public static void initializeHttpResponseHeaders(Properties configuredHeaders) { initializeHttpResponseHeaders(configuredHeaders); } -} +} \ No newline at end of file diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java index 51a02e02f6..e92e6f03c2 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java @@ -25,7 +25,6 @@ import javax.servlet.SessionCookieConfig; import javax.servlet.SessionTrackingMode; import javax.servlet.descriptor.JspConfigDescriptor; - import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; @@ -34,32 +33,43 @@ import java.util.Map; import java.util.Set; + /** */ public class NullServletContext implements ServletContext { + + + public void setSessionTrackingModes( Set sessionTrackingModes) { } + public boolean setInitParameter(String name, String value) { return false; } + public void setAttribute(String name, Object object) { } + public void removeAttribute(String name) { } + public void log(String message, Throwable throwable) { } + public void log(Exception exception, String msg) { } + public void log(String msg) { } + public String getVirtualServerName() { return null; } @@ -85,186 +95,234 @@ public String getResponseCharacterEncoding() { public void setResponseCharacterEncoding(String s) { } + public SessionCookieConfig getSessionCookieConfig() { return null; } + public Enumeration getServlets() { return null; } + public Map getServletRegistrations() { return null; } + public ServletRegistration getServletRegistration(String servletName) { return null; } + public Enumeration getServletNames() { return null; } + public String getServletContextName() { return null; } + public Servlet getServlet(String name) throws ServletException { return null; } + public String getServerInfo() { return null; } + public Set getResourcePaths(String path) { return null; } + public InputStream getResourceAsStream(String path) { return null; } + public URL getResource(String path) throws MalformedURLException { return null; } + public RequestDispatcher getRequestDispatcher(String path) { return null; } + public String getRealPath(String path) { return null; } + public RequestDispatcher getNamedDispatcher(String name) { return null; } + public int getMinorVersion() { return 0; } + public String getMimeType(String file) { return null; } + public int getMajorVersion() { return 0; } + public JspConfigDescriptor getJspConfigDescriptor() { return null; } + public Enumeration getInitParameterNames() { return null; } + public String getInitParameter(String name) { return null; } + public Map getFilterRegistrations() { return null; } + public FilterRegistration getFilterRegistration(String filterName) { return null; } + public Set getEffectiveSessionTrackingModes() { return null; } + public int getEffectiveMinorVersion() { return 0; } + public int getEffectiveMajorVersion() { return 0; } + public Set getDefaultSessionTrackingModes() { return null; } + public String getContextPath() { return null; } + public ServletContext getContext(String uripath) { return null; } + public ClassLoader getClassLoader() { return null; } + public Enumeration getAttributeNames() { return null; } + public Object getAttribute(String name) { return null; } + public void declareRoles(String... roleNames) { } + public T createServlet(Class clazz) throws ServletException { return null; } + public T createListener(Class clazz) throws ServletException { return null; } + public T createFilter(Class clazz) throws ServletException { return null; } + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, Class servletClass) { return null; } + public ServletRegistration.Dynamic addJspFile(String s, String s1) { return null; } + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, Servlet servlet) { return null; } + public javax.servlet.ServletRegistration.Dynamic addServlet( String servletName, String className) { return null; } + public void addListener(Class listenerClass) { } + public void addListener(T t) { } + public void addListener(String className) { } + public Dynamic addFilter(String filterName, Class filterClass) { return null; } + public Dynamic addFilter(String filterName, Filter filter) { return null; } + public Dynamic addFilter(String filterName, String className) { return null; } + + + } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java index c711393c19..26f92a2757 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/RestUtil.java @@ -18,7 +18,7 @@ package org.apache.atlas.server.common.filters; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,10 +27,12 @@ import java.util.Enumeration; public class RestUtil { - public static final String TIMEOUT_ACTION = "timeout"; - public static final String LOGOUT_URL = "/logout.html"; - public static final String DELIMITTER = "://"; - private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(RestUtil.class); + + public static final String TIMEOUT_ACTION = "timeout"; + public static final String LOGOUT_URL = "/logout.html"; + public static final String DELIMITTER = "://"; + private static final String PROXY_ATLAS_URL_PATH = "/atlas"; private static final String X_FORWARDED_PROTO = "x-forwarded-proto"; private static final String X_FORWARDED_HOST = "x-forwarded-host"; @@ -45,16 +47,19 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { String xForwardedHost = ""; String xForwardedContext = ""; Enumeration headerNames = httpRequest.getHeaderNames(); + while (headerNames.hasMoreElements()) { String name = (String) headerNames.nextElement(); Enumeration values = httpRequest.getHeaders(name); String value = ""; + if (values != null) { while (values.hasMoreElements()) { value = (String) values.nextElement(); } } - if (StringUtils.trimToNull(name) != null && StringUtils.trimToNull(value) != null) { + + if (org.apache.commons.lang3.StringUtils.trimToNull(name) != null && org.apache.commons.lang3.StringUtils.trimToNull(value) != null) { if (name.equalsIgnoreCase(X_FORWARDED_PROTO)) { xForwardedProto = value; } else if (name.equalsIgnoreCase(X_FORWARDED_HOST)) { @@ -64,49 +69,59 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { } } } + if (xForwardedHost.contains(",")) { - if (LOG.isDebugEnabled()) { - LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); - } + LOG.debug("xForwardedHost value is " + xForwardedHost + " it contains multiple hosts, selecting the first host."); + xForwardedHost = xForwardedHost.split(",")[0].trim(); } + String xForwardedURL = ""; - if (StringUtils.trimToNull(xForwardedProto) != null) { + + if (org.apache.commons.lang3.StringUtils.trimToNull(xForwardedProto) != null) { //if header contains x-forwarded-host and x-forwarded-context - if (StringUtils.trimToNull(xForwardedHost) != null && StringUtils.trimToNull(xForwardedContext) != null) { + if (org.apache.commons.lang3.StringUtils.trimToNull(xForwardedHost) != null && org.apache.commons.lang3.StringUtils.trimToNull(xForwardedContext) != null) { xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + httpRequest.getRequestURI(); - } else if (StringUtils.trimToNull(xForwardedHost) != null) { + } else if (org.apache.commons.lang3.StringUtils.trimToNull(xForwardedHost) != null) { //if header contains x-forwarded-host and does not contains x-forwarded-context xForwardedURL = xForwardedProto + DELIMITTER + xForwardedHost + httpRequest.getRequestURI(); } else { //if header does not contains x-forwarded-host and x-forwarded-context //preserve the x-forwarded-proto value coming from the request. String requestURL = httpRequest.getRequestURL().toString(); - if (StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { + + if (org.apache.commons.lang3.StringUtils.trimToNull(requestURL) != null && requestURL.startsWith("http:")) { requestURL = requestURL.replaceFirst("http", xForwardedProto); } + xForwardedURL = requestURL; } } + return xForwardedURL; } public static String constructRedirectURL(HttpServletRequest request, String redirectUrl, String xForwardedURL, String originalUrlQueryParam) { String delimiter = "?"; + if (redirectUrl.contains("?")) { delimiter = "&"; } + String loginURL = redirectUrl + delimiter + originalUrlQueryParam + "="; + if (StringUtils.trimToNull(xForwardedURL) != null) { loginURL += xForwardedURL; } else { loginURL += request.getRequestURL().append(getOriginalQueryString(request)); } + return loginURL; } private static String getOriginalQueryString(HttpServletRequest request) { String originalQueryString = request.getQueryString(); + return (originalQueryString == null) ? "" : "?" + originalQueryString; } } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java index 8d16e07947..3e260ce904 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthenticationProperties.java @@ -24,7 +24,7 @@ public class SSOAuthenticationProperties { private String authenticationProviderUrl; private RSAPublicKey publicKey; - private String cookieName = "hadoop-jwt"; + private String cookieName = "hadoop-jwt"; private String originalUrlQueryParam; private String[] userAgentList; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java index 307ddb4fa8..452b254869 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationFailureHandler.java @@ -36,16 +36,17 @@ public class AtlasAuthenticationFailureHandler implements AuthenticationFailureH private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFailureHandler.class); @Override - public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, - AuthenticationException e) throws IOException, ServletException { + public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException { LOG.debug("Login Failure ", e); JSONObject json = new JSONObject(); + json.put("msgDesc", e.getMessage()); response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.setCharacterEncoding("UTF-8"); + response.getWriter().write(json.toJSONString()); } } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java index 7c76d9acd8..c73a09e130 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java @@ -30,14 +30,15 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; + @Component public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { - public static final String LOCALLOGIN = "locallogin"; - private static final Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); - private int sessionTimeout = 3600; + + private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); + private int sessionTimeout = 3600; + public static final String LOCALLOGIN = "locallogin"; @PostConstruct public void setup() { @@ -46,14 +47,15 @@ public void setup() { @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { + Authentication authentication) throws IOException, ServletException { + LOG.debug("Login Success " + authentication.getPrincipal()); JSONObject json = new JSONObject(); json.put("msgDesc", "Success"); if (request.getSession() != null) { // incase of form based login mark it as local login in session - request.getSession().setAttribute(LOCALLOGIN, "true"); + request.getSession().setAttribute(LOCALLOGIN,"true"); request.getServletContext().setAttribute(request.getSession().getId(), LOCALLOGIN); if (this.sessionTimeout != -1) { diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java b/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java index 328cc81c2d..9fb5759bf8 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java @@ -25,17 +25,18 @@ import java.util.Collections; import java.util.Set; -public class PamPrincipal extends Object implements Principal { - private String userName; - private String gecos; - private String homeDir; - private String shell; - private int uid; - private int gid; - private Set groups; +public class PamPrincipal implements Principal { + private final String userName; + private final String gecos; + private final String homeDir; + private final String shell; + private final int uid; + private final int gid; + private final Set groups; public PamPrincipal(UnixUser user) { super(); + userName = user.getUserName(); gecos = user.getGecos(); homeDir = user.getDir(); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java index 625078839a..cb89add61d 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java @@ -26,6 +26,7 @@ import java.util.Set; public class UserAuthorityGranter implements AuthorityGranter { + @Override public Set grant(Principal principal) { Collections.singleton("DATA_SCIENTIST"); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java index b3629beb9e..f2cfb8c184 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java @@ -28,12 +28,13 @@ * Support function to parse and format date. */ public final class DateTimeHelper { - public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - private static final String DATE_PATTERN = + + public static final String ISO8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + private static final String DATE_PATTERN = "(2\\d\\d\\d|19\\d\\d)-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])T" + "([0-1][0-9]|2[0-3]):([0-5][0-9])Z"; - private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); + private static final Pattern PATTERN = Pattern.compile(DATE_PATTERN); - private static final ThreadLocal DATE_FORMAT = new ThreadLocal() { + private static ThreadLocal DATE_FORMAT = new ThreadLocal() { @Override public DateFormat initialValue() { DateFormat dateFormat = new SimpleDateFormat(ISO8601_FORMAT); @@ -52,4 +53,4 @@ public static DateFormat getDateFormat() { public static String formatDateUTC(Date date) { return (date != null) ? getDateFormat().format(date) : null; } -} +} \ No newline at end of file diff --git a/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java b/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java index d4c749ccd9..3b4f8c93b2 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/util/Servlets.java @@ -43,6 +43,7 @@ import java.io.IOException; import java.io.StringWriter; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,9 +52,11 @@ * Utility functions for dealing with servlets. */ public final class Servlets { - public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; - public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; - private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); + private static final Logger LOG = LoggerFactory.getLogger(Servlets.class); + + public static final String JSON_MEDIA_TYPE = MediaType.APPLICATION_JSON + "; charset=UTF-8"; + public static final String BINARY = MediaType.APPLICATION_OCTET_STREAM; + private static final int QUERY_PARAM_MAX_LENGTH = AtlasConfiguration.QUERY_PARAM_MAX_LENGTH.getInt(); private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); private static final String DO_AS = "doAs"; @@ -65,26 +68,30 @@ private Servlets() { /** * Returns the user of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the user */ public static String getUserFromRequest(HttpServletRequest httpRequest) { String user = httpRequest.getRemoteUser(); + if (!StringUtils.isEmpty(user)) { return user; } user = httpRequest.getParameter("user.name"); // available in query-param + if (!StringUtils.isEmpty(user)) { return user; } user = httpRequest.getHeader("Remote-User"); // backwards-compatibility + if (!StringUtils.isEmpty(user)) { return user; } user = getDoAsUser(httpRequest); + if (!StringUtils.isEmpty(user)) { return user; } @@ -95,6 +102,7 @@ public static String getUserFromRequest(HttpServletRequest httpRequest) { public static String getDoAsUser(HttpServletRequest request) { if (StringUtils.isNoneEmpty(request.getQueryString())) { List list = URLEncodedUtils.parse(request.getQueryString(), UTF8_CHARSET); + if (list != null) { for (NameValuePair nv : list) { if (DO_AS.equals(nv.getName())) { @@ -103,17 +111,19 @@ public static String getDoAsUser(HttpServletRequest request) { } } } + return null; } /** * Returns the URI of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the URI, including the query string */ public static String getRequestURI(HttpServletRequest httpRequest) { final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURI()); + if (httpRequest.getQueryString() != null) { url.append('?').append(httpRequest.getQueryString()); } @@ -124,11 +134,12 @@ public static String getRequestURI(HttpServletRequest httpRequest) { /** * Returns the full URL of the given request. * - * @param httpRequest an HTTP servlet request + * @param httpRequest an HTTP servlet request * @return the full URL, including the query string */ public static String getRequestURL(HttpServletRequest httpRequest) { final StringBuilder url = new StringBuilder(100).append(httpRequest.getRequestURL()); + if (httpRequest.getQueryString() != null) { url.append('?').append(httpRequest.getQueryString()); } @@ -164,7 +175,9 @@ public static String getRequestPayload(HttpServletRequest request) throws IOExce } StringWriter writer = new StringWriter(); + IOUtils.copy(request.getInputStream(), writer); + return writer.toString(); } @@ -174,6 +187,7 @@ public static String getRequestId() { public static String escapeJsonString(String inputStr) { ParamChecker.notNull(inputStr, "Input String cannot be null"); + return StringEscapeUtils.escapeJson(inputStr); } @@ -215,6 +229,7 @@ public static String decodeQueryString(String query) throws AtlasBaseException { return UriUtils.decode(query, "UTF-8"); } catch (Exception e) { LOG.error("Error occurred while decoding query:" + query, e.getMessage()); + throw new AtlasBaseException(e.getMessage()); } } From c3e2bf6d1180de724d0ccfee0ed2157dae87feab Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 16 Apr 2026 15:14:32 +0530 Subject: [PATCH 06/22] Updating dao class and package to use server-common module for both webapp and rest-notification-webapp and Updating method visibility to resolve compile time error for UserDaoTest (testMergePasswordAndSalt_EmptySalt) method. --- .../rest/util/CredentialProviderUtility.java | 2 +- .../notification/rest/web/dao/UserDao.java | 266 ------------------ .../AtlasFileAuthenticationProvider.java | 2 +- .../rest/web/service/UserService.java | 6 +- server-common/pom.xml | 4 + .../atlas/server/common}/dao/UserDao.java | 9 +- .../atlas/util/CredentialProviderUtility.java | 2 +- .../AtlasFileAuthenticationProvider.java | 2 +- .../apache/atlas/web/service/UserService.java | 6 +- .../org/apache/atlas/web/dao/UserDaoTest.java | 5 +- .../atlas/web/security/UserDaoTest.java | 8 +- .../atlas/web/service/UserServiceTest.java | 34 +-- .../test/resources/test-spring-security.xml | 2 +- 13 files changed, 44 insertions(+), 304 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/dao/UserDao.java (95%) diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java index bc1415c0f8..d50368cfa5 100755 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java @@ -16,7 +16,7 @@ */ package org.apache.atlas.notification.rest.util; -import org.apache.atlas.notification.rest.web.dao.UserDao; +import org.apache.atlas.server.common.dao.UserDao; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java deleted file mode 100644 index 760e313ef8..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/dao/UserDao.java +++ /dev/null @@ -1,266 +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.atlas.notification.rest.web.dao; - -import com.google.common.annotations.VisibleForTesting; -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.atlas.notification.rest.web.model.User; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; -import org.apache.commons.configuration.Configuration; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.crypto.bcrypt.BCrypt; -import org.springframework.security.crypto.codec.Hex; -import org.springframework.security.crypto.codec.Utf8; -import org.springframework.stereotype.Repository; -import org.springframework.util.StringUtils; - -import javax.annotation.PostConstruct; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.List; -import java.util.Properties; - - -@Repository -public class UserDao { - private static final Logger LOG = LoggerFactory.getLogger(UserDao.class); - - private static final String DEFAULT_USER_CREDENTIALS_PROPERTIES = "users-credentials.properties"; - private static boolean v1ValidationEnabled = true; - private static boolean v2ValidationEnabled = true; - - private Properties userLogins = new Properties(); - - @PostConstruct - public void init() { - loadFileLoginsDetails(); - } - - void loadFileLoginsDetails() { - userLogins.clear(); - - InputStream inStr = null; - - try { - Configuration configuration = ApplicationProperties.get(); - - v1ValidationEnabled = configuration.getBoolean("atlas.authentication.method.file.v1-validation.enabled", true); - v2ValidationEnabled = configuration.getBoolean("atlas.authentication.method.file.v2-validation.enabled", true); - - inStr = ApplicationProperties.getFileAsInputStream(configuration, "atlas.authentication.method.file.filename", DEFAULT_USER_CREDENTIALS_PROPERTIES); - - userLogins.load(inStr); - } catch (IOException | AtlasException e) { - LOG.error("Error while reading user.properties file", e); - - throw new RuntimeException(e); - } finally { - if (inStr != null) { - try { - inStr.close(); - } catch (Exception excp) { - // ignore - } - } - } - } - - public User loadUserByUsername(final String username) throws AuthenticationException { - String userdetailsStr = userLogins.getProperty(username); - - if (userdetailsStr == null || userdetailsStr.isEmpty()) { - throw new UsernameNotFoundException("Username not found." + username); - } - - String password = ""; - String role = ""; - String[] dataArr = userdetailsStr.split("::"); - - if (dataArr != null && dataArr.length == 2) { - role = dataArr[0]; - password = dataArr[1]; - } else { - LOG.error("User role credentials is not set properly for {}", username); - - throw new AtlasAuthenticationException("User role credentials is not set properly for " + username ); - } - - List grantedAuths = new ArrayList<>(); - - if (StringUtils.hasText(role)) { - grantedAuths.add(new SimpleGrantedAuthority(role)); - } else { - LOG.error("User role credentials is not set properly for {}", username); - - throw new AtlasAuthenticationException("User role credentials is not set properly for " + username ); - } - - User userDetails = new User(username, password, grantedAuths); - - return userDetails; - } - - @VisibleForTesting - public void setUserLogins(Properties userLogins) { - this.userLogins = userLogins; - } - - public static String encrypt(String password) { - String ret = null; - - try { - ret = BCrypt.hashpw(password, BCrypt.gensalt()); - } catch (Throwable excp) { - LOG.warn("UserDao.encrypt(): failed", excp); - } - - return ret; - } - - public static boolean checkEncrypted(String password, String encryptedPwd, String userName) { - boolean ret = checkPasswordBCrypt(password, encryptedPwd); - - if (!ret && v2ValidationEnabled) { - ret = checkPasswordSHA256WithSalt(password, encryptedPwd, userName); - } - - if (!ret && v1ValidationEnabled) { - ret = checkPasswordSHA256(password, encryptedPwd); - } - - return ret; - } - - private static boolean checkPasswordBCrypt(String password, String encryptedPwd) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordBCrypt()"); - } - - boolean ret = false; - - try { - ret = BCrypt.checkpw(password, encryptedPwd); - } catch (Throwable excp) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordBCrypt(): failed", excp); - } - } - - return ret; - } - - private static boolean checkPasswordSHA256WithSalt(String password, String encryptedPwd, String salt) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordSHA256WithSalt()"); - } - - boolean ret = false; - - try { - String hash = encodePassword(password, salt); - - ret = hash != null && hash.equals(encryptedPwd); - } catch (Throwable excp) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordSHA256WithSalt(): failed", excp); - } - } - - return ret; - } - - private static boolean checkPasswordSHA256(String password, String encryptedPwd) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordSHA256()"); - } - - boolean ret = false; - - try { - String hash = getSha256Hash(password); - - ret = hash != null && hash.equals(encryptedPwd); - } catch (Throwable excp) { - if (LOG.isDebugEnabled()) { - LOG.debug("checkPasswordSHA256(): failed", excp); - } - } - - return ret; - } - - private static String getSha256Hash(String base) throws AtlasAuthenticationException { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - byte[] hash = digest.digest(base.getBytes("UTF-8")); - StringBuffer hexString = new StringBuffer(); - - for (byte aHash : hash) { - String hex = Integer.toHexString(0xff & aHash); - - if (hex.length() == 1) { - hexString.append('0'); - } - - hexString.append(hex); - } - - return hexString.toString(); - } catch (Exception ex) { - throw new AtlasAuthenticationException("Exception while encoding password.", ex); - } - } - - public static String encodePassword(String rawPass, Object salt) { - String saltedPass = mergePasswordAndSalt(rawPass, salt, false); - MessageDigest messageDigest = getMessageDigest(); - byte[] digest = messageDigest.digest(Utf8.encode(saltedPass)); - - return new String(Hex.encode(digest)); - } - - protected static final MessageDigest getMessageDigest() throws IllegalArgumentException { - try { - return MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException var2) { - throw new IllegalArgumentException("No such algorithm [SHA-256 ]"); - } - } - - protected static String mergePasswordAndSalt(String password, Object salt, boolean strict) { - if (!StringUtils.hasText(password)) { - password = ""; - } - - if (strict && salt != null && (salt.toString().lastIndexOf("{") != -1 || salt.toString().lastIndexOf("}") != -1)) { - throw new IllegalArgumentException("Cannot use { or } in salt.toString()"); - } else { - return StringUtils.hasText(salt.toString()) ? password + "{" + salt.toString() + "}" : password; - } - } - -} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java index 266a1bafc1..b108cc3ad5 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java @@ -17,7 +17,7 @@ package org.apache.atlas.notification.rest.web.security; -import org.apache.atlas.notification.rest.web.dao.UserDao; +import org.apache.atlas.server.common.dao.UserDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.BadCredentialsException; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java index 1e2597117a..205237627a 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java @@ -17,8 +17,8 @@ package org.apache.atlas.notification.rest.web.service; -import org.apache.atlas.notification.rest.web.dao.UserDao; -import org.apache.atlas.notification.rest.web.model.User; +import org.apache.atlas.server.common.dao.UserDao; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -36,7 +36,7 @@ public UserService(UserDao userDao) { } @Override - public User loadUserByUsername(final String username) + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { return userDao.loadUserByUsername(username); } diff --git a/server-common/pom.xml b/server-common/pom.xml index 2fab66d465..e80583e88e 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -102,6 +102,10 @@ org.springframework.security spring-security-core + + org.springframework.security + spring-security-crypto + org.springframework.security spring-security-web diff --git a/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java b/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java similarity index 95% rename from webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java rename to server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java index 884aa9f334..eeb12f2f4f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/dao/UserDao.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java @@ -15,12 +15,11 @@ * limitations under the License. */ -package org.apache.atlas.web.dao; +package org.apache.atlas.server.common.dao; import com.google.common.annotations.VisibleForTesting; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; -import org.apache.atlas.web.model.User; import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; @@ -28,6 +27,8 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.security.crypto.codec.Hex; @@ -98,7 +99,7 @@ protected static MessageDigest getMessageDigest() throws IllegalArgumentExceptio } } - protected static String mergePasswordAndSalt(String password, Object salt, boolean strict) { + public static String mergePasswordAndSalt(String password, Object salt, boolean strict) { if (!StringUtils.hasText(password)) { password = ""; } @@ -115,7 +116,7 @@ public void init() { loadFileLoginsDetails(); } - public User loadUserByUsername(final String username) throws AuthenticationException { + public UserDetails loadUserByUsername(final String username) throws AuthenticationException { String userdetailsStr = userLogins.getProperty(username); if (userdetailsStr == null || userdetailsStr.isEmpty()) { diff --git a/webapp/src/main/java/org/apache/atlas/util/CredentialProviderUtility.java b/webapp/src/main/java/org/apache/atlas/util/CredentialProviderUtility.java index 182c26dbb9..050e3fca3b 100755 --- a/webapp/src/main/java/org/apache/atlas/util/CredentialProviderUtility.java +++ b/webapp/src/main/java/org/apache/atlas/util/CredentialProviderUtility.java @@ -16,7 +16,7 @@ */ package org.apache.atlas.util; -import org.apache.atlas.web.dao.UserDao; +import org.apache.atlas.server.common.dao.UserDao; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Options; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java index 096ca26d83..4b0b4d70d1 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java @@ -16,7 +16,7 @@ */ package org.apache.atlas.web.security; -import org.apache.atlas.web.dao.UserDao; +import org.apache.atlas.server.common.dao.UserDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.BadCredentialsException; diff --git a/webapp/src/main/java/org/apache/atlas/web/service/UserService.java b/webapp/src/main/java/org/apache/atlas/web/service/UserService.java index e0ea2b2c02..900163fe06 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/UserService.java +++ b/webapp/src/main/java/org/apache/atlas/web/service/UserService.java @@ -17,8 +17,8 @@ package org.apache.atlas.web.service; -import org.apache.atlas.web.dao.UserDao; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.dao.UserDao; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; @@ -35,7 +35,7 @@ public UserService(UserDao userDao) { } @Override - public User loadUserByUsername(final String username) throws UsernameNotFoundException { + public UserDetails loadUserByUsername(final String username) throws UsernameNotFoundException { return userDao.loadUserByUsername(username); } } diff --git a/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java b/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java index 28d8e4b938..6e364e352b 100644 --- a/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java @@ -18,8 +18,9 @@ package org.apache.atlas.web.dao; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.dao.UserDao; import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -65,7 +66,7 @@ public void testLoadUserByUsername_Success() { userDao.setUserLogins(mockProps); - User user = userDao.loadUserByUsername("johndoe"); + UserDetails user = userDao.loadUserByUsername("johndoe"); assertEquals(user.getUsername(), "johndoe"); assertTrue(user.getAuthorities().stream() diff --git a/webapp/src/test/java/org/apache/atlas/web/security/UserDaoTest.java b/webapp/src/test/java/org/apache/atlas/web/security/UserDaoTest.java index 71b8606a42..bd4224e980 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/UserDaoTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/UserDaoTest.java @@ -16,9 +16,9 @@ */ package org.apache.atlas.web.security; -import org.apache.atlas.web.dao.UserDao; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.dao.UserDao; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.testng.annotations.Test; @@ -39,7 +39,7 @@ public void testUserDaowithValidUserLoginAndPassword() { user.setUserLogins(userLogins); - User userBean = user.loadUserByUsername("admin"); + UserDetails userBean = user.loadUserByUsername("admin"); assertEquals(userBean.getPassword(), "admin123"); @@ -66,7 +66,7 @@ public void testUserDaowithInValidLogin() { user.setUserLogins(userLogins); try { - User userBean = user.loadUserByUsername("xyz"); + UserDetails userBean = user.loadUserByUsername("xyz"); } catch (UsernameNotFoundException uex) { hadException = true; } diff --git a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java index 79e62148f9..607bee53f9 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java @@ -18,8 +18,8 @@ package org.apache.atlas.web.service; -import org.apache.atlas.web.dao.UserDao; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.dao.UserDao; +import org.springframework.security.core.userdetails.UserDetails; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -38,7 +38,7 @@ public class UserServiceTest { private UserDao userDao; @Mock - private User user; + private UserDetails user; private UserService userService; @@ -63,7 +63,7 @@ public void testLoadUserByUsername() { String username = "testuser"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -74,7 +74,7 @@ public void testLoadUserByUsernameWithDifferentUsername() { String username = "anotheruser"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -85,7 +85,7 @@ public void testLoadUserByUsernameWithEmptyUsername() { String username = ""; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -96,7 +96,7 @@ public void testLoadUserByUsernameWithNullUsername() { String username = null; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -115,7 +115,7 @@ public void testLoadUserByUsernameWithSpecialCharacters() { String username = "user@domain.com"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -126,7 +126,7 @@ public void testLoadUserByUsernameWithNumericUsername() { String username = "12345"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -137,7 +137,7 @@ public void testLoadUserByUsernameWithLongUsername() { String username = "verylongusernamethatexceedsnormallimits"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -148,7 +148,7 @@ public void testLoadUserByUsernameWithWhitespace() { String username = "user name"; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -159,7 +159,7 @@ public void testLoadUserByUsernameWithLeadingTrailingSpaces() { String username = " testuser "; when(userDao.loadUserByUsername(username)).thenReturn(user); - User result = userService.loadUserByUsername(username); + UserDetails result = userService.loadUserByUsername(username); assertEquals(result, user); verify(userDao).loadUserByUsername(username); @@ -170,14 +170,14 @@ public void testMultipleCallsToLoadUserByUsername() { String username1 = "user1"; String username2 = "user2"; - User user1 = user; - User user2 = user; + UserDetails user1 = user; + UserDetails user2 = user; when(userDao.loadUserByUsername(username1)).thenReturn(user1); when(userDao.loadUserByUsername(username2)).thenReturn(user2); - User result1 = userService.loadUserByUsername(username1); - User result2 = userService.loadUserByUsername(username2); + UserDetails result1 = userService.loadUserByUsername(username1); + UserDetails result2 = userService.loadUserByUsername(username2); assertEquals(result1, user1); assertEquals(result2, user2); @@ -207,7 +207,7 @@ public void testLoadUserByUsernameReturnType() { Object result = userService.loadUserByUsername(username); - assertTrue(result instanceof User); + assertTrue(result instanceof UserDetails); assertEquals(result, user); } diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index efb59e42d9..8eb7ac3898 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -52,7 +52,7 @@ - + From 405aed651c9948733112b866d74c926c3849d08e Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Tue, 21 Apr 2026 17:18:49 +0530 Subject: [PATCH 07/22] Updated Filter classes and module to use server-common module for both webapp and rest-notification webapp. --- .../rest/web/filters/ActiveServerFilter.java | 176 ---- .../filters/AtlasAuthenticationFilter.java | 822 ------------------ .../filters/AtlasCSRFPreventionFilter.java | 270 ------ .../AtlasKnoxSSOAuthenticationFilter.java | 597 ------------- .../rest/web/filters/AuditFilter.java | 206 ----- .../rest/web/filters/SSOAuthentication.java | 74 -- .../web/security/AtlasSecurityConfig.java | 84 +- .../src/main/webapp/WEB-INF/web.xml | 2 +- server-common/pom.xml | 25 + .../common}/filters/ActiveServerFilter.java | 20 +- .../filters/AtlasAuthenticationFilter.java | 19 +- .../filters/AtlasCSRFPreventionFilter.java | 4 +- .../AtlasKnoxSSOAuthenticationFilter.java | 14 +- .../server/common}/filters/AuditFilter.java | 30 +- .../common}/filters/SSOAuthentication.java | 2 +- .../spi/ActiveInstanceStateProvider.java | 22 + .../AtlasAuthenticationProviderBridge.java | 34 + .../filters/spi/ServiceStateProvider.java | 28 + .../NotificationHookConsumer.java | 4 +- .../atlas/web/resources/AdminResource.java | 4 +- .../web/security/AtlasSecurityConfig.java | 83 +- webapp/src/main/resources/spring-security.xml | 6 +- webapp/src/main/webapp/WEB-INF/web.xml | 2 +- .../web/filters/ActiveServerFilterTest.java | 82 +- .../AtlasAuthenticationFilterTest.java | 1 + .../AtlasCSRFPreventionFilterTest.java | 3 +- .../AtlasKnoxSSOAuthenticationFilterTest.java | 63 +- .../atlas/web/filters/AuditFilterTest.java | 1 + .../web/security/AtlasSecurityConfigTest.java | 8 +- .../web/security/FileAuthenticationTest.java | 4 +- .../TestSpringSecurityBridgeConfig.java | 57 ++ .../test/resources/test-spring-security.xml | 8 +- webapp/src/test/webapp/WEB-INF/web.xml | 2 +- 33 files changed, 496 insertions(+), 2261 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java delete mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/ActiveServerFilter.java (93%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasAuthenticationFilter.java (98%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasCSRFPreventionFilter.java (99%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AtlasKnoxSSOAuthenticationFilter.java (98%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/AuditFilter.java (90%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/filters/SSOAuthentication.java (97%) create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ActiveInstanceStateProvider.java create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/filters/spi/AtlasAuthenticationProviderBridge.java create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ServiceStateProvider.java create mode 100644 webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java deleted file mode 100644 index f3b3b46e37..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/ActiveServerFilter.java +++ /dev/null @@ -1,176 +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.atlas.notification.rest.web.filters; - -import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; -import org.apache.atlas.notification.rest.web.service.ServiceState; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.springframework.web.util.UriUtils; - -import javax.inject.Inject; -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.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.HttpMethod; -import javax.ws.rs.core.HttpHeaders; -import java.io.IOException; - -/** - * A servlet {@link Filter} that redirects web requests from a passive Atlas server instance to an active one. - * - * All requests to an active instance pass through. Requests received by a passive instance are redirected - * by identifying the currently active server. Requests to servers which are in transition are returned with - * an error SERVICE_UNAVAILABLE. Identification of this state is carried out using - * {@link ServiceState} and {@link ActiveInstanceState}. - */ -@Component -public class ActiveServerFilter implements Filter { - - private static final Logger LOG = LoggerFactory.getLogger(ActiveServerFilter.class); - - private final ActiveInstanceState activeInstanceState; - private ServiceState serviceState; - - @Inject - public ActiveServerFilter(ActiveInstanceState activeInstanceState, ServiceState serviceState) { - this.activeInstanceState = activeInstanceState; - this.serviceState = serviceState; - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - LOG.info("ActiveServerFilter initialized"); - } - - /** - * Determines if this Atlas server instance is passive and redirects to active if so. - * - * @param servletRequest Request object from which the URL and other parameters are determined. - * @param servletResponse Response object to handle the redirect. - * @param filterChain Chain to pass through requests if the instance is Active. - * @throws IOException - * @throws ServletException - */ - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, - FilterChain filterChain) throws IOException, ServletException { - if (isFilteredURI(servletRequest)) { - LOG.debug("Is a filtered URI: {}. Passing request downstream.", - ((HttpServletRequest)servletRequest).getRequestURI()); - filterChain.doFilter(servletRequest, servletResponse); - } else if (isInstanceActive()) { - LOG.debug("Active. Passing request downstream"); - filterChain.doFilter(servletRequest, servletResponse); - } else if (serviceState.isInstanceInTransition()) { - HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; - LOG.error("Instance in transition. Service may not be ready to return a result"); - httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); - } else { - HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; - String activeServerAddress = activeInstanceState.getActiveServerAddress(); - if (activeServerAddress == null) { - LOG.error("Could not retrieve active server address as it is null. Cannot redirect request {}", - ((HttpServletRequest)servletRequest).getRequestURI()); - httpServletResponse.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); - } else { - handleRedirect((HttpServletRequest) servletRequest, httpServletResponse, activeServerAddress); - } - } - } - - - private boolean isRootURI(ServletRequest servletRequest) { - HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; - String requestURI = httpServletRequest.getRequestURI(); - return requestURI.equals("/"); - } - - boolean isInstanceActive() { - return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; - } - - - private void handleRedirect(HttpServletRequest servletRequest, HttpServletResponse httpServletResponse, - String activeServerAddress) throws IOException { - String requestURI = servletRequest.getRequestURI(); - String queryString = servletRequest.getQueryString(); - - if (queryString != null && (!queryString.isEmpty())) { - queryString = UriUtils.encodeQuery(queryString, "UTF-8"); - } - - if ((queryString != null) && (!queryString.isEmpty())) { - requestURI += "?" + queryString; - } - - if (requestURI == null) { - requestURI = "/"; - } - String redirectLocation = activeServerAddress + requestURI; - LOG.info("Not active. Redirecting to {}", redirectLocation); - // A POST/PUT/DELETE require special handling by sending HTTP 307 instead of the regular 301/302. - // Reference: http://stackoverflow.com/questions/2068418/whats-the-difference-between-a-302-and-a-307-redirect - if (isUnsafeHttpMethod(servletRequest)) { - httpServletResponse.setHeader(HttpHeaders.LOCATION, redirectLocation); - httpServletResponse.setStatus(HttpServletResponse.SC_TEMPORARY_REDIRECT); - } else { - httpServletResponse.sendRedirect(redirectLocation); - } - } - - private boolean isUnsafeHttpMethod(HttpServletRequest httpServletRequest) { - String method = httpServletRequest.getMethod(); - return (method.equals(HttpMethod.POST)) || - (method.equals(HttpMethod.PUT)) || - (method.equals(HttpMethod.DELETE)); - } - - @Override - public void destroy() { - - } - - final String adminUriNotFiltered[] = { "/admin/export", "/admin/import", "/admin/importfile", "/admin/audits", - "/admin/purge", "/admin/expimp/audit", "/admin/metrics", "/admin/server", "/admin/audit/", "admin/tasks", "admin/async/import", "admin/async/import/status"}; - private boolean isFilteredURI(ServletRequest servletRequest) { - HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; - String requestURI = httpServletRequest.getRequestURI(); - - if(requestURI.contains("/admin/")) { - for (String s : adminUriNotFiltered) { - if (requestURI.contains(s)) { - LOG.error("URL not supported in HA mode: {}", requestURI); - return false; - } - } - - return true; - } else { - return false; - } - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java deleted file mode 100644 index a96ab8a536..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasAuthenticationFilter.java +++ /dev/null @@ -1,822 +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.atlas.notification.rest.web.filters; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasConfiguration; -import org.apache.atlas.server.common.util.Servlets; -import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationProvider; -import org.apache.atlas.security.SecurityProperties; -import org.apache.atlas.utils.AuthenticationUtil; -import org.apache.commons.collections.iterators.IteratorEnumeration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationConverter; -import org.apache.commons.lang.StringUtils; -import org.apache.hadoop.security.SecurityUtil; -import org.apache.hadoop.security.UserGroupInformation; -import org.apache.hadoop.security.authentication.client.AuthenticatedURL; -import org.apache.hadoop.security.authentication.client.AuthenticationException; -import org.apache.hadoop.security.authentication.client.KerberosAuthenticator; -import org.apache.hadoop.security.authentication.server.AuthenticationFilter; -import org.apache.hadoop.security.authentication.server.AuthenticationHandler; -import org.apache.hadoop.security.authentication.server.AuthenticationToken; -import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; -import org.apache.hadoop.security.authentication.util.Signer; -import org.apache.hadoop.security.authentication.util.SignerException; -import org.apache.hadoop.security.authentication.util.SignerSecretProvider; -import org.apache.hadoop.security.authorize.AuthorizationException; -import org.apache.hadoop.security.authorize.ProxyUsers; -import org.slf4j.MDC; -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 org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; -import org.springframework.stereotype.Component; - -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; -import javax.ws.rs.core.Response; -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.Principal; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.TimeZone; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.HeadersUtil; -import org.apache.atlas.server.common.filters.NullServletContext; -import org.apache.atlas.server.common.filters.RestUtil; - -/** - * This enforces authentication as part of the filter before processing the request. - * todo: Subclass of {@link AuthenticationFilter}. - */ - -@Component -public class AtlasAuthenticationFilter extends AuthenticationFilter { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class); - - private static final int SESSION_TIMEOUT_DISABLED_VALUE = -1; - private static final String CONFIG_KERBEROS_TOKEN_VALIDITY = "atlas.authentication.method.kerberos.token.validity"; - private static final String CONFIG_PROXY_USERS = "atlas.proxyusers"; - private static final String PREFIX = "atlas.authentication.method"; - private static final String[] DEFAULT_PROXY_USERS = new String[] { "knox" }; - private static final String CONF_PROXYUSER_PREFIX = "atlas.proxyuser"; - protected static final ServletContext nullContext = new NullServletContext(); - private static final String ORIGINAL_URL_QUERY_PARAM = "originalUrl"; - - private Signer signer; - private SignerSecretProvider secretProvider; - private final boolean isKerberos = AuthenticationUtil.isKerberosAuthenticationEnabled(); - private boolean isInitializedByTomcat; - private Set browserUserAgents; - private boolean supportKeyTabBrowserLogin = false; - private Configuration configuration; - private Properties headerProperties; - private Set atlasProxyUsers = new HashSet<>(); - private HttpServlet optionsServlet; - private boolean supportTrustedProxy = false; - private int sessionTimeout; - - private SecurityContextLogoutHandler logoutHandler; - - public AtlasAuthenticationFilter() { - LOG.info("==> AtlasAuthenticationFilter()"); - - try { - init(null); - } catch (ServletException e) { - LOG.error("Error while initializing AtlasAuthenticationFilter", e); - } - - LOG.info("<== AtlasAuthenticationFilter()"); - } - - /** - * Initialize the filter. - * - * @param filterConfig filter configuration. - * @throws ServletException thrown if the filter could not be initialized. - */ - @Override - public void init(FilterConfig filterConfig) throws ServletException { - LOG.info("==> AtlasAuthenticationFilter.init"); - - final FilterConfig globalConf = filterConfig; - final Map params = new HashMap<>(); - try { - configuration = ApplicationProperties.get(); - } catch (Exception e) { - throw new ServletException(e); - } - - if (configuration != null) { - headerProperties = ConfigurationConverter.getProperties(configuration.subset("atlas.headers")); - } - - String tokenValidityStr = null; - - if(configuration != null) { - tokenValidityStr = configuration.getString(CONFIG_KERBEROS_TOKEN_VALIDITY); - } - - if (StringUtils.isNotBlank(tokenValidityStr)) { - try { - Long tokenValidity = Long.parseLong(tokenValidityStr); - - if (tokenValidity > 0) { - params.put(AuthenticationFilter.AUTH_TOKEN_VALIDITY, tokenValidity.toString()); - } else { - throw new ServletException(tokenValidity + ": invalid value for property '" + CONFIG_KERBEROS_TOKEN_VALIDITY + "'. Must be a positive integer"); - } - } catch (NumberFormatException e) { - throw new ServletException(tokenValidityStr + ": invalid value for property '" + CONFIG_KERBEROS_TOKEN_VALIDITY + "'. Must be a positive integer", e); - } - } - - FilterConfig filterConfig1 = new FilterConfig() { - @Override - public ServletContext getServletContext() { - if (globalConf != null) { - return globalConf.getServletContext(); - } else { - return nullContext; - } - } - - @SuppressWarnings("unchecked") - @Override - public Enumeration getInitParameterNames() { - return new IteratorEnumeration(params.keySet().iterator()); - } - - @Override - public String getInitParameter(String param) { - return params.get(param); - } - - @Override - public String getFilterName() { - return "AtlasAuthenticationFilter"; - } - }; - - super.init(filterConfig1); - - ProxyUsers.refreshSuperUserGroupsConfiguration(getProxyuserConfiguration(), CONF_PROXYUSER_PREFIX); - - optionsServlet = new HttpServlet() { - }; - optionsServlet.init(); - - if (sessionTimeout != -1) { - logoutHandler = new SecurityContextLogoutHandler(); - } - - LOG.info("<== AtlasAuthenticationFilter.init(filterConfig={})", filterConfig); - - } - - - @Override - public void initializeSecretProvider(FilterConfig filterConfig) throws ServletException { - LOG.info("==> AtlasAuthenticationFilter.initializeSecretProvider"); - - secretProvider = (SignerSecretProvider) filterConfig.getServletContext().getAttribute(AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE); - - if (secretProvider == null) { - // As tomcat cannot specify the provider object in the configuration. - // It'll go into this path - String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX); - - configPrefix = (configPrefix != null) ? configPrefix + "." : ""; - - try { - secretProvider = AuthenticationFilter.constructSecretProvider(filterConfig.getServletContext(), super.getConfiguration(configPrefix, filterConfig), false); - - this.isInitializedByTomcat = true; - } catch (Exception ex) { - throw new ServletException(ex); - } - } - - signer = new Signer(secretProvider); - - LOG.info("<== AtlasAuthenticationFilter.initializeSecretProvider(filterConfig={})", filterConfig); - } - - @Override - protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { - LOG.info("==> AtlasAuthenticationFilter.getConfiguration()"); - - try { - configuration = ApplicationProperties.get(); - } catch (Exception e) { - throw new ServletException(e); - } - - Properties ret = new Properties(); - - String kerberosAuthEnabled = configuration != null ? configuration.getString("atlas.authentication.method.kerberos") : null; - - final String authMethod; - - if (kerberosAuthEnabled == null || kerberosAuthEnabled.equalsIgnoreCase("false")) { - LOG.info("No authentication method configured. Defaulting to simple authentication"); - - authMethod = "simple"; - } else if (kerberosAuthEnabled.equalsIgnoreCase("true")) { - authMethod = "kerberos"; - - if (configuration.getString("atlas.authentication.method.kerberos.name.rules") != null) { - ret.put("kerberos.name.rules", configuration.getString("atlas.authentication.method.kerberos.name.rules")); - } - - if (configuration.getString("atlas.authentication.method.kerberos.keytab") != null) { - ret.put("kerberos.keytab", configuration.getString("atlas.authentication.method.kerberos.keytab")); - } - - if (configuration.getString("atlas.authentication.method.kerberos.principal") != null) { - ret.put("kerberos.principal", configuration.getString("atlas.authentication.method.kerberos.principal")); - } - } else { - authMethod = ""; - } - - ret.put(AuthenticationFilter.AUTH_TYPE, authMethod); - ret.put(AuthenticationFilter.COOKIE_PATH, "/"); - - // add any config passed in as init parameters - Enumeration enumeration = filterConfig.getInitParameterNames(); - while (enumeration.hasMoreElements()) { - String name = enumeration.nextElement(); - - ret.put(name, filterConfig.getInitParameter(name)); - } - - //Resolve _HOST into bind address - String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS); - if (bindAddress == null) { - LOG.info("No host name configured. Defaulting to local host name."); - - try { - bindAddress = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - throw new ServletException("Unable to obtain host name", e); - } - } - - String principal = ret.getProperty(KerberosAuthenticationHandler.PRINCIPAL); - if (principal != null) { - try { - principal = SecurityUtil.getServerPrincipal(principal, bindAddress); - } catch (IOException ex) { - throw new RuntimeException("Could not resolve Kerberos principal name: " + ex.toString(), ex); - } - - ret.put(KerberosAuthenticationHandler.PRINCIPAL, principal); - } - - - LOG.debug(" AuthenticationFilterConfig: {}", ret); - sessionTimeout = AtlasConfiguration.SESSION_TIMEOUT_SECS.getInt(); - LOG.info("AtlasAuthenticationFilter: {} = {}: {}", - AtlasConfiguration.SESSION_TIMEOUT_SECS.getPropertyName(), sessionTimeout, - (sessionTimeout == SESSION_TIMEOUT_DISABLED_VALUE) ? "Disabled" : "Enabled"); - - supportKeyTabBrowserLogin = configuration.getBoolean("atlas.authentication.method.kerberos.support.keytab.browser.login", false); - supportTrustedProxy = configuration.getBoolean("atlas.authentication.method.trustedproxy", false); - String agents = configuration.getString(AtlasCSRFPreventionFilter.BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT); - - if (agents == null) { - agents = AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT; - } - - String[] proxyUsers = configuration.getStringArray(CONFIG_PROXY_USERS); - - if (proxyUsers == null || proxyUsers.length == 0) { - proxyUsers = DEFAULT_PROXY_USERS; - } - - atlasProxyUsers = new HashSet<>(Arrays.asList(proxyUsers)); - - parseBrowserUserAgents(agents); - - LOG.info("<== AtlasAuthenticationFilter.getConfiguration(configPrefix={}, filterConfig={}): {}", configPrefix, filterConfig, ret); - - return ret; - } - - @Override - public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException { - final HttpServletRequest httpRequest = (HttpServletRequest) request; - - try { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - HttpServletResponse httpResponse = (HttpServletResponse) response; - AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); - String action = httpRequest.getParameter("action"); - String doAsUser = request.getParameter("doAs"); - - HeadersUtil.setSecurityHeaders(responseWrapper); - - if (headerProperties != null) { - for (String headerKey : headerProperties.stringPropertyNames()) { - responseWrapper.setHeader(headerKey, headerProperties.getProperty(headerKey)); - } - } - - if (logoutHandler != null && supportTrustedProxy && StringUtils.isNotEmpty(doAsUser) && StringUtils.equals(action, RestUtil.TIMEOUT_ACTION)) { - if (existingAuth != null) { - logoutHandler.logout(httpRequest, httpResponse, existingAuth); - } - redirectTimeoutReqeust(httpRequest, httpResponse); - return; - } - - if (existingAuth == null) { - String authHeader = httpRequest.getHeader("Authorization"); - - if (authHeader != null && authHeader.startsWith("Basic")) { - filterChain.doFilter(request, response); - } else if (isKerberos) { - doKerberosAuth(request, response, filterChain); - } else { - filterChain.doFilter(request, response); - } - } else { - filterChain.doFilter(request, response); - } - } catch (NullPointerException e) { - LOG.error("Exception in AtlasAuthenticationFilter ", e); - //PseudoAuthenticationHandler.getUserName() from hadoop-auth throws NPE if user name is not specified - ((HttpServletResponse) response).sendError(Response.Status.BAD_REQUEST.getStatusCode(), - "Authentication is enabled and user is not specified. Specify user.name parameter"); - } - } - - private void redirectTimeoutReqeust(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException{ - String logoutUrl = httpRequest.getRequestURL().toString(); - - logoutUrl = StringUtils.replace(logoutUrl, httpRequest.getRequestURI(), RestUtil.LOGOUT_URL); - if (LOG.isDebugEnabled()) { - LOG.debug("logoutUrl value is " + logoutUrl); - } - String xForwardedURL = RestUtil.constructForwardableURL(httpRequest); - - - if (LOG.isDebugEnabled()) { - LOG.debug("xForwardedURL = " + xForwardedURL); - } - String redirectUrl = RestUtil.constructRedirectURL(httpRequest, logoutUrl, xForwardedURL, ORIGINAL_URL_QUERY_PARAM); - - if (LOG.isDebugEnabled()) { - LOG.debug("Redirect URL = " + redirectUrl); - LOG.debug("session id = " + httpRequest.getRequestedSessionId()); - } - - httpResponse.sendRedirect(redirectUrl); - } - - - /** - * This method is copied from hadoop auth lib, code added for error handling and fallback to other auth methods - * - * If the request has a valid authentication token it allows the request to continue to the target resource, - * otherwise it triggers an authentication sequence using the configured {@link AuthenticationHandler}. - * - * @param request the request object. - * @param response the response object. - * @param filterChain the filter chain object. - * - * @throws IOException thrown if an IO error occurred. - * @throws ServletException thrown if a processing error occurred. - */ - private void doKerberosAuth(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - KerberosFilterChainWrapper filterChainWrapper = new KerberosFilterChainWrapper(request, response, filterChain); - boolean unauthorizedResponse = true; - int errCode = HttpServletResponse.SC_UNAUTHORIZED; - AuthenticationException authenticationEx = null; - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - boolean isHttps = "https".equals(httpRequest.getScheme()); - AuthenticationHandler authHandler = getAuthenticationHandler(); - String doAsUser = supportTrustedProxy ? Servlets.getDoAsUser(httpRequest) : null; - - try { - boolean newToken = false; - AuthenticationToken token; - - try { - token = getToken(httpRequest); - } catch (AuthenticationException ex) { - LOG.warn("AuthenticationToken ignored: {}", ex); - // will be sent back in a 401 unless filter authenticates - authenticationEx = ex; - token = null; - } - - if (authHandler.managementOperation(token, httpRequest, httpResponse)) { - if (token == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest)); - } - - token = authHandler.authenticate(httpRequest, httpResponse); - - if (token != null && token.getExpires() != 0 && token != AuthenticationToken.ANONYMOUS) { - token.setExpires(System.currentTimeMillis() + getValidity() * 1000); - } - - newToken = true; - } - - if (token != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName()); - } - - unauthorizedResponse = false; - - final AuthenticationToken authToken = token; - - httpRequest = new HttpServletRequestWrapper(httpRequest) { - @Override - public String getAuthType() { - return authToken.getType(); - } - - @Override - public String getRemoteUser() { - return authToken.getUserName(); - } - - @Override - public Principal getUserPrincipal() { - return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null; - } - }; - - // Create the proxy user if doAsUser exists - - if (supportTrustedProxy && doAsUser != null && !doAsUser.equals(httpRequest.getRemoteUser())) { - LOG.debug("doAsUser is {}", doAsUser); - - UserGroupInformation requestUgi = (token != null) ? UserGroupInformation.createRemoteUser(token.getUserName()) : null; - - if (requestUgi != null) { - requestUgi = UserGroupInformation.createProxyUser(doAsUser, requestUgi); - - try { - ProxyUsers.authorize(requestUgi, request.getRemoteAddr()); - request.setAttribute("proxyUser", doAsUser); - } catch (AuthorizationException ex) { - LOG.warn("Proxy user AuthorizationException", ex); - - httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); - filterChain.doFilter(request, response); - - return; - - } - } - } else if(StringUtils.isNotBlank(httpRequest.getRemoteUser()) && atlasProxyUsers.contains(httpRequest.getRemoteUser())){ - LOG.info("Ignoring kerberos login from proxy user "+ httpRequest.getRemoteUser()); - - httpResponse.setHeader(KerberosAuthenticator.WWW_AUTHENTICATE, ""); - httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - filterChain.doFilter(request, response); - - return; - } - - if (newToken && !token.isExpired() && token != AuthenticationToken.ANONYMOUS) { - String signedToken = signer.sign(token.toString()); - - createAtlasAuthCookie(httpResponse, signedToken, getCookieDomain(), getCookiePath(), token.getExpires(), isHttps); - } - - filterChainWrapper.doFilter(httpRequest, httpResponse); - } - } else { - unauthorizedResponse = false; - } - } catch (AuthenticationException ex) { - LOG.warn("Authentication exception: {}", ex.getMessage(), ex); - - // exception from the filter itself is fatal - errCode = HttpServletResponse.SC_FORBIDDEN; - authenticationEx = ex; - } - - if (unauthorizedResponse) { - if (!httpResponse.isCommitted()) { - createAtlasAuthCookie(httpResponse, "", getCookieDomain(), getCookiePath(), 0, isHttps); - - // If response code is 401. Then WWW-Authenticate Header should be - // present.. reset to 403 if not found.. - if (errCode == HttpServletResponse.SC_UNAUTHORIZED && !httpResponse.containsHeader(KerberosAuthenticator.WWW_AUTHENTICATE)) { - errCode = HttpServletResponse.SC_FORBIDDEN; - } - - boolean isKerberosOnBrowser = supportKeyTabBrowserLogin || doAsUser != null; - - if (authenticationEx == null) { // added this code for atlas error handling and fallback - if (!isKerberosOnBrowser && isBrowser(httpRequest.getHeader("User-Agent"))) { - filterChain.doFilter(request, response); - } else { - boolean chk = true; - Collection headerNames = httpResponse.getHeaderNames(); - - for (String headerName : headerNames) { - String value = httpResponse.getHeader(headerName); - - if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("ATLASSESSIONID")) { - chk = false; - break; - } - } - - String authHeader = httpRequest.getHeader("Authorization"); - - if (authHeader == null && chk) { - filterChain.doFilter(request, response); - } else if (authHeader != null && authHeader.startsWith("Basic")) { - filterChain.doFilter(request, response); - } - } - } else { - httpResponse.sendError(errCode, authenticationEx.getMessage()); - } - } - } - } - - - @Override - public void destroy() { - - if ((this.secretProvider != null) && (this.isInitializedByTomcat)) { - this.secretProvider.destroy(); - this.secretProvider = null; - } - optionsServlet.destroy(); - super.destroy(); - } - - - private static String readUserFromCookie(HttpServletResponse response1) { - String userName = null; - boolean isCookieSet = response1.containsHeader("Set-Cookie"); - - if (isCookieSet) { - Collection authUserName = response1.getHeaders("Set-Cookie"); - - if (authUserName != null) { - for (String cookie : authUserName) { - if (!StringUtils.isEmpty(cookie)) { - if (cookie.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase()) && cookie.contains("u=")) { - String[] split = cookie.split(";"); - - if (split != null) { - for (String s : split) { - if (!StringUtils.isEmpty(s) && s.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase())) { - int ustr = s.indexOf("u="); - - if (ustr != -1) { - int andStr = s.indexOf("&", ustr); - - if (andStr != -1) { - try { - userName = s.substring(ustr + 2, andStr); - break; - } catch (Exception e) { - userName = null; - } - } - } - } - } - } - } - } - } - } - } - - return userName; - } - - private void createAtlasAuthCookie(HttpServletResponse resp, String token, String domain, String path, long expires, boolean isSecure) { - StringBuilder sb = (new StringBuilder(AuthenticatedURL.AUTH_COOKIE)).append("="); - - if (token != null && token.length() > 0) { - sb.append("\"").append(token).append("\""); - } - - sb.append("; Version=1"); - - if (path != null) { - sb.append("; Path=").append(path); - } - - if (domain != null) { - sb.append("; Domain=").append(domain); - } - - if (expires >= 0L) { - SimpleDateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz"); - df.setTimeZone(TimeZone.getTimeZone("GMT")); - sb.append("; Expires=").append(df.format(new Date(expires))); - } - - if (isSecure) { - sb.append("; Secure"); - } - - sb.append("; HttpOnly"); - resp.addHeader("Set-Cookie", sb.toString()); - } - - @Override - protected AuthenticationToken getToken(HttpServletRequest request) - throws IOException, AuthenticationException { - AuthenticationToken token = null; - String tokenStr = null; - Cookie[] cookies = request.getCookies(); - if (cookies != null) { - for (Cookie cookie : cookies) { - if (cookie.getName().equals(AuthenticatedURL.AUTH_COOKIE)) { - tokenStr = cookie.getValue(); - try { - tokenStr = this.signer.verifyAndExtract(tokenStr); - } catch (SignerException ex) { - throw new AuthenticationException(ex); - } - } - } - } - - if (tokenStr != null) { - token = AuthenticationToken.parse(tokenStr); - if (token != null) { - AuthenticationHandler authHandler = getAuthenticationHandler(); - if (!token.getType().equals(authHandler.getType())) { - throw new AuthenticationException("Invalid AuthenticationToken type"); - } - if (token.isExpired()) { - throw new AuthenticationException("AuthenticationToken expired"); - } - } - } - return token; - } - - void parseBrowserUserAgents(String userAgents) { - String[] agentsArray = userAgents.split(","); - browserUserAgents = new HashSet<>(); - for (String patternString : agentsArray) { - browserUserAgents.add(Pattern.compile(patternString)); - } - } - - boolean isBrowser(String userAgent) { - if (userAgent != null) { - for (Pattern pattern : browserUserAgents) { - Matcher matcher = pattern.matcher(userAgent); - - if (matcher.matches()) { - return true; - } - } - } - - return false; - } - - - private class KerberosFilterChainWrapper implements FilterChain { - private final ServletRequest request; - private final ServletResponse response; - private final FilterChain filterChain; - - KerberosFilterChainWrapper(ServletRequest request, ServletResponse response, FilterChain filterChain) { - this.request = request; - this.response = response; - this.filterChain = filterChain; - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException { - final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; - final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; - final Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - String loggedInUser = readUserFromCookie(httpResponse); - String userName = loggedInUser; - - if (!StringUtils.isEmpty((String) httpRequest.getAttribute("proxyUser"))) { - userName = (String) httpRequest.getAttribute("proxyUser"); - } else if (StringUtils.isEmpty(userName) && !StringUtils.isEmpty(httpRequest.getRemoteUser())) { - userName = httpRequest.getRemoteUser(); - } - - if ((existingAuth == null || !existingAuth.isAuthenticated()) && !StringUtils.isEmpty(userName)) { - final List grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); - final UserDetails principal = new User(userName, "", grantedAuths); - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); - final WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); - - ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); - - SecurityContextHolder.getContext().setAuthentication(finalAuthentication); - if (sessionTimeout != SESSION_TIMEOUT_DISABLED_VALUE) { - httpRequest.getSession().setMaxInactiveInterval(sessionTimeout); - } - - request.setAttribute("atlas.http.authentication.type", true); - - if (!StringUtils.equals(loggedInUser, userName)) { - LOG.info("Logged into Atlas as = {}, by proxyUser = {}", userName, loggedInUser); - } else { - LOG.info("Logged into Atlas as = {}", userName); - } - } - - // OPTIONS method is sent from quick start jersey atlas client - if (httpRequest.getMethod().equals("OPTIONS")) { - optionsServlet.service(request, response); - } else { - try { - String requestUser = httpRequest.getRemoteUser(); - String requestTrace = requestUser + ":" + httpRequest.getMethod() + httpRequest.getRequestURI(); - MDC.put("request", requestTrace); - - LOG.info("Request from authenticated user: {}, URL={}", requestUser, Servlets.getRequestURI(httpRequest)); - - filterChain.doFilter(servletRequest, servletResponse); - } finally { - MDC.remove("request"); - } - } - } - } - - private org.apache.hadoop.conf.Configuration getProxyuserConfiguration() { - org.apache.hadoop.conf.Configuration ret = new org.apache.hadoop.conf.Configuration(false); - - if(configuration!=null) { - Properties props = ConfigurationConverter.getProperties(configuration.subset(CONF_PROXYUSER_PREFIX)); - - for (String key : props.stringPropertyNames()) { - ret.set(CONF_PROXYUSER_PREFIX + "." + key, props.getProperty(key)); - } - } - - return ret; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java deleted file mode 100644 index 48ad2aa7b5..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasCSRFPreventionFilter.java +++ /dev/null @@ -1,270 +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.atlas.notification.rest.web.filters; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; -import org.json.simple.JSONObject; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -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.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.HeadersUtil; - -@Component -public class AtlasCSRFPreventionFilter implements Filter { - private static final Logger LOG = LoggerFactory.getLogger(AtlasCSRFPreventionFilter.class); - private static Configuration configuration; - - static { - try { - configuration = ApplicationProperties.get(); - LOG.info("Configuration obtained :: "+configuration); - } catch (AtlasException e) { - LOG.error(e.getMessage(), e); - } - } - - public static final boolean isCSRF_ENABLED = configuration.getBoolean("atlas.rest-csrf.enabled", true); - public static final String BROWSER_USER_AGENT_PARAM = "atlas.rest-csrf.browser-useragents-regex"; - public static final String BROWSER_USER_AGENTS_DEFAULT = "^Mozilla.*,^Opera.*,^Chrome"; - public static final String CUSTOM_METHODS_TO_IGNORE_PARAM = "atlas.rest-csrf.methods-to-ignore"; - public static final String METHODS_TO_IGNORE_DEFAULT = "GET,OPTIONS,HEAD,TRACE"; - public static final String CUSTOM_HEADER_PARAM = "atlas.rest-csrf.custom-header"; - public static final String HEADER_DEFAULT = "X-XSRF-HEADER"; - public static final String HEADER_USER_AGENT = "User-Agent"; - public static final String CSRF_TOKEN = "_csrfToken"; - - - private String headerName = HEADER_DEFAULT; - private Set methodsToIgnore = null; - private Set browserUserAgents; - - public AtlasCSRFPreventionFilter() { - try { - if (isCSRF_ENABLED){ - init(null); - } - } catch (Exception e) { - LOG.error("Error while initializing Filter ", e); - } - } - - public void init(FilterConfig filterConfig) throws ServletException { - String customHeader = configuration.getString(CUSTOM_HEADER_PARAM, HEADER_DEFAULT); - if (customHeader != null) { - headerName = customHeader; - } - - String customMethodsToIgnore = configuration.getString(CUSTOM_METHODS_TO_IGNORE_PARAM, METHODS_TO_IGNORE_DEFAULT); - if (customMethodsToIgnore != null) { - parseMethodsToIgnore(customMethodsToIgnore); - } else { - parseMethodsToIgnore(METHODS_TO_IGNORE_DEFAULT); - } - String agents = configuration.getString(BROWSER_USER_AGENT_PARAM, BROWSER_USER_AGENTS_DEFAULT); - if (agents == null) { - agents = BROWSER_USER_AGENTS_DEFAULT; - } - parseBrowserUserAgents(agents); - LOG.info("Adding cross-site request forgery (CSRF) protection"); - } - - void parseMethodsToIgnore(String mti) { - String[] methods = mti.split(","); - methodsToIgnore = new HashSet<>(); - Collections.addAll(methodsToIgnore, methods); - } - - void parseBrowserUserAgents(String userAgents) { - String[] agentsArray = userAgents.split(","); - browserUserAgents = new HashSet<>(); - for (String patternString : agentsArray) { - browserUserAgents.add(Pattern.compile(patternString)); - } - } - - protected boolean isBrowser(String userAgent) { - if (userAgent == null) { - return false; - } - if (browserUserAgents != null){ - for (Pattern pattern : browserUserAgents) { - Matcher matcher = pattern.matcher(userAgent); - if (matcher.matches()) { - return true; - } - } - } - return false; - } - - public interface HttpInteraction { - /** - * Returns the value of a header. - * - * @param header - * name of header - * @return value of header - */ - String getHeader(String header); - - /** - * Returns the method. - * - * @return method - */ - String getMethod(); - - /** - * Called by the filter after it decides that the request may proceed. - * - * @throws IOException - * if there is an I/O error - * @throws ServletException - * if the implementation relies on the servlet API and a - * servlet API call has failed - */ - void proceed() throws IOException, ServletException; - - /** - * Called by the filter after it decides that the request is a potential - * CSRF attack and therefore must be rejected. - * - * @param code - * status code to send - * @param message - * response message - * @throws IOException - * if there is an I/O error - */ - void sendError(int code, String message) throws IOException; - } - - public void handleHttpInteraction(HttpInteraction httpInteraction) throws IOException, ServletException { - HttpSession session = ((ServletFilterHttpInteraction) httpInteraction).getSession(); - String csrfToken = StringUtils.EMPTY; - - if (session != null) { - csrfToken = (String) session.getAttribute(CSRF_TOKEN); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Session is null"); - } - } - - String clientCsrfToken = httpInteraction.getHeader(headerName); - - if (!isBrowser(httpInteraction.getHeader(HEADER_USER_AGENT)) || methodsToIgnore.contains(httpInteraction.getMethod()) - || (clientCsrfToken != null && clientCsrfToken.equals(csrfToken))) { - httpInteraction.proceed(); - } else { - httpInteraction.sendError(HttpServletResponse.SC_BAD_REQUEST,"Missing header or invalid Header value for CSRF Vulnerability Protection"); - } - } - - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { - final HttpServletRequest httpRequest = (HttpServletRequest) request; - final HttpServletResponse httpResponse = (HttpServletResponse) response; - AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); - HeadersUtil.setSecurityHeaders(responseWrapper); - - if (isCSRF_ENABLED){ - handleHttpInteraction(new ServletFilterHttpInteraction(httpRequest, httpResponse, chain)); - }else{ - chain.doFilter(request, response); - } - } - - public void destroy() { - } - - private static final class ServletFilterHttpInteraction implements - HttpInteraction { - - private final FilterChain chain; - private final HttpServletRequest httpRequest; - private final HttpServletResponse httpResponse; - - /** - * Creates a new ServletFilterHttpInteraction. - * - * @param httpRequest - * request to process - * @param httpResponse - * response to process - * @param chain - * filter chain to forward to if HTTP interaction is allowed - */ - public ServletFilterHttpInteraction(HttpServletRequest httpRequest, - HttpServletResponse httpResponse, FilterChain chain) { - this.httpRequest = httpRequest; - this.httpResponse = httpResponse; - this.chain = chain; - } - - @Override - public String getHeader(String header) { - return httpRequest.getHeader(header); - } - - @Override - public String getMethod() { - return httpRequest.getMethod(); - } - - @Override - public void proceed() throws IOException, ServletException { - chain.doFilter(httpRequest, httpResponse); - } - - public HttpSession getSession() { - return httpRequest.getSession(); - } - - @Override - public void sendError(int code, String message) throws IOException { - JSONObject json = new JSONObject(); - json.put("msgDesc", message); - httpResponse.setContentType("application/json"); - httpResponse.setStatus(code); - httpResponse.setCharacterEncoding("UTF-8"); - httpResponse.getWriter().write(json.toJSONString()); - } - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java deleted file mode 100644 index d1d531f275..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AtlasKnoxSSOAuthenticationFilter.java +++ /dev/null @@ -1,597 +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.atlas.notification.rest.web.filters; - -import com.google.common.annotations.VisibleForTesting; -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; -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.notification.rest.web.security.AtlasAuthenticationProvider; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; -import org.apache.http.client.utils.URIBuilder; -import org.json.simple.JSONObject; -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 org.springframework.stereotype.Component; - -import javax.inject.Inject; -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 java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URISyntaxException; -import java.net.URLEncoder; -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.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.HeadersUtil; -import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; - -@Component("ssoAuthenticationFilter") -public class AtlasKnoxSSOAuthenticationFilter implements Filter { - private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); - - public static final String BROWSER_USERAGENT = "atlas.sso.knox.browser.useragent"; - public static final String JWT_AUTH_PROVIDER_URL = "atlas.sso.knox.providerurl"; - public static final String JWT_PUBLIC_KEY = "atlas.sso.knox.publicKey"; - public static final String JWT_COOKIE_NAME = "atlas.sso.knox.cookiename"; - public static final String JWT_ORIGINAL_URL_QUERY_PARAM = "atlas.sso.knox.query.param.originalurl"; - public static final String JWT_COOKIE_NAME_DEFAULT = "hadoop-jwt"; - public static final String JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT = "originalUrl"; - public static final String DEFAULT_BROWSER_USERAGENT = "Mozilla,Opera,Chrome"; - public static final String PROXY_ATLAS_URL_PATH = "/atlas"; - - private final AtlasAuthenticationProvider authenticationProvider; - - private SSOAuthenticationProperties jwtProperties; - - private String originalUrlQueryParam = "originalUrl"; - private String authenticationProviderUrl = null; - private RSAPublicKey publicKey = null; - private String cookieName = "hadoop-jwt"; - private Configuration configuration = null; - private boolean ssoEnabled = false; - private JWSVerifier verifier = null; - @VisibleForTesting - private final int MAX_LOGIN_URL_LENGTH = 2043; - - @Inject - public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider) { - this.authenticationProvider = authenticationProvider; - try { - configuration = ApplicationProperties.get(); - } catch (Exception e) { - LOG.error("Error while getting application properties", e); - } - if (configuration != null) { - ssoEnabled = configuration.getBoolean("atlas.sso.knox.enabled", false); - jwtProperties = loadJwtProperties(); - } - setJwtProperties(); - } - - public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider, - SSOAuthenticationProperties jwtProperties) { - this.authenticationProvider = authenticationProvider; - this.jwtProperties = jwtProperties; - setJwtProperties(); - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - } - - /* - * doFilter of AtlasKnoxSSOAuthenticationFilter is the first in the filter list so in this it check for the request - * if the request is from browser and sso is enabled then it process the request against knox sso - * else if it's ssoenable and the request is with local login string then it show's the appropriate msg - * else if ssoenable is false then it contiunes with further filters as it was before sso - */ - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - - HttpServletResponse httpResponse = (HttpServletResponse) servletResponse; - - AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); - HeadersUtil.setSecurityHeaders(responseWrapper); - - if (!ssoEnabled) { - filterChain.doFilter(servletRequest, servletResponse); - return; - } - - HttpServletRequest httpRequest = (HttpServletRequest) servletRequest; - if (LOG.isDebugEnabled()) { - LOG.debug("Knox doFilter {}", httpRequest.getRequestURI()); - } - - if (httpRequest.getSession() != null && httpRequest.getSession().getAttribute("locallogin") != null) { - servletRequest.setAttribute("ssoEnabled", false); - filterChain.doFilter(servletRequest, servletResponse); - return; - } - - if (jwtProperties == null || isAuthenticated()) { - filterChain.doFilter(servletRequest, servletResponse); - return; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Knox ssoEnabled {} {}", ssoEnabled, httpRequest.getRequestURI()); - } - //if jwt properties are loaded and is current not authenticated then it will go for sso authentication - //Note : Need to remove !isAuthenticated() after knoxsso solve the bug from cross-origin script - HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; - String serializedJWT = getJWTFromCookie(httpRequest); - // if we get the hadoop-jwt token from the cookies then will process it further - 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 = AtlasAuthenticationProvider.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); - authenticationProvider.setSsoEnabled(ssoEnabled); - Authentication authentication = authenticationProvider.authenticate(finalAuthentication); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - - filterChain.doFilter(servletRequest, httpServletResponse); - } else { // if the token is not valid then redirect to knox sso - redirectToKnox(httpRequest, httpServletResponse, filterChain); - } - } catch (ParseException e) { - LOG.warn("Unable to parse the JWT token", e); - redirectToKnox(httpRequest, httpServletResponse, filterChain); - } - } else { - redirectToKnox(httpRequest, httpServletResponse, filterChain); - } - - } - - private void redirectToKnox(HttpServletRequest httpRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws IOException, ServletException { - - if (!isWebUserAgent(httpRequest.getHeader("User-Agent"))) { - filterChain.doFilter(httpRequest, httpServletResponse); - return; - } - - String ajaxRequestHeader = httpRequest.getHeader("X-Requested-With"); - - if ("XMLHttpRequest".equals(ajaxRequestHeader)) { - String ssourl = constructLoginURL(httpRequest, true); - JSONObject json = new JSONObject(); - json.put("knoxssoredirectURL", URLEncoder.encode(ssourl, "UTF-8")); - httpServletResponse.setContentType("application/json"); - httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED); - httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, json.toString()); - - } else { - String ssourl = constructLoginURL(httpRequest, false); - httpServletResponse.sendRedirect(ssourl); - } - - } - - private boolean isWebUserAgent(String userAgent) { - boolean isWeb = false; - if (jwtProperties != null) { - String userAgentList[] = jwtProperties.getUserAgentList(); - if (userAgentList != null && userAgentList.length > 0) { - for (String ua : userAgentList) { - if (StringUtils.startsWithIgnoreCase(userAgent, ua)) { - isWeb = true; - break; - } - } - } - } - return isWeb; - } - - - private void setJwtProperties() { - if (jwtProperties != null) { - authenticationProviderUrl = jwtProperties.getAuthenticationProviderUrl(); - publicKey = jwtProperties.getPublicKey(); - cookieName = jwtProperties.getCookieName(); - originalUrlQueryParam = jwtProperties.getOriginalUrlQueryParam(); - if (publicKey != null) { - verifier = new RSASSAVerifier(publicKey); - } - } - } - - /** - * Do not try to validate JWT if user already authenticated via other - * provider - * - * @return true, if JWT validation required - */ - private boolean isAuthenticated() { - Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); - return !(!(existingAuth != null && existingAuth.isAuthenticated()) || existingAuth instanceof SSOAuthentication); - } - - /** - * Encapsulate the acquisition of the JWT token from HTTP cookies within the - * request. - * - * @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 (cookieName != null && cookies != null) { - for (Cookie cookie : cookies) { - if (cookieName.equals(cookie.getName())) { - if (LOG.isDebugEnabled()) { - LOG.debug("{} cookie has been found and is being processed", cookieName); - } - serializedJWT = cookie.getValue(); - break; - } - } - } - return serializedJWT; - } - - /** - * 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, boolean isXMLRequest) { - String delimiter = "?"; - if (authenticationProviderUrl.contains("?")) { - delimiter = "&"; - } - - String xForwardedURL = constructForwardableURL(parseXForwardHeader(request), request.getRequestURI()); - - StringBuilder knoxLoginURL = new StringBuilder(); - knoxLoginURL.append(authenticationProviderUrl) - .append(delimiter) - .append(originalUrlQueryParam).append("="); - - if (isXMLRequest) { - String atlasApplicationURL = ""; - String referalURL = request.getHeader("referer"); - - if (referalURL == null) { - atlasApplicationURL = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath(); - } else { - atlasApplicationURL = referalURL; - } - - if (StringUtils.trimToNull(xForwardedURL) != null) { - safeAppend(knoxLoginURL, xForwardedURL, atlasApplicationURL); - } else { - safeAppend(knoxLoginURL, atlasApplicationURL); - } - } else { - if (StringUtils.trimToNull(xForwardedURL) != null) { - safeAppend(knoxLoginURL, xForwardedURL, getOriginalQueryString(request)); - } else { - safeAppend(knoxLoginURL, request.getRequestURL().toString(), getOriginalQueryString(request)); - } - } - return knoxLoginURL.toString(); - } - - private String getOriginalQueryString(HttpServletRequest request) { - String originalQueryString = request.getQueryString(); - return (originalQueryString == null) ? "" : "?" + originalQueryString; - } - - - private Map parseXForwardHeader(HttpServletRequest httpRequest) { - String xForwardedProto = ""; - String xForwardedHost = ""; - String xForwardedContext = ""; - Map xFwdHeaderMap = null; - Enumeration names = httpRequest.getHeaderNames(); - while (names.hasMoreElements()) { - String name = (String) names.nextElement(); - Enumeration values = httpRequest.getHeaders(name); - String value = ""; - if (values != null) { - while (values.hasMoreElements()) { - value = (String) values.nextElement(); - } - } - if (StringUtils.trimToNull(name) != null - && StringUtils.trimToNull(value) != null) { - if (name.equalsIgnoreCase("x-forwarded-proto")) { - xForwardedProto = value; - } else if (name.equalsIgnoreCase("x-forwarded-host")) { - xForwardedHost = value; - } else if (name.equalsIgnoreCase("x-forwarded-context")) { - xForwardedContext = value; - } - } - } - - if (StringUtils.isNotEmpty(xForwardedProto) && StringUtils.isNotEmpty(xForwardedHost) - && StringUtils.isNotEmpty(xForwardedContext)) { - xFwdHeaderMap = new HashMap(); - xFwdHeaderMap.put("x-forwarded-proto", xForwardedProto); - xFwdHeaderMap.put("x-forwarded-host", xForwardedHost); - xFwdHeaderMap.put("x-forwarded-context", xForwardedContext); - } - - return xFwdHeaderMap; - } - - - private String constructForwardableURL(Map xFwdHeaderMap, String requestURI) { - - if (LOG.isDebugEnabled()) { - LOG.debug(" constructForwardableURL ==>>" + xFwdHeaderMap + " requestURI " + requestURI); - } - - String xForwardedURL = null; - - if (xFwdHeaderMap != null) { - String xForwardedProto = xFwdHeaderMap.get("x-forwarded-proto"); - String xForwardedHost = xFwdHeaderMap.get("x-forwarded-host"); - String xForwardedContext = xFwdHeaderMap.get("x-forwarded-context"); - - if (StringUtils.isNotBlank(xForwardedProto) - && StringUtils.isNotBlank(xForwardedHost) - && StringUtils.isNotBlank(xForwardedContext)) { - try { - if (LOG.isDebugEnabled()) { - LOG.debug(" Atlas url with proxy path ==>" + xForwardedProto + "://" - + xForwardedHost + xForwardedContext + PROXY_ATLAS_URL_PATH + requestURI); - } - - URIBuilder builder = new URIBuilder(); - builder.setScheme(xForwardedProto) - .setHost(xForwardedHost) - .setPath(xForwardedContext + PROXY_ATLAS_URL_PATH + requestURI); - - xForwardedURL = builder.build().toString(); - } catch (URISyntaxException ue) { - LOG.error(" URISyntaxException while build xforward url ", ue); - } - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug(" xForwardedURL ==>> " + xForwardedURL); - } - - return xForwardedURL; - } - - - @VisibleForTesting - void safeAppend(StringBuilder sb, String... strings) { - for (String s : strings) { - if ((sb.length() + s.length()) < MAX_LOGIN_URL_LENGTH) { - sb.append(s); - } - } - } - - /** - * This method provides a single method for validating the JWT for use in - * request processing. It provides for the override of specific aspects of - * this implementation through submethods used within but also allows for - * the override of the entire token validation algorithm. - * - * @param jwtToken the token to validate - * @return true if valid - */ - protected boolean validateToken(SignedJWT jwtToken) { - boolean isValid = validateSignature(jwtToken); - - if (isValid) { - isValid = validateExpiration(jwtToken); - if (!isValid) { - LOG.warn("Expiration time validation of JWT token failed."); - } - } else { - LOG.warn("Signature of JWT token could not be verified. Please check the public key"); - } - return isValid; - } - - /** - * 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 validateSignature(SignedJWT jwtToken) { - boolean valid = false; - if (JWSObject.State.SIGNED == jwtToken.getState()) { - if (LOG.isDebugEnabled()) { - LOG.debug("SSO token is in a SIGNED state"); - } - if (jwtToken.getSignature() != null) { - if (LOG.isDebugEnabled()) { - LOG.debug("SSO token signature is not null"); - } - try { - if (verifier != null && jwtToken.verify(verifier)) { - valid = true; - if (LOG.isDebugEnabled()) { - 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); - } - } - } - return valid; - } - - /** - * Validate that the expiration time of the JWT token has not been violated. - * If it has then throw an AuthenticationException. Override this method in - * subclasses in order to customize the expiration validation behavior. - * - * @param jwtToken the token that contains the expiration date to validate - * @return valid true if the token has not expired; false otherwise - */ - protected boolean validateExpiration(SignedJWT jwtToken) { - boolean valid = false; - try { - Date expires = jwtToken.getJWTClaimsSet().getExpirationTime(); - if (expires == null || new Date().before(expires)) { - if (LOG.isDebugEnabled()) { - LOG.debug("SSO token expiration date has been successfully validated"); - } - valid = true; - } else { - LOG.warn("SSO expiration date validation failed."); - } - } catch (ParseException pe) { - LOG.warn("SSO expiration date validation failed.", pe); - } - return valid; - } - - @Override - public void destroy() { - } - - public SSOAuthenticationProperties loadJwtProperties() { - String providerUrl = configuration.getString(JWT_AUTH_PROVIDER_URL); - if (providerUrl != null && configuration.getBoolean("atlas.sso.knox.enabled", false)) { - SSOAuthenticationProperties jwtProperties = new SSOAuthenticationProperties(); - String publicKeyPathStr = configuration.getString(JWT_PUBLIC_KEY); - if (publicKeyPathStr == null) { - LOG.error("Public key pem not specified for SSO auth provider {}. SSO auth will be disabled"); - return null; - } - jwtProperties.setAuthenticationProviderUrl(providerUrl); - jwtProperties.setCookieName(configuration.getString(JWT_COOKIE_NAME, JWT_COOKIE_NAME_DEFAULT)); - jwtProperties.setOriginalUrlQueryParam(configuration.getString(JWT_ORIGINAL_URL_QUERY_PARAM, JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT)); - String[] userAgent = configuration.getStringArray(BROWSER_USERAGENT); - if (userAgent != null && userAgent.length > 0) { - jwtProperties.setUserAgentList(userAgent); - } else { - jwtProperties.setUserAgentList(DEFAULT_BROWSER_USERAGENT.split(",")); - } - try { - RSAPublicKey publicKey = parseRSAPublicKey(publicKeyPathStr); - jwtProperties.setPublicKey(publicKey); - } catch (IOException e) { - LOG.error("Unable to read public certificate file. JWT auth will be disabled.", e); - } catch (CertificateException e) { - LOG.error("Unable to parse public certificate file. JWT auth will be disabled.", e); - } catch (ServletException e) { - LOG.error("ServletException while processing the properties", e); - } - return jwtProperties; - } else { - return null; - } - } - - /* - * public static RSAPublicKey getPublicKeyFromFile(String filePath) throws - * IOException, CertificateException { - * FileUtils.readFileToString(new File(filePath)); - * getPublicKeyFromString(pemString); } - */ - - 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_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/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java deleted file mode 100755 index fcbfd2fe90..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/AuditFilter.java +++ /dev/null @@ -1,206 +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.atlas.notification.rest.web.filters; - -import org.apache.atlas.AtlasClient; -import org.apache.atlas.AtlasException; -import org.apache.atlas.DeleteType; -import org.apache.atlas.RequestContext; -import org.apache.atlas.authorize.AtlasAuthorizationUtils; -import org.apache.atlas.notification.rest.AtlasRepositoryConfiguration; -import org.apache.atlas.server.common.util.DateTimeHelper; -import org.apache.atlas.server.common.util.Servlets; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -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.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.Date; -import java.util.Set; -import java.util.UUID; - -import static org.apache.atlas.AtlasConfiguration.REST_API_CREATE_SHELL_ENTITY_FOR_NON_EXISTING_REF; -import static org.apache.atlas.AtlasConfiguration.REST_API_ENABLE_DELETE_TYPE_OVERRIDE; - -/** - * This records audit information as part of the filter after processing the request - * and also introduces a UUID into request and response for tracing requests in logs. - */ -@Component -public class AuditFilter implements Filter { - private static final Logger LOG = LoggerFactory.getLogger(AuditFilter.class); - private static final Logger AUDIT_LOG = LoggerFactory.getLogger("AUDIT"); - - private boolean deleteTypeOverrideEnabled = false; - private boolean createShellEntityForNonExistingReference = false; - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - LOG.info("AuditFilter initialization started"); - - deleteTypeOverrideEnabled = REST_API_ENABLE_DELETE_TYPE_OVERRIDE.getBoolean(); - createShellEntityForNonExistingReference = REST_API_CREATE_SHELL_ENTITY_FOR_NON_EXISTING_REF.getBoolean(); - - LOG.info("REST_API_ENABLE_DELETE_TYPE_OVERRIDE={}", deleteTypeOverrideEnabled); - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) - throws IOException, ServletException { - final long startTime = System.currentTimeMillis(); - final Date requestTime = new Date(); - final HttpServletRequest httpRequest = (HttpServletRequest) request; - final HttpServletResponse httpResponse = (HttpServletResponse) response; - final String requestId = UUID.randomUUID().toString(); - final Thread currentThread = Thread.currentThread(); - final String oldName = currentThread.getName(); - final String user = AtlasAuthorizationUtils.getCurrentUserName(); - final Set userGroups = AtlasAuthorizationUtils.getCurrentUserGroups(); - final String deleteType = httpRequest.getParameter("deleteType"); - final boolean skipFailedEntities = Boolean.parseBoolean(httpRequest.getParameter("skipFailedEntities")); - - try { - currentThread.setName(formatName(oldName, requestId)); - - RequestContext.clear(); - RequestContext requestContext = RequestContext.get(); - requestContext.setUser(user, userGroups); - requestContext.setClientIPAddress(AtlasAuthorizationUtils.getRequestIpAddress(httpRequest)); - requestContext.setCreateShellEntityForNonExistingReference(createShellEntityForNonExistingReference); - requestContext.setForwardedAddresses(AtlasAuthorizationUtils.getForwardedAddressesFromRequest(httpRequest)); - requestContext.setSkipFailedEntities(skipFailedEntities); - - if (StringUtils.isNotEmpty(deleteType)) { - if (deleteTypeOverrideEnabled) { - requestContext.setDeleteType(DeleteType.from(deleteType)); - } else { - LOG.warn("Override of deleteType is not enabled. Ignoring parameter deleteType={}, in request from user={}", deleteType, user); - } - } - - filterChain.doFilter(request, response); - } finally { - long timeTaken = System.currentTimeMillis() - startTime; - - recordAudit(httpRequest, requestTime, user, httpResponse.getStatus(), timeTaken); - - // put the request id into the response so users can trace logs for this request - httpResponse.setHeader(AtlasClient.REQUEST_ID, requestId); - currentThread.setName(oldName); - RequestContext.clear(); - } - } - - private String formatName(String oldName, String requestId) { - return oldName + " - " + requestId; - } - - private void recordAudit(HttpServletRequest httpRequest, Date when, String who, int httpStatus, long timeTaken) { - final String fromAddress = httpRequest.getRemoteAddr(); - final String whatRequest = httpRequest.getMethod(); - final String whatURL = Servlets.getRequestURL(httpRequest); - final String whatUrlPath = httpRequest.getRequestURL().toString(); //url path without query string - - if (!isOperationExcludedFromAudit(whatRequest, whatUrlPath.toLowerCase(), null)) { - audit(new AuditLog(who, fromAddress, whatRequest, whatURL, when, httpStatus, timeTaken)); - } else { - if(LOG.isDebugEnabled()) { - LOG.debug(" Skipping Audit for {} ", whatURL); - } - } - } - - public static void audit(AuditLog auditLog) { - if (AUDIT_LOG.isInfoEnabled() && auditLog != null) { - AUDIT_LOG.info(auditLog.toString()); - } - } - - boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { - try { - return AtlasRepositoryConfiguration.isExcludedFromAudit(config, requestHttpMethod, requestOperation); - } catch (AtlasException e) { - return false; - } - } - - @Override - public void destroy() { - // do nothing - } - - public static class AuditLog { - private static final char FIELD_SEP = '|'; - - private final String userName; - private final String fromAddress; - private final String requestMethod; - private final String requestUrl; - private final Date requestTime; - private int httpStatus; - private long timeTaken; - - public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl) { - this(userName, fromAddress, requestMethod, requestUrl, new Date()); - } - - public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl, Date requestTime) { - this(userName, fromAddress, requestMethod, requestUrl, requestTime, HttpServletResponse.SC_OK, 0); - } - - public AuditLog(String userName, String fromAddress, String requestMethod, String requestUrl, Date requestTime, int httpStatus, long timeTaken) { - this.userName = userName; - this.fromAddress = fromAddress; - this.requestMethod = requestMethod; - this.requestUrl = requestUrl; - this.requestTime = requestTime; - this.httpStatus = httpStatus; - this.timeTaken = timeTaken; - } - - public void setHttpStatus(int httpStatus) { this.httpStatus = httpStatus; } - - public void setTimeTaken(long timeTaken) { this.timeTaken = timeTaken; } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - sb.append(DateTimeHelper.formatDateUTC(requestTime)) - .append(FIELD_SEP).append(userName) - .append(FIELD_SEP).append(fromAddress) - .append(FIELD_SEP).append(requestMethod) - .append(FIELD_SEP).append(requestUrl) - .append(FIELD_SEP).append(httpStatus) - .append(FIELD_SEP).append(timeTaken); - - return sb.toString(); - } - } -} \ No newline at end of file diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java deleted file mode 100644 index d43f3f1d68..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/filters/SSOAuthentication.java +++ /dev/null @@ -1,74 +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.atlas.notification.rest.web.filters; - -import com.nimbusds.jwt.SignedJWT; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; - -import java.util.Collection; - -/** - * Internal token which describes JWT authentication - */ -public class SSOAuthentication implements Authentication { - - private final SignedJWT token; - private boolean authenticated = false; - - public SSOAuthentication(SignedJWT token) { - this.token = token; - } - - @Override - public SignedJWT getCredentials() { - return token; - } - - @Override - public Object getDetails() { - return null; - } - - @Override - public boolean isAuthenticated() { - return authenticated; - } - - @Override - public void setAuthenticated(boolean authenticated) throws IllegalArgumentException { - this.authenticated = authenticated; - } - - @Override - public String getName() { - return null; - } - - @Override - public Collection getAuthorities() { - return null; - } - - @Override - public Object getPrincipal() { - return null; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java index 7c4fd2ba35..4c9acb67a6 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java @@ -17,17 +17,23 @@ */ package org.apache.atlas.notification.rest.web.security; -import org.apache.atlas.notification.rest.web.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.ActiveServerFilter; import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; -import org.apache.atlas.notification.rest.web.filters.AtlasAuthenticationFilter; -import org.apache.atlas.notification.rest.web.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; -import org.apache.atlas.notification.rest.web.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; +import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; +import org.apache.atlas.notification.rest.web.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; 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; @@ -158,4 +164,74 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .addFilterAfter(atlasAuthenticationFilter, SecurityContextHolderAwareRequestFilter.class) .addFilterAfter(csrfPreventionFilter, AtlasAuthenticationFilter.class); } + + @Bean + public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { + return activeInstanceState::getActiveServerAddress; + } + + @Bean + public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { + return new ServiceStateProvider() { + @Override + public boolean isActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + @Override + public boolean isInstanceInTransition() { + return serviceState.isInstanceInTransition(); + } + + @Override + public boolean isInstanceInMigration() { + return serviceState.isInstanceInMigration(); + } + + @Override + public String getStateName() { + return serviceState.getState().toString(); + } + }; + } + + @Bean + public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { + return new AtlasAuthenticationProviderBridge() { + @Override + public java.util.List getAuthoritiesFromUGI(String userName) { + return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + } + + @Override + public void setSsoEnabled(boolean enabled) { + authenticationProvider.setSsoEnabled(enabled); + } + + @Override + public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) { + return authenticationProvider.authenticate(authentication); + } + }; + } + + @Bean + public ActiveServerFilter commonActiveServerFilter(ActiveInstanceStateProvider activeInstanceStateProvider, ServiceStateProvider serviceStateProvider) { + return new ActiveServerFilter(activeInstanceStateProvider, serviceStateProvider); + } + + @Bean + public AtlasAuthenticationFilter commonAtlasAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasKnoxSSOAuthenticationFilter commonAtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasKnoxSSOAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasCSRFPreventionFilter commonAtlasCSRFPreventionFilter() { + return new AtlasCSRFPreventionFilter(); + } } diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml index e4b3ccfc65..6307047f8b 100644 --- a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -67,7 +67,7 @@ AuditFilter - org.apache.atlas.notification.rest.web.filters.AuditFilter + org.apache.atlas.server.common.filters.AuditFilter diff --git a/server-common/pom.xml b/server-common/pom.xml index e80583e88e..d23727aa44 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -44,6 +44,18 @@ com.googlecode.json-simple json-simple + + com.nimbusds + nimbus-jose-jwt + 10.0.2 + compile + + + org.bouncycastle + bcprov-jdk15on + + + com.sun.jersey jersey-core @@ -69,6 +81,11 @@ javax.servlet javax.servlet-api + + org.apache.atlas + atlas-authorization + ${project.version} + org.apache.atlas atlas-client-v1 @@ -81,10 +98,18 @@ org.apache.atlas atlas-intg + + org.apache.atlas + atlas-server-api + org.apache.commons commons-lang3 + + org.apache.hadoop + hadoop-common + org.apache.httpcomponents httpclient diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java similarity index 93% rename from webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java index 04acbfc8f9..873babc941 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/ActiveServerFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java @@ -16,16 +16,14 @@ * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; -import org.apache.atlas.web.service.ActiveInstanceState; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; import org.springframework.web.util.UriUtils; -import javax.inject.Inject; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -45,9 +43,8 @@ * All requests to an active instance pass through. Requests received by a passive instance are redirected * by identifying the currently active server. Requests to servers which are in transition are returned with * an error SERVICE_UNAVAILABLE. Identification of this state is carried out using - * {@link ServiceState} and {@link ActiveInstanceState}. + * {@link ServiceStateProvider} and {@link ActiveInstanceStateProvider}. */ -@Component public class ActiveServerFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(ActiveServerFilter.class); @@ -56,11 +53,10 @@ public class ActiveServerFilter implements Filter { private final String[] adminUriNotFiltered = {"/admin/export", "/admin/import", "/admin/importfile", "/admin/audits", "/admin/purge", "/admin/expimp/audit", "/admin/metrics", "/admin/server", "/admin/audit/", "admin/tasks", "/admin/debug/metrics", "/admin/audits/ageout", "admin/async/import", "admin/async/import/status"}; - private final ActiveInstanceState activeInstanceState; - private final ServiceState serviceState; + private final ActiveInstanceStateProvider activeInstanceState; + private final ServiceStateProvider serviceState; - @Inject - public ActiveServerFilter(ActiveInstanceState activeInstanceState, ServiceState serviceState) { + public ActiveServerFilter(ActiveInstanceStateProvider activeInstanceState, ServiceStateProvider serviceState) { this.activeInstanceState = activeInstanceState; this.serviceState = serviceState; } @@ -126,7 +122,7 @@ public void destroy() { } boolean isInstanceActive() { - return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + return serviceState.isActive(); } private boolean isFilteredURI(ServletRequest servletRequest) { diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java index 3ccbe3a295..69f975f865 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java @@ -16,13 +16,13 @@ * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; -import org.apache.atlas.web.security.AtlasAuthenticationProvider; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.apache.commons.configuration.Configuration; @@ -53,8 +53,8 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; -import org.springframework.stereotype.Component; +import javax.inject.Inject; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; @@ -75,6 +75,7 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -99,7 +100,6 @@ * todo: Subclass of {@link AuthenticationFilter}. */ -@Component public class AtlasAuthenticationFilter extends AuthenticationFilter { private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class); @@ -115,6 +115,8 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { private final boolean isKerberos = AuthenticationUtil.isKerberosAuthenticationEnabled(); + private final AtlasAuthenticationProviderBridge authenticationProviderBridge; + private Signer signer; private SignerSecretProvider secretProvider; private boolean isInitializedByTomcat; @@ -130,6 +132,13 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { private SecurityContextLogoutHandler logoutHandler; public AtlasAuthenticationFilter() { + this(userName -> Collections.emptyList()); + } + + @Inject + public AtlasAuthenticationFilter(AtlasAuthenticationProviderBridge authenticationProviderBridge) { + this.authenticationProviderBridge = authenticationProviderBridge; + LOG.info("==> AtlasAuthenticationFilter()"); try { @@ -781,7 +790,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } if ((existingAuth == null || !existingAuth.isAuthenticated()) && !StringUtils.isEmpty(userName)) { - final List grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + final List grantedAuths = authenticationProviderBridge.getAuthoritiesFromUGI(userName); final UserDetails principal = new User(userName, "", grantedAuths); final AbstractAuthenticationToken finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); final WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasCSRFPreventionFilter.java similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasCSRFPreventionFilter.java index 6c1974f46f..143164df6d 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasCSRFPreventionFilter.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; @@ -25,7 +25,6 @@ import org.json.simple.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -47,7 +46,6 @@ import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; import org.apache.atlas.server.common.filters.HeadersUtil; -@Component public class AtlasCSRFPreventionFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasCSRFPreventionFilter.class); diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java index d786cfc90f..3b5f0f9740 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -18,7 +18,7 @@ * under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import com.google.common.annotations.VisibleForTesting; import com.nimbusds.jose.JWSObject; @@ -26,7 +26,7 @@ import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jwt.SignedJWT; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.security.AtlasAuthenticationProvider; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; @@ -41,7 +41,6 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails; -import org.springframework.stereotype.Component; import javax.inject.Inject; import javax.servlet.Filter; @@ -76,7 +75,6 @@ import org.apache.atlas.server.common.filters.HeadersUtil; import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; -@Component("ssoAuthenticationFilter") public class AtlasKnoxSSOAuthenticationFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); @@ -94,7 +92,7 @@ public class AtlasKnoxSSOAuthenticationFilter implements Filter { private static final String PEM_HEADER = "-----BEGIN CERTIFICATE-----\n"; private static final String PEM_FOOTER = "\n-----END CERTIFICATE-----"; - private final AtlasAuthenticationProvider authenticationProvider; + private final AtlasAuthenticationProviderBridge authenticationProvider; private SSOAuthenticationProperties jwtProperties; private String originalUrlQueryParam = "originalUrl"; @@ -106,7 +104,7 @@ public class AtlasKnoxSSOAuthenticationFilter implements Filter { private JWSVerifier verifier; @Inject - public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider) { + public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge authenticationProvider) { this.authenticationProvider = authenticationProvider; try { @@ -123,7 +121,7 @@ public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticati setJwtProperties(); } - public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider, SSOAuthenticationProperties jwtProperties) { + public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge authenticationProvider, SSOAuthenticationProperties jwtProperties) { this.authenticationProvider = authenticationProvider; this.jwtProperties = jwtProperties; @@ -223,7 +221,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo //if we get the userName from the token then log into atlas using the same user if (userName != null && !userName.trim().isEmpty()) { - List grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + List grantedAuths = authenticationProvider.getAuthoritiesFromUGI(userName); final UserDetails principal = new User(userName, "", grantedAuths); final AbstractAuthenticationToken finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java similarity index 90% rename from webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java index a04fe9f4df..00635174ed 100755 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AuditFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java @@ -16,21 +16,18 @@ * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import org.apache.atlas.AtlasClient; -import org.apache.atlas.AtlasException; import org.apache.atlas.DeleteType; import org.apache.atlas.RequestContext; import org.apache.atlas.authorize.AtlasAuthorizationUtils; -import org.apache.atlas.util.AtlasRepositoryConfiguration; import org.apache.atlas.server.common.util.DateTimeHelper; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -42,6 +39,7 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.lang.reflect.Method; import java.util.Date; import java.util.Set; import java.util.UUID; @@ -53,11 +51,15 @@ * This records audit information as part of the filter after processing the request * and also introduces a UUID into request and response for tracing requests in logs. */ -@Component public class AuditFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AuditFilter.class); private static final Logger AUDIT_LOG = LoggerFactory.getLogger("AUDIT"); + private static final String[] AUDIT_EXCLUSION_CLASSES = { + "org.apache.atlas.util.AtlasRepositoryConfiguration", + "org.apache.atlas.notification.rest.AtlasRepositoryConfiguration" + }; + private boolean deleteTypeOverrideEnabled; private boolean createShellEntityForNonExistingReference; @@ -129,11 +131,21 @@ public void destroy() { } boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { - try { - return AtlasRepositoryConfiguration.isExcludedFromAudit(config, requestHttpMethod, requestOperation); - } catch (AtlasException e) { - return false; + for (String className : AUDIT_EXCLUSION_CLASSES) { + try { + Class cls = Class.forName(className); + Method method = cls.getMethod("isExcludedFromAudit", Configuration.class, String.class, String.class); + Object val = method.invoke(null, config, requestHttpMethod, requestOperation); + + if (val instanceof Boolean) { + return (Boolean) val; + } + } catch (Exception e) { + // Continue to the next implementation. + } } + + return false; } private String formatName(String oldName, String requestId) { diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthentication.java similarity index 97% rename from webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthentication.java index d15f075625..7ffe059b3c 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/SSOAuthentication.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/SSOAuthentication.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import com.nimbusds.jwt.SignedJWT; import org.springframework.security.core.Authentication; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ActiveInstanceStateProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ActiveInstanceStateProvider.java new file mode 100644 index 0000000000..1edbba5546 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ActiveInstanceStateProvider.java @@ -0,0 +1,22 @@ +/** + * 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.atlas.server.common.filters.spi; + +public interface ActiveInstanceStateProvider { + String getActiveServerAddress(); +} diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/AtlasAuthenticationProviderBridge.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/AtlasAuthenticationProviderBridge.java new file mode 100644 index 0000000000..6ef3361cbf --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/AtlasAuthenticationProviderBridge.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.atlas.server.common.filters.spi; + +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.List; + +public interface AtlasAuthenticationProviderBridge { + List getAuthoritiesFromUGI(String userName); + + default void setSsoEnabled(boolean enabled) { + } + + default Authentication authenticate(Authentication authentication) { + return authentication; + } +} diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ServiceStateProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ServiceStateProvider.java new file mode 100644 index 0000000000..ab7d6129f9 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/spi/ServiceStateProvider.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.atlas.server.common.filters.spi; + +public interface ServiceStateProvider { + boolean isActive(); + + boolean isInstanceInTransition(); + + boolean isInstanceInMigration(); + + String getStateName(); +} diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java index 32bf301667..b5c497636e 100644 --- a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java +++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java @@ -72,8 +72,8 @@ import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityDeleteRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityPartialUpdateRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityUpdateRequest; -import org.apache.atlas.web.filters.AuditFilter; -import org.apache.atlas.web.filters.AuditFilter.AuditLog; +import org.apache.atlas.server.common.filters.AuditFilter; +import org.apache.atlas.server.common.filters.AuditFilter.AuditLog; import org.apache.atlas.web.service.ServiceState; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java index b916f96e95..460e8888b9 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java @@ -76,7 +76,7 @@ import org.apache.atlas.util.SearchTracker; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; import org.apache.atlas.web.service.ServiceState; @@ -133,7 +133,7 @@ import java.util.stream.Collectors; import static javax.servlet.http.HttpServletResponse.SC_NO_CONTENT; -import static org.apache.atlas.web.filters.AtlasCSRFPreventionFilter.CSRF_TOKEN; +import static org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter.CSRF_TOKEN; /** * Jersey Resource for admin operations. diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 8e6ff5fe1f..0b77b933d6 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -17,14 +17,19 @@ */ package org.apache.atlas.web.security; -import org.apache.atlas.web.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.ActiveServerFilter; import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; -import org.apache.atlas.web.filters.AtlasAuthenticationFilter; -import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; -import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; +import org.apache.atlas.web.service.ActiveInstanceState; +import org.apache.atlas.web.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.keycloak.adapters.AdapterDeploymentContext; @@ -324,4 +329,74 @@ protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessin return filter; } + + @Bean + public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { + return activeInstanceState::getActiveServerAddress; + } + + @Bean + public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { + return new ServiceStateProvider() { + @Override + public boolean isActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + @Override + public boolean isInstanceInTransition() { + return serviceState.isInstanceInTransition(); + } + + @Override + public boolean isInstanceInMigration() { + return serviceState.isInstanceInMigration(); + } + + @Override + public String getStateName() { + return serviceState.getState().toString(); + } + }; + } + + @Bean + public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { + return new AtlasAuthenticationProviderBridge() { + @Override + public java.util.List getAuthoritiesFromUGI(String userName) { + return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + } + + @Override + public void setSsoEnabled(boolean enabled) { + authenticationProvider.setSsoEnabled(enabled); + } + + @Override + public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) { + return authenticationProvider.authenticate(authentication); + } + }; + } + + @Bean + public ActiveServerFilter commonActiveServerFilter(ActiveInstanceStateProvider activeInstanceStateProvider, ServiceStateProvider serviceStateProvider) { + return new ActiveServerFilter(activeInstanceStateProvider, serviceStateProvider); + } + + @Bean + public AtlasAuthenticationFilter commonAtlasAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasKnoxSSOAuthenticationFilter commonAtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasKnoxSSOAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasCSRFPreventionFilter commonAtlasCSRFPreventionFilter() { + return new AtlasCSRFPreventionFilter(); + } } diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml index 4bdace505c..a507b14b4a 100644 --- a/webapp/src/main/resources/spring-security.xml +++ b/webapp/src/main/resources/spring-security.xml @@ -54,13 +54,13 @@ - + - + - + AuditFilter - org.apache.atlas.web.filters.AuditFilter + org.apache.atlas.server.common.filters.AuditFilter diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java index 51d40a49d0..95fbc3011b 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java @@ -18,6 +18,8 @@ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.apache.atlas.web.service.ActiveInstanceState; import org.apache.atlas.web.service.ServiceState; import org.mockito.Mock; @@ -64,12 +66,36 @@ public void setUp() { MockitoAnnotations.initMocks(this); } + private ActiveServerFilter newActiveServerFilter() { + return new ActiveServerFilter(activeInstanceState::getActiveServerAddress, new ServiceStateProvider() { + @Override + public boolean isActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + @Override + public boolean isInstanceInTransition() { + return serviceState.isInstanceInTransition(); + } + + @Override + public boolean isInstanceInMigration() { + return serviceState.isInstanceInMigration(); + } + + @Override + public String getStateName() { + return serviceState.getState().toString(); + } + }); + } + @Test public void testShouldPassThroughRequestsIfActive() throws IOException, ServletException { when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.ACTIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -81,7 +107,7 @@ public void testShouldFailIfCannotRetrieveActiveServerAddress() throws IOExcepti when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(null); @@ -95,7 +121,7 @@ public void testShouldRedirectRequestToActiveServerAddress() throws IOException, when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getRequestURI()).thenReturn("types"); @@ -116,7 +142,7 @@ public void adminImportRequestsToPassiveServerShouldToActiveServerAddress() thro when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn(partialUrl); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getRequestURI()).thenReturn(partialUrl); @@ -133,7 +159,7 @@ public void testRedirectedRequestShouldContainQueryParameters() throws IOExcepti when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); @@ -149,7 +175,7 @@ public void testRedirectedRequestShouldContainQueryParameters() throws IOExcepti public void testRedirectedRequestShouldContainEncodeQueryParameters() throws IOException, ServletException { when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); @@ -165,7 +191,7 @@ public void testRedirectedRequestShouldContainEncodeQueryParameters() throws IOE public void testOriginalRequestShouldNotEncodeQueryParametersAgain() throws IOException, ServletException { when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); @@ -181,7 +207,7 @@ public void testOriginalRequestShouldNotEncodeQueryParametersAgain() throws IOEx public void testOriginalRequestShouldNotEncodePartiallyEncodedQueryParameters() throws IOException, ServletException { when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); @@ -198,7 +224,7 @@ public void testShouldRedirectPOSTRequest() throws IOException, ServletException when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.POST); @@ -215,7 +241,7 @@ public void testShouldRedirectPUTRequest() throws IOException, ServletException when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.PUT); @@ -232,7 +258,7 @@ public void testShouldRedirectDELETERequest() throws IOException, ServletExcepti when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); when(servletRequest.getMethod()).thenReturn(HttpMethod.DELETE); @@ -249,7 +275,7 @@ public void testShouldReturnServiceUnavailableIfStateBecomingActive() throws IOE when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.BECOMING_ACTIVE); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -262,7 +288,7 @@ public void testShouldNotRedirectAdminAPIs() throws IOException, ServletExceptio when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); when(servletRequest.getRequestURI()).thenReturn("api/atlas/admin/asmasn"); // any Admin URI is fine. - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -278,7 +304,7 @@ public void testShouldHandleMigrationStateWithRootURI() throws IOException, Serv when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost:21000/")); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -293,7 +319,7 @@ public void testShouldHandleMigrationStateWithRootURIForUnsafeMethod() throws IO when(servletRequest.getMethod()).thenReturn(HttpMethod.POST); when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost:21000/")); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -308,7 +334,7 @@ public void testShouldHandleMigrationStateWithNonRootURI() throws IOException, S when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -321,7 +347,7 @@ public void testShouldHandleEmptyRequestURI() throws IOException, ServletExcepti when(servletRequest.getRequestURI()).thenReturn(""); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); @@ -337,7 +363,7 @@ public void testShouldHandleNullQueryString() throws IOException, ServletExcepti when(servletRequest.getQueryString()).thenReturn(null); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); @@ -353,7 +379,7 @@ public void testShouldHandleEmptyQueryString() throws IOException, ServletExcept when(servletRequest.getQueryString()).thenReturn(""); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); when(activeInstanceState.getActiveServerAddress()).thenReturn(ACTIVE_SERVER_ADDRESS); @@ -368,7 +394,7 @@ public void testShouldHandleTransitionState() throws IOException, ServletExcepti when(serviceState.isInstanceInTransition()).thenReturn(true); when(servletRequest.getRequestURI()).thenReturn("api/atlas/types"); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -377,7 +403,7 @@ public void testShouldHandleTransitionState() throws IOException, ServletExcepti @Test public void testShouldHandleFilterInitialization() throws ServletException { - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); // Test init method activeServerFilter.init(null); @@ -390,7 +416,7 @@ public void testShouldHandleFilterInitialization() throws ServletException { @Test public void testIsInstanceActiveMethod() throws Exception { - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); // Test with ACTIVE state when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.ACTIVE); @@ -409,7 +435,7 @@ public void testIsInstanceActiveMethod() throws Exception { @Test public void testIsRootURIMethod() throws Exception { - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); Method isRootURIMethod = ActiveServerFilter.class.getDeclaredMethod("isRootURI", ServletRequest.class); isRootURIMethod.setAccessible(true); @@ -428,7 +454,7 @@ public void testIsRootURIMethod() throws Exception { @Test public void testIsUnsafeHttpMethodMethod() throws Exception { - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); Method isUnsafeHttpMethodMethod = ActiveServerFilter.class.getDeclaredMethod("isUnsafeHttpMethod", HttpServletRequest.class); isUnsafeHttpMethodMethod.setAccessible(true); @@ -463,7 +489,7 @@ public void testShouldHandleFilteredURIWhenInstanceIsPassive() throws IOExceptio when(servletRequest.getRequestURI()).thenReturn("api/admin/export"); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); } @@ -474,7 +500,7 @@ public void testShouldHandleFilteredURIWhenInstanceIsActive() throws IOException when(servletRequest.getRequestURI()).thenReturn("api/admin/export"); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); @@ -488,7 +514,7 @@ public void testShouldHandleFilteredURIWhenInstanceIsInTransition() throws IOExc when(servletRequest.getRequestURI()).thenReturn("api/admin/export"); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); } @@ -500,7 +526,7 @@ public void testShouldHandleFilteredURIWhenInstanceIsInMigration() throws IOExce when(servletRequest.getRequestURI()).thenReturn("api/admin/export"); when(servletRequest.getMethod()).thenReturn(HttpMethod.GET); - ActiveServerFilter activeServerFilter = new ActiveServerFilter(activeInstanceState, serviceState); + ActiveServerFilter activeServerFilter = newActiveServerFilter(); activeServerFilter.doFilter(servletRequest, servletResponse, filterChain); } diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java index cf5156569b..85d2b97ad8 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.AtlasException; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java index db3ccd6104..0a91aac3eb 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasCSRFPreventionFilterTest.java @@ -16,6 +16,7 @@ */ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.mockito.Mockito; import org.testng.annotations.Test; @@ -28,7 +29,7 @@ import java.io.IOException; import java.io.PrintWriter; -import static org.apache.atlas.web.filters.AtlasCSRFPreventionFilter.CSRF_TOKEN; +import static org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter.CSRF_TOKEN; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java index cf7556d10d..4aaabb4f00 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java @@ -23,6 +23,8 @@ import com.nimbusds.jwt.SignedJWT; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; +import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.web.security.AtlasAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; @@ -119,6 +121,25 @@ public void tearDown() { SecurityContextHolder.clearContext(); } + private AtlasAuthenticationProviderBridge bridge(AtlasAuthenticationProvider provider) { + return new AtlasAuthenticationProviderBridge() { + @Override + public java.util.List getAuthoritiesFromUGI(String userName) { + return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + } + + @Override + public void setSsoEnabled(boolean enabled) { + provider.setSsoEnabled(enabled); + } + + @Override + public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) { + return provider.authenticate(authentication); + } + }; + } + // Helper method to use reflection to set private fields private void setPrivateField(Object target, String fieldName, Object value) throws Exception { Field field = target.getClass().getDeclaredField(fieldName); @@ -147,7 +168,7 @@ public void testDoFilterSSODisabled() throws IOException, ServletException, Atla mockedStatic.when(ApplicationProperties::get).thenReturn(configuration); when(configuration.getBoolean("atlas.sso.knox.enabled", false)).thenReturn(false); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); @@ -172,7 +193,7 @@ public void testDoFilterLocalLogin() throws IOException, ServletException, Atlas when(servletRequest.getSession()).thenReturn(httpSession); when(httpSession.getAttribute("locallogin")).thenReturn("true"); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); @@ -198,7 +219,7 @@ public void testDoFilterAlreadyAuthenticated() throws IOException, ServletExcept when(securityContext.getAuthentication()).thenReturn(existingAuth); when(existingAuth.isAuthenticated()).thenReturn(true); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); @@ -238,7 +259,7 @@ public void testDoFilterInvalidJWT() throws IOException, ServletException, Parse mockedSecurityHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext); when(securityContext.getAuthentication()).thenReturn(null); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); verify(servletResponse).sendRedirect(contains("https://knox.sso?originalUrl=")); @@ -274,7 +295,7 @@ public void testDoFilterXMLHttpRequestNoJWT() throws IOException, ServletExcepti mockedSecurityHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext); when(securityContext.getAuthentication()).thenReturn(null); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); verify(servletResponse).setContentType("application/json"); @@ -291,7 +312,7 @@ public void testLoadJwtPropertiesNoProviderUrl() { when(configuration.getBoolean("atlas.sso.knox.enabled", false)).thenReturn(true); when(configuration.getString(AtlasKnoxSSOAuthenticationFilter.JWT_AUTH_PROVIDER_URL)).thenReturn(null); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); SSOAuthenticationProperties properties = filter.loadJwtProperties(); assertNull(properties); @@ -310,7 +331,7 @@ public void testLoadJwtPropertiesValid() throws CertificateException, IOExceptio when(configuration.getString(AtlasKnoxSSOAuthenticationFilter.JWT_ORIGINAL_URL_QUERY_PARAM, AtlasKnoxSSOAuthenticationFilter.JWT_ORIGINAL_URL_QUERY_PARAM_DEFAULT)).thenReturn("customUrlParam"); when(configuration.getStringArray(AtlasKnoxSSOAuthenticationFilter.BROWSER_USERAGENT)).thenReturn(new String[] {"Chrome", "Firefox"}); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); SSOAuthenticationProperties properties = filter.loadJwtProperties(); assertEquals("https://knox.sso", properties.getAuthenticationProviderUrl()); @@ -324,7 +345,7 @@ public void testLoadJwtPropertiesValid() throws CertificateException, IOExceptio public void testGetJWTFromCookie() { when(servletRequest.getCookies()).thenReturn(new Cookie[] {new Cookie("hadoop-jwt", "test.jwt.token")}); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); String jwt = filter.getJWTFromCookie(servletRequest); assertEquals("test.jwt.token", jwt); @@ -334,7 +355,7 @@ public void testGetJWTFromCookie() { public void testGetJWTFromCookieNoCookies() { when(servletRequest.getCookies()).thenReturn(null); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); String jwt = filter.getJWTFromCookie(servletRequest); assertNull(jwt); @@ -350,7 +371,7 @@ public void testValidateTokenValid() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setPublicKey(rsaPublicKey); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); // Use reflection to set verifier field setPrivateField(filter, "verifier", jwsVerifier); @@ -369,7 +390,7 @@ public void testValidateTokenInvalidSignature() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setPublicKey(rsaPublicKey); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); // Use reflection to set verifier field setPrivateField(filter, "verifier", jwsVerifier); @@ -389,7 +410,7 @@ public void testValidateTokenExpired() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setPublicKey(rsaPublicKey); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); // Use reflection to set verifier field setPrivateField(filter, "verifier", jwsVerifier); @@ -403,7 +424,7 @@ public void testValidateTokenExpired() throws Exception { public void testIsWebUserAgentUsingReflection() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setUserAgentList(new String[] {"Mozilla", "Chrome"}); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); // Use reflection to call private isWebUserAgent method Boolean result = (Boolean) callPrivateMethod(filter, "isWebUserAgent", new Class[] {String.class}, "Mozilla/5.0"); @@ -418,7 +439,7 @@ public void testConstructLoginURLNonXML() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setAuthenticationProviderUrl("https://knox.sso"); properties.setOriginalUrlQueryParam("customUrlParam"); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); when(servletRequest.getRequestURL()).thenReturn(new StringBuffer("http://localhost/atlas")); when(servletRequest.getQueryString()).thenReturn("param=value"); @@ -433,7 +454,7 @@ public void testConstructLoginURLXMLWithReferer() throws Exception { SSOAuthenticationProperties properties = new SSOAuthenticationProperties(); properties.setAuthenticationProviderUrl("https://knox.sso"); properties.setOriginalUrlQueryParam("customUrlParam"); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider, properties); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider), properties); when(servletRequest.getHeader("referer")).thenReturn("http://localhost/referer"); when(servletRequest.getHeaderNames()).thenReturn(Collections.enumeration(Collections.emptyList())); @@ -449,7 +470,7 @@ public void testParseXForwardHeaderUsingReflection() throws Exception { when(servletRequest.getHeaders("x-forwarded-host")).thenReturn(Collections.enumeration(Collections.singletonList("proxy.host"))); when(servletRequest.getHeaders("x-forwarded-context")).thenReturn(Collections.enumeration(Collections.singletonList("/context"))); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); // Use reflection to call private parseXForwardHeader method @SuppressWarnings("unchecked") @@ -469,7 +490,7 @@ public void testConstructForwardableURLUsingReflection() throws Exception { xFwdHeaderMap.put("x-forwarded-host", "proxy.host"); xFwdHeaderMap.put("x-forwarded-context", "/context"); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); // Use reflection to call private constructForwardableURL method String url = (String) callPrivateMethod(filter, "constructForwardableURL", @@ -480,7 +501,7 @@ public void testConstructForwardableURLUsingReflection() throws Exception { @Test public void testSafeAppend() throws Exception { - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); StringBuilder sb = new StringBuilder(); // Use reflection to call private safeAppend method @@ -491,7 +512,7 @@ public void testSafeAppend() throws Exception { @Test public void testInit() throws ServletException { - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); FilterConfig filterConfig = mock(FilterConfig.class); // This should not throw any exception @@ -500,7 +521,7 @@ public void testInit() throws ServletException { @Test public void testDestroy() { - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); // This should not throw any exception filter.destroy(); @@ -538,7 +559,7 @@ public void testDoFilterNonWebUserAgent() throws IOException, ServletException, mockedSecurityHolder.when(SecurityContextHolder::getContext).thenReturn(securityContext); when(securityContext.getAuthentication()).thenReturn(null); - AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(authenticationProvider); + AtlasKnoxSSOAuthenticationFilter filter = new AtlasKnoxSSOAuthenticationFilter(bridge(authenticationProvider)); filter.doFilter(servletRequest, servletResponse, filterChain); // Should continue with filter chain for non-web user agents diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java index 582e3d9460..ced9128888 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java @@ -17,6 +17,7 @@ */ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AuditFilter; import org.apache.atlas.DeleteType; import org.apache.atlas.RequestContext; import org.apache.atlas.util.AtlasRepositoryConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java index 7609b58709..809c94af50 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java @@ -19,12 +19,12 @@ package org.apache.atlas.web.security; -import org.apache.atlas.web.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.ActiveServerFilter; import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; -import org.apache.atlas.web.filters.AtlasAuthenticationFilter; -import org.apache.atlas.web.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; -import org.apache.atlas.web.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; import org.keycloak.adapters.AdapterDeploymentContext; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java index 4d589addc7..0ed6977ca7 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java @@ -27,7 +27,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -77,7 +77,7 @@ public void setup() throws Exception { System.setProperty("atlas.conf", persistDir); - applicationContext = new ClassPathXmlApplicationContext("test-spring-security.xml"); + applicationContext = new AnnotationConfigApplicationContext(TestSpringSecurityBridgeConfig.class); authProvider = applicationContext.getBean(org.apache.atlas.web.security.AtlasAuthenticationProvider.class); } diff --git a/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java new file mode 100644 index 0000000000..ed500716c0 --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java @@ -0,0 +1,57 @@ +/* + * 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 ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.atlas.web.security; + +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportResource; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; + +import java.util.List; + +/** + * Loads {@code test-spring-security.xml} for tests (e.g. {@link FileAuthenticationTest}) and + * registers {@link AtlasAuthenticationProviderBridge} for Knox/ Kerberos filters declared in XML. + * XML alone cannot express the bridge; production uses {@link AtlasSecurityConfig} instead. + */ +@Configuration +@ImportResource("classpath:test-spring-security.xml") +public class TestSpringSecurityBridgeConfig { + + @Bean + public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { + return new AtlasAuthenticationProviderBridge() { + @Override + public List getAuthoritiesFromUGI(String userName) { + return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + } + + @Override + public void setSsoEnabled(boolean enabled) { + authenticationProvider.setSsoEnabled(enabled); + } + + @Override + public Authentication authenticate(Authentication authentication) { + return authenticationProvider.authenticate(authentication); + } + }; + } +} diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index 8eb7ac3898..b7988b329e 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -71,14 +71,14 @@ - + - - + + - + AuditFilter - org.apache.atlas.web.filters.AuditFilter + org.apache.atlas.server.common.filters.AuditFilter From 6270135b043adf1648d9feaa922a5dd810c4fc7c Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 23 Apr 2026 10:59:28 +0530 Subject: [PATCH 08/22] Updated method visibility to resolve the testcases and compilation error's. --- .../server/common/filters/AtlasAuthenticationFilter.java | 6 +++--- .../common/filters/AtlasKnoxSSOAuthenticationFilter.java | 6 +++--- .../org/apache/atlas/server/common/filters/AuditFilter.java | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java index 69f975f865..33397bdb98 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java @@ -272,7 +272,7 @@ public void destroy() { } @Override - protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + public Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { LOG.info("==> AtlasAuthenticationFilter.getConfiguration()"); try { @@ -459,7 +459,7 @@ public void doFilter(final ServletRequest request, final ServletResponse respons } } - void parseBrowserUserAgents(String userAgents) { + public void parseBrowserUserAgents(String userAgents) { String[] agentsArray = userAgents.split(","); browserUserAgents = new HashSet<>(); @@ -469,7 +469,7 @@ void parseBrowserUserAgents(String userAgents) { } } - boolean isBrowser(String userAgent) { + public boolean isBrowser(String userAgent) { if (userAgent != null) { for (Pattern pattern : browserUserAgents) { Matcher matcher = pattern.matcher(userAgent); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java index 3b5f0f9740..6d299890c8 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -302,7 +302,7 @@ public SSOAuthenticationProperties loadJwtProperties() { * @param req servlet request to get the JWT token from * @return serialized JWT token */ - protected String getJWTFromCookie(HttpServletRequest req) { + public String getJWTFromCookie(HttpServletRequest req) { String serializedJWT = null; Cookie[] cookies = req.getCookies(); @@ -327,7 +327,7 @@ protected String getJWTFromCookie(HttpServletRequest req) { * @param request for getting the original request URL * @return url to use as login url for redirect */ - protected String constructLoginURL(HttpServletRequest request, boolean isXMLRequest) { + public String constructLoginURL(HttpServletRequest request, boolean isXMLRequest) { String delimiter = "?"; if (authenticationProviderUrl.contains("?")) { @@ -377,7 +377,7 @@ protected String constructLoginURL(HttpServletRequest request, boolean isXMLRequ * @param jwtToken the token to validate * @return true if valid */ - protected boolean validateToken(SignedJWT jwtToken) { + public boolean validateToken(SignedJWT jwtToken) { boolean isValid = validateSignature(jwtToken); if (isValid) { diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java index 00635174ed..a154b84a2c 100755 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AuditFilter.java @@ -130,7 +130,7 @@ public void destroy() { // do nothing } - boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { + public boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { for (String className : AUDIT_EXCLUSION_CLASSES) { try { Class cls = Class.forName(className); From 7b4964fbaacea99820781358b5086972432f4ee2 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 23 Apr 2026 14:10:01 +0530 Subject: [PATCH 09/22] Updating security classes and package to use server-common module for both webapp and rest-notification webapp. --- .../AtlasADAuthenticationProvider.java | 205 ------------ .../AtlasAbstractAuthenticationProvider.java | 149 --------- .../AtlasFileAuthenticationProvider.java | 80 ----- .../AtlasLdapAuthenticationProvider.java | 301 ------------------ .../AtlasPamAuthenticationProvider.java | 175 ---------- .../web/security/AtlasSecurityConfig.java | 33 +- .../AtlasSecurityStateProviderConfig.java | 58 ++++ .../rest/web/security/PamLoginModule.java | 236 -------------- server-common/pom.xml | 19 ++ .../atlas/server/common/model/User.java | 124 ++++++++ .../AtlasADAuthenticationProvider.java | 4 +- .../AtlasAbstractAuthenticationProvider.java | 2 +- .../security/AtlasAuthenticationProvider.java | 114 +++---- .../AtlasFileAuthenticationProvider.java | 2 +- .../AtlasLdapAuthenticationProvider.java | 4 +- .../AtlasPamAuthenticationProvider.java | 6 +- .../common}/security/PamLoginModule.java | 2 +- .../NotificationHookConsumer.java | 2 +- .../security/AtlasAuthenticationProvider.java | 5 + .../AtlasKeycloakAuthenticationProvider.java | 2 +- .../web/security/AtlasSecurityConfig.java | 32 -- .../AtlasSecurityStateProviderConfig.java | 58 ++++ .../NotificationHookConsumerTest.java | 26 +- .../AtlasAuthenticationFilterTest.java | 2 +- .../AtlasADAuthenticationProviderTest.java | 2 + ...lasAbstractAuthenticationProviderTest.java | 1 + .../AtlasAuthenticationProviderTest.java | 4 + ...lasKeycloakAuthenticationProviderTest.java | 1 + .../AtlasLdapAuthenticationProviderTest.java | 4 +- .../AtlasPamAuthenticationProviderTest.java | 6 +- .../web/security/PamLoginModuleTest.java | 1 + .../test/resources/test-spring-security.xml | 8 +- 32 files changed, 369 insertions(+), 1299 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/model/User.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasADAuthenticationProvider.java (98%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasAbstractAuthenticationProvider.java (99%) rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasAuthenticationProvider.java (71%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasFileAuthenticationProvider.java (98%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasLdapAuthenticationProvider.java (99%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/AtlasPamAuthenticationProvider.java (97%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/security/PamLoginModule.java (99%) create mode 100644 webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java deleted file mode 100644 index ff957ef766..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasADAuthenticationProvider.java +++ /dev/null @@ -1,205 +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.atlas.notification.rest.web.security; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.notification.rest.web.model.User; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ldap.core.support.LdapContextSource; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.ldap.DefaultSpringSecurityContextSource; -import org.springframework.security.ldap.authentication.BindAuthenticator; -import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; -import org.springframework.security.ldap.authentication.ad.ActiveDirectoryLdapAuthenticationProvider; -import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Properties; - -import org.apache.atlas.server.common.security.AtlasAuthenticationException; - -@Component -public class AtlasADAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - private static Logger LOG = LoggerFactory.getLogger(AtlasADAuthenticationProvider.class); - - private String adURL; - private String adDomain; - private String adBindDN; - private String adBindPassword; - private String adUserSearchFilter; - private String adBase; - private String adReferral; - private String adDefaultRole; - private boolean groupsFromUGI; - - @PostConstruct - public void setup() { - setADProperties(); - } - - @Override - public Authentication authenticate(Authentication authentication) { - Authentication auth = getADBindAuthentication(authentication); - if (auth != null && auth.isAuthenticated()) { - return auth; - } else { - auth = getADAuthentication(authentication); - if (auth != null && auth.isAuthenticated()) { - return auth; - } - } - if (auth == null) { - throw new AtlasAuthenticationException("AD Authentication Failed"); - } - return auth; - } - - private Authentication getADBindAuthentication (Authentication authentication) { - try { - String userName = authentication.getName(); - String userPassword = ""; - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } - - LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource(adURL); - ldapContextSource.setUserDn(adBindDN); - ldapContextSource.setPassword(adBindPassword); - ldapContextSource.setReferral(adReferral); - ldapContextSource.setCacheEnvironmentProperties(true); - ldapContextSource.setAnonymousReadOnly(false); - ldapContextSource.setPooled(true); - ldapContextSource.afterPropertiesSet(); - - FilterBasedLdapUserSearch userSearch=new FilterBasedLdapUserSearch(adBase, adUserSearchFilter,ldapContextSource); - userSearch.setSearchSubtree(true); - - BindAuthenticator bindAuthenticator = new BindAuthenticator(ldapContextSource); - bindAuthenticator.setUserSearch(userSearch); - bindAuthenticator.afterPropertiesSet(); - - LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator); - - if (userName != null && userPassword != null - && !userName.trim().isEmpty() - && !userPassword.trim().isEmpty()) { - final List grantedAuths = getAuthorities(userName); - final UserDetails principal = new User(userName, userPassword, - grantedAuths); - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( - principal, userPassword, grantedAuths); - authentication = ldapAuthenticationProvider.authenticate(finalAuthentication); - if (groupsFromUGI) { - authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); - } - return authentication; - } else { - LOG.error("AD Authentication Failed userName or userPassword is null or empty"); - return null; - } - } catch (Exception e) { - LOG.error("AD Authentication Failed:", e); - return null; - } - } - - private Authentication getADAuthentication(Authentication authentication) { - try { - String userName = authentication.getName(); - String userPassword = ""; - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } - - ActiveDirectoryLdapAuthenticationProvider adAuthenticationProvider = - new ActiveDirectoryLdapAuthenticationProvider(adDomain, adURL); - adAuthenticationProvider.setConvertSubErrorCodesToExceptions(true); - adAuthenticationProvider.setUseAuthenticationRequestCredentials(true); - adAuthenticationProvider.setSearchFilter(adUserSearchFilter); - - if (userName != null && userPassword != null - && !userName.trim().isEmpty() - && !userPassword.trim().isEmpty()) { - final List grantedAuths = getAuthorities(userName); - final UserDetails principal = new User(userName, userPassword, - grantedAuths); - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( - principal, userPassword, grantedAuths); - authentication = adAuthenticationProvider.authenticate(finalAuthentication); - if(groupsFromUGI) { - authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); - } - return authentication; - } else { - LOG.error("AD Authentication Failed userName or userPassword is null or empty"); - return null; - } - } catch (Exception e) { - LOG.error("AD Authentication Failed:", e); - return null; - } - } - - private void setADProperties() { - try { - - Configuration configuration = ApplicationProperties.get(); - Properties properties = ConfigurationConverter.getProperties(configuration.subset("atlas.authentication.method.ldap.ad")); - this.adDomain = properties.getProperty("domain"); - this.adURL = properties.getProperty("url"); - this.adBindDN = properties.getProperty("bind.dn"); - this.adBindPassword = properties.getProperty("bind.password"); - this.adUserSearchFilter = properties.getProperty("user.searchfilter"); - if (adUserSearchFilter==null || adUserSearchFilter.trim().isEmpty()) { - adUserSearchFilter="(sAMAccountName={0})"; - } - this.adBase = properties.getProperty("base.dn"); - this.adReferral = properties.getProperty("referral"); - this.adDefaultRole = properties.getProperty("default.role"); - - this.groupsFromUGI = configuration.getBoolean("atlas.authentication.method.ldap.ugi-groups", true); - - if(LOG.isDebugEnabled()) { - LOG.debug("AtlasADAuthenticationProvider{" + - "adURL='" + adURL + '\'' + - ", adDomain='" + adDomain + '\'' + - ", adBindDN='" + adBindDN + '\'' + - ", adUserSearchFilter='" + adUserSearchFilter + '\'' + - ", adBase='" + adBase + '\'' + - ", adReferral='" + adReferral + '\'' + - ", adDefaultRole='" + adDefaultRole + '\'' + - ", groupsFromUGI=" + groupsFromUGI + - '}'); - } - - - } catch (Exception e) { - LOG.error("Exception while setADProperties", e); - } - } - -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java deleted file mode 100644 index e88c60d944..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAbstractAuthenticationProvider.java +++ /dev/null @@ -1,149 +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.atlas.notification.rest.web.security; - -import org.apache.atlas.utils.AuthenticationUtil; -import org.apache.commons.collections.CollectionUtils; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.security.Groups; -import org.apache.hadoop.security.UserGroupInformation; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -public abstract class AtlasAbstractAuthenticationProvider implements AuthenticationProvider { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAbstractAuthenticationProvider.class); - - @Override - public boolean supports(Class authentication) { - return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); - } - - /** - * - * @param authentication - * @return - */ - public Authentication getAuthenticationWithGrantedAuthority( - Authentication authentication) { - UsernamePasswordAuthenticationToken result = null; - if (authentication != null && authentication.isAuthenticated()) { - final List grantedAuths = getAuthorities(authentication - .getName()); - final UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString(), - grantedAuths); - result = new UsernamePasswordAuthenticationToken(userDetails, - authentication.getCredentials(), grantedAuths); - result.setDetails(authentication.getDetails()); - return result; - } - return authentication; - } - - /** - * This method will be modified when actual roles are introduced. - * - */ - protected List getAuthorities(String username) { - final List grantedAuths = new ArrayList<>(); - grantedAuths.add(new SimpleGrantedAuthority("DATA_SCIENTIST")); - return grantedAuths; - } - - - public Authentication getAuthenticationWithGrantedAuthorityFromUGI( - Authentication authentication) { - UsernamePasswordAuthenticationToken result = null; - if (authentication != null && authentication.isAuthenticated()) { - - List grantedAuthsUGI = getAuthoritiesFromUGI(authentication - .getName()); - - final UserDetails userDetails = new User(authentication.getName(), authentication.getCredentials().toString(), - grantedAuthsUGI); - result = new UsernamePasswordAuthenticationToken(userDetails, - authentication.getCredentials(), grantedAuthsUGI); - result.setDetails(authentication.getDetails()); - return result; - } - return authentication; - } - - public static List getAuthoritiesFromUGI(String userName) { - Set userGroups = new HashSet<>(); - UserGroupInformation ugi = UserGroupInformation.createRemoteUser(userName); - - if (ugi != null) { - String[] groups = ugi.getGroupNames(); - - if(LOG.isDebugEnabled()) { - LOG.debug("UserGroupInformation userGroups=" + Arrays.toString(groups)); - } - - if (groups != null) { - for (String group : groups) { - userGroups.add(group); - } - } - } - - // if group empty take groups from Hadoop LDAP-based group mapping - if (CollectionUtils.isEmpty(userGroups) || AuthenticationUtil.includeHadoopGroups()) { - try { - Configuration config = new Configuration(); - Groups gp = new Groups(config); - List groups = gp.getGroups(userName); - - if(LOG.isDebugEnabled()) { - LOG.debug("Hadoop userGroups=" + groups); - } - - if (groups != null) { - for (String group : groups) { - userGroups.add(group); - } - } - } catch (java.io.IOException e) { - LOG.error("Exception while fetching groups ", e); - } - } - - List ret = new ArrayList<>(); - - for (String userGroup : userGroups) { - ret.add(new SimpleGrantedAuthority(userGroup)); - } - - return ret; - } - -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java deleted file mode 100644 index b108cc3ad5..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasFileAuthenticationProvider.java +++ /dev/null @@ -1,80 +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 compliRance 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.atlas.notification.rest.web.security; - - -import org.apache.atlas.server.common.dao.UserDao; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.util.Collection; - - -@Component -public class AtlasFileAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - - private static Logger logger = LoggerFactory.getLogger(AtlasFileAuthenticationProvider.class); - - private final UserDetailsService userDetailsService; - - @Inject - public AtlasFileAuthenticationProvider(UserDetailsService userDetailsService) { - this.userDetailsService = userDetailsService; - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - String username = authentication.getName(); - String password = (String) authentication.getCredentials(); - - if (username == null || username.isEmpty()) { - logger.error("Username can't be null or empty."); - - throw new BadCredentialsException("Username can't be null or empty."); - } - - if (password == null || password.isEmpty()) { - logger.error("Password can't be null or empty."); - - throw new BadCredentialsException("Password can't be null or empty."); - } - - UserDetails user = userDetailsService.loadUserByUsername(username); - boolean isValidPassword = UserDao.checkEncrypted(password, user.getPassword(), username); - - if (!isValidPassword) { - logger.error("Wrong password " + username); - - throw new BadCredentialsException("Wrong password"); - } - - Collection authorities = user.getAuthorities(); - - authentication = new UsernamePasswordAuthenticationToken(username, password, authorities); - - return authentication; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java deleted file mode 100644 index 6ef27e5267..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasLdapAuthenticationProvider.java +++ /dev/null @@ -1,301 +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.atlas.notification.rest.web.security; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.notification.rest.web.model.User; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.configuration.ConfigurationConverter; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.ldap.core.support.LdapContextSource; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.ldap.DefaultSpringSecurityContextSource; -import org.springframework.security.ldap.authentication.BindAuthenticator; -import org.springframework.security.ldap.authentication.LdapAuthenticationProvider; -import org.springframework.security.ldap.search.FilterBasedLdapUserSearch; -import org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import java.util.List; -import java.util.Properties; - -import org.apache.atlas.server.common.security.AtlasAuthenticationException; - -@Component -public class AtlasLdapAuthenticationProvider extends - AtlasAbstractAuthenticationProvider { - private static Logger LOG = LoggerFactory.getLogger(AtlasLdapAuthenticationProvider.class); - private boolean isDebugEnabled = LOG.isDebugEnabled(); - - private String ldapURL; - private String ldapUserDNPattern; - private String ldapGroupSearchBase; - private String ldapGroupSearchFilter; - private String ldapGroupRoleAttribute; - private String ldapBindDN; - private String ldapBindPassword; - private String ldapDefaultRole; - private String ldapUserSearchFilter; - private String ldapReferral; - private String ldapBase; - private boolean groupsFromUGI; - - @PostConstruct - public void setup() { - setLdapProperties(); - } - - @Override - public Authentication authenticate(Authentication authentication) - throws AuthenticationException { - try { - authentication = getLdapBindAuthentication(authentication); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } else { - authentication = getLdapAuthentication(authentication); - if (authentication != null && authentication.isAuthenticated()) { - return authentication; - } - } - } catch (Exception e) { - throw new AtlasAuthenticationException(e.getMessage(), e.getCause()); - } - return authentication; - } - - private Authentication getLdapBindAuthentication( - Authentication authentication) { - try { - if (isDebugEnabled) { - LOG.debug("==> AtlasLdapAuthenticationProvider getLdapBindAuthentication"); - } - String userName = authentication.getName(); - String userPassword = ""; - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } - - LdapContextSource ldapContextSource = getLdapContextSource(); - - DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = getDefaultLdapAuthoritiesPopulator(ldapContextSource); - - if (ldapUserSearchFilter == null - || ldapUserSearchFilter.trim().isEmpty()) { - ldapUserSearchFilter = "(uid={0})"; - } - - FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch( - ldapBase, ldapUserSearchFilter, ldapContextSource); - userSearch.setSearchSubtree(true); - - BindAuthenticator bindAuthenticator = getBindAuthenticator( - userSearch, ldapContextSource); - - LdapAuthenticationProvider ldapAuthenticationProvider = new LdapAuthenticationProvider( - bindAuthenticator, defaultLdapAuthoritiesPopulator); - - if (userName != null && userPassword != null - && !userName.trim().isEmpty() - && !userPassword.trim().isEmpty()) { - final List grantedAuths = getAuthorities(userName); - final UserDetails principal = new User(userName, userPassword, - grantedAuths); - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( - principal, userPassword, grantedAuths); - authentication = ldapAuthenticationProvider.authenticate(finalAuthentication); - if(groupsFromUGI) { - authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); - } - return authentication; - } else { - LOG.error("LDAP Authentication::userName or userPassword is null or empty for userName " - + userName); - } - } catch (Exception e) { - LOG.error(" getLdapBindAuthentication LDAP Authentication Failed:", e); - } - if (isDebugEnabled) { - LOG.debug("<== AtlasLdapAuthenticationProvider getLdapBindAuthentication"); - } - return authentication; - } - - private Authentication getLdapAuthentication(Authentication authentication) { - - if (isDebugEnabled) { - LOG.debug("==> AtlasLdapAuthenticationProvider getLdapAuthentication"); - } - - try { - // taking the user-name and password from the authentication - // object. - String userName = authentication.getName(); - String userPassword = ""; - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } - - // populating LDAP context source with LDAP URL and user-DN-pattern - LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource( - ldapURL); - - ldapContextSource.setCacheEnvironmentProperties(false); - ldapContextSource.setAnonymousReadOnly(true); - - // Creating BindAuthenticator using Ldap Context Source. - BindAuthenticator bindAuthenticator = new BindAuthenticator( - ldapContextSource); - //String[] userDnPatterns = new String[] { rangerLdapUserDNPattern }; - String[] userDnPatterns = ldapUserDNPattern.split(";"); - bindAuthenticator.setUserDnPatterns(userDnPatterns); - - LdapAuthenticationProvider ldapAuthenticationProvider = null; - - if (!StringUtils.isEmpty(ldapGroupSearchBase) && !StringUtils.isEmpty(ldapGroupSearchFilter)) { - // Creating LDAP authorities populator using Ldap context source and - // Ldap group search base. - // populating LDAP authorities populator with group search - // base,group role attribute, group search filter. - DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator( - ldapContextSource, ldapGroupSearchBase); - defaultLdapAuthoritiesPopulator.setGroupRoleAttribute(ldapGroupRoleAttribute); - defaultLdapAuthoritiesPopulator.setGroupSearchFilter(ldapGroupSearchFilter); - defaultLdapAuthoritiesPopulator.setIgnorePartialResultException(true); - - // Creating Ldap authentication provider using BindAuthenticator and Ldap authentication populator - ldapAuthenticationProvider = new LdapAuthenticationProvider( - bindAuthenticator, defaultLdapAuthoritiesPopulator); - } else { - ldapAuthenticationProvider = new LdapAuthenticationProvider(bindAuthenticator); - } - - // getting user authenticated - if (userName != null && userPassword != null - && !userName.trim().isEmpty() - && !userPassword.trim().isEmpty()) { - final List grantedAuths = getAuthorities(userName); - - final UserDetails principal = new User(userName, userPassword, - grantedAuths); - - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( - principal, userPassword, grantedAuths); - - authentication = ldapAuthenticationProvider - .authenticate(finalAuthentication); - if (groupsFromUGI) { - authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); - } - return authentication; - } else { - return authentication; - } - } catch (Exception e) { - LOG.error("getLdapAuthentication LDAP Authentication Failed:", e); - } - if (isDebugEnabled) { - LOG.debug("<== AtlasLdapAuthenticationProvider getLdapAuthentication"); - } - return authentication; - } - - private void setLdapProperties() { - try { - Configuration configuration = ApplicationProperties.get(); - Properties properties = ConfigurationConverter.getProperties(configuration.subset("atlas.authentication.method.ldap")); - ldapURL = properties.getProperty("url"); - ldapUserDNPattern = properties.getProperty("userDNpattern"); - ldapGroupSearchBase = properties.getProperty("groupSearchBase"); - ldapGroupSearchFilter = properties.getProperty("groupSearchFilter"); - ldapGroupRoleAttribute = properties.getProperty("groupRoleAttribute"); - ldapBindDN = properties.getProperty("bind.dn"); - ldapBindPassword = properties.getProperty("bind.password"); - ldapDefaultRole = properties.getProperty("default.role"); - ldapUserSearchFilter = properties.getProperty("user.searchfilter"); - ldapReferral = properties.getProperty("referral"); - ldapBase = properties.getProperty("base.dn"); - groupsFromUGI = configuration.getBoolean("atlas.authentication.method.ldap.ugi-groups", true); - - if(LOG.isDebugEnabled()) { - LOG.debug("AtlasLdapAuthenticationProvider{" + - "ldapURL='" + ldapURL + '\'' + - ", ldapUserDNPattern='" + ldapUserDNPattern + '\'' + - ", ldapGroupSearchBase='" + ldapGroupSearchBase + '\'' + - ", ldapGroupSearchFilter='" + ldapGroupSearchFilter + '\'' + - ", ldapGroupRoleAttribute='" + ldapGroupRoleAttribute + '\'' + - ", ldapBindDN='" + ldapBindDN + '\'' + - ", ldapDefaultRole='" + ldapDefaultRole + '\'' + - ", ldapUserSearchFilter='" + ldapUserSearchFilter + '\'' + - ", ldapReferral='" + ldapReferral + '\'' + - ", ldapBase='" + ldapBase + '\'' + - ", groupsFromUGI=" + groupsFromUGI + - '}'); - } - - } catch (Exception e) { - LOG.error("Exception while setLdapProperties", e); - } - - } - - private LdapContextSource getLdapContextSource() throws Exception { - LdapContextSource ldapContextSource = new DefaultSpringSecurityContextSource( - ldapURL); - ldapContextSource.setUserDn(ldapBindDN); - ldapContextSource.setPassword(ldapBindPassword); - ldapContextSource.setReferral(ldapReferral); - ldapContextSource.setCacheEnvironmentProperties(false); - ldapContextSource.setAnonymousReadOnly(false); - ldapContextSource.setPooled(true); - ldapContextSource.afterPropertiesSet(); - return ldapContextSource; - } - - private DefaultLdapAuthoritiesPopulator getDefaultLdapAuthoritiesPopulator( - LdapContextSource ldapContextSource) { - DefaultLdapAuthoritiesPopulator defaultLdapAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator( - ldapContextSource, ldapGroupSearchBase); - defaultLdapAuthoritiesPopulator - .setGroupRoleAttribute(ldapGroupRoleAttribute); - defaultLdapAuthoritiesPopulator - .setGroupSearchFilter(ldapGroupSearchFilter); - defaultLdapAuthoritiesPopulator.setIgnorePartialResultException(true); - return defaultLdapAuthoritiesPopulator; - } - - private BindAuthenticator getBindAuthenticator( - FilterBasedLdapUserSearch userSearch, - LdapContextSource ldapContextSource) throws Exception { - BindAuthenticator bindAuthenticator = new BindAuthenticator( - ldapContextSource); - bindAuthenticator.setUserSearch(userSearch); - String[] userDnPatterns = new String[] { ldapUserDNPattern }; - bindAuthenticator.setUserDnPatterns(userDnPatterns); - bindAuthenticator.afterPropertiesSet(); - return bindAuthenticator; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java deleted file mode 100644 index edede097ff..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasPamAuthenticationProvider.java +++ /dev/null @@ -1,175 +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.atlas.notification.rest.web.security; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.notification.rest.web.model.User; -import org.apache.commons.configuration.ConfigurationConverter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.authentication.jaas.DefaultJaasAuthenticationProvider; -import org.springframework.security.authentication.jaas.memory.InMemoryConfiguration; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.stereotype.Component; - -import javax.annotation.PostConstruct; -import javax.security.auth.login.AppConfigurationEntry; -import javax.security.auth.login.Configuration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; - -import org.apache.atlas.server.common.security.AtlasAuthenticationException; -import org.apache.atlas.server.common.security.UserAuthorityGranter; - -@Component -public class AtlasPamAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - - private static Logger LOG = LoggerFactory.getLogger(AtlasPamAuthenticationProvider.class); - private boolean isDebugEnabled = LOG.isDebugEnabled(); - private static String loginModuleName = "org.apache.atlas.notification.rest.web.security.PamLoginModule"; - private static AppConfigurationEntry.LoginModuleControlFlag controlFlag = - AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; - private Map options = new HashMap(); - private boolean groupsFromUGI; - private DefaultJaasAuthenticationProvider jaasAuthenticationProvider = - new DefaultJaasAuthenticationProvider(); - - @PostConstruct - public void setup() { - setPamProperties(); - init(); - } - - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - Authentication auth = getPamAuthentication(authentication); - if (auth != null && auth.isAuthenticated()) { - return auth; - } else { - throw new AtlasAuthenticationException("PAM Authentication Failed"); - } - } - - private Authentication getPamAuthentication(Authentication authentication) { - if (isDebugEnabled) { - LOG.debug("==> AtlasPamAuthenticationProvider getPamAuthentication"); - } - try { - String userName = authentication.getName(); - String userPassword = ""; - if (authentication.getCredentials() != null) { - userPassword = authentication.getCredentials().toString(); - } - - // getting user authenticated - if (userName != null && userPassword != null - && !userName.trim().isEmpty() - && !userPassword.trim().isEmpty()) { - final List grantedAuths = getAuthorities(userName); - - final UserDetails principal = new User(userName, userPassword, - grantedAuths); - - final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken( - principal, userPassword, grantedAuths); - - authentication = jaasAuthenticationProvider - .authenticate(finalAuthentication); - - if(groupsFromUGI) { - authentication = getAuthenticationWithGrantedAuthorityFromUGI(authentication); - } else { - authentication = getAuthenticationWithGrantedAuthority(authentication); - } - return authentication; - } else { - return authentication; - } - - } catch (Exception e) { - LOG.debug("Pam Authentication Failed:", e); - } - if (isDebugEnabled) { - LOG.debug("<== AtlasPamAuthenticationProvider getPamAuthentication : " + jaasAuthenticationProvider); - } - return authentication; - } - - private void setPamProperties() { - try { - this.groupsFromUGI = ApplicationProperties.get().getBoolean("atlas.authentication.method.pam.ugi-groups", true); - Properties properties = ConfigurationConverter.getProperties(ApplicationProperties.get() - .subset("atlas.authentication.method.pam")); - for (String key : properties.stringPropertyNames()) { - String value = properties.getProperty(key); - options.put(key, value); - } - if (!options.containsKey("service")) { - options.put("service", "atlas-login"); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("AtlasPAMAuthenticationProvider{groupsFromUGI= "+ groupsFromUGI +'\'' + - ", options=" + options + - '}'); - } - - } catch (Exception e) { - LOG.error("Exception while setLdapProperties", e); - } - } - - private void init() { - try { - AppConfigurationEntry appConfigurationEntry = new AppConfigurationEntry( - loginModuleName, controlFlag, options); - AppConfigurationEntry[] appConfigurationEntries = new AppConfigurationEntry[]{appConfigurationEntry}; - Map appConfigurationEntriesOptions = - new HashMap(); - appConfigurationEntriesOptions.put("SPRINGSECURITY", - appConfigurationEntries); - Configuration configuration = new InMemoryConfiguration( - appConfigurationEntriesOptions); - jaasAuthenticationProvider.setConfiguration(configuration); - UserAuthorityGranter authorityGranter = new UserAuthorityGranter(); - UserAuthorityGranter[] authorityGranters = new UserAuthorityGranter[]{authorityGranter}; - jaasAuthenticationProvider.setAuthorityGranters(authorityGranters); - jaasAuthenticationProvider.afterPropertiesSet(); - - if(LOG.isDebugEnabled()) { - LOG.debug("AtlasPAMAuthenticationProvider{" + - "jaasAuthenticationProvider='" + jaasAuthenticationProvider + '\'' + - ", loginModuleName='" + loginModuleName + '\'' + - ", controlFlag='" + controlFlag + '\'' + - ", options='" + options + '}'); - } - - - } catch (Exception e) { - LOG.error("Failed to init PAM Authentication", e); - } - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java index 4c9acb67a6..3158580af2 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java @@ -27,8 +27,7 @@ import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; -import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; -import org.apache.atlas.notification.rest.web.service.ServiceState; +import org.apache.atlas.server.common.security.AtlasAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -165,36 +164,6 @@ protected void configure(HttpSecurity httpSecurity) throws Exception { .addFilterAfter(csrfPreventionFilter, AtlasAuthenticationFilter.class); } - @Bean - public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { - return activeInstanceState::getActiveServerAddress; - } - - @Bean - public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { - return new ServiceStateProvider() { - @Override - public boolean isActive() { - return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; - } - - @Override - public boolean isInstanceInTransition() { - return serviceState.isInstanceInTransition(); - } - - @Override - public boolean isInstanceInMigration() { - return serviceState.isInstanceInMigration(); - } - - @Override - public String getStateName() { - return serviceState.getState().toString(); - } - }; - } - @Bean public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { return new AtlasAuthenticationProviderBridge() { diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java new file mode 100644 index 0000000000..ced8b63e0e --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java @@ -0,0 +1,58 @@ +/** + * 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.atlas.notification.rest.web.security; + +import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; +import org.apache.atlas.notification.rest.web.service.ServiceState; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AtlasSecurityStateProviderConfig { + @Bean + public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { + return activeInstanceState::getActiveServerAddress; + } + + @Bean + public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { + return new ServiceStateProvider() { + @Override + public boolean isActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + @Override + public boolean isInstanceInTransition() { + return serviceState.isInstanceInTransition(); + } + + @Override + public boolean isInstanceInMigration() { + return serviceState.isInstanceInMigration(); + } + + @Override + public String getStateName() { + return serviceState.getState().toString(); + } + }; + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java deleted file mode 100644 index 4a630c5e2c..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/PamLoginModule.java +++ /dev/null @@ -1,236 +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.atlas.notification.rest.web.security; - -import org.jvnet.libpam.PAM; -import org.jvnet.libpam.PAMException; -import org.jvnet.libpam.UnixUser; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.security.auth.Subject; -import javax.security.auth.callback.Callback; -import javax.security.auth.callback.CallbackHandler; -import javax.security.auth.callback.NameCallback; -import javax.security.auth.callback.PasswordCallback; -import javax.security.auth.callback.UnsupportedCallbackException; -import javax.security.auth.login.FailedLoginException; -import javax.security.auth.login.LoginException; -import javax.security.auth.spi.LoginModule; -import java.io.IOException; -import java.security.Principal; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import org.apache.atlas.server.common.security.PamPrincipal; - -public class PamLoginModule extends Object implements LoginModule { - private static final Logger LOG = LoggerFactory.getLogger(PamLoginModule.class); - - public static final String SERVICE_KEY = "service"; - - private PAM pam; - private Subject subject; - private CallbackHandler callbackHandler; - private Map options; - - private String username; - private String password; - - private boolean authSucceeded; - private PamPrincipal principal; - - public PamLoginModule() - { - super(); - authSucceeded = false; - } - - @Override - public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) - { - this.subject = subject; - this.callbackHandler = callbackHandler; - this.options = new HashMap<>(options); - } - - @Override - public boolean login() throws LoginException - { - initializePam(); - obtainUserAndPassword(); - return performLogin(); - } - - private void initializePam() throws LoginException - { - String service = (String) options.get(SERVICE_KEY); - if (service == null) - { - throw new LoginException("Error: PAM service was not defined"); - } - createPam(service); - } - - private void createPam(String service) throws LoginException - { - try - { - pam = new PAM(service); - } - catch (PAMException ex) - { - LoginException le = new LoginException("Error initializing PAM"); - le.initCause(ex); - throw le; - } - } - - private void obtainUserAndPassword() throws LoginException - { - if (callbackHandler == null) - { - throw new LoginException("Error: no CallbackHandler available to gather authentication information from the user"); - } - - try - { - NameCallback nameCallback = new NameCallback("username"); - PasswordCallback passwordCallback = new PasswordCallback("password", false); - - invokeCallbackHandler(nameCallback, passwordCallback); - - initUserName(nameCallback); - initPassword(passwordCallback); - - if (LOG.isDebugEnabled()) - LOG.debug("Searching for user " + nameCallback.getName()); - } - catch (IOException | UnsupportedCallbackException ex) - { - LoginException le = new LoginException("Error in callbacks"); - le.initCause(ex); - throw le; - } - } - - private void invokeCallbackHandler(NameCallback nameCallback, PasswordCallback passwordCallback) throws IOException, UnsupportedCallbackException - { - Callback[] callbacks = new Callback[2]; - callbacks[0] = nameCallback; - callbacks[1] = passwordCallback; - - callbackHandler.handle(callbacks); - } - - private void initUserName(NameCallback nameCallback) - { - username = nameCallback.getName(); - } - - private void initPassword(PasswordCallback passwordCallback) - { - char[] password = passwordCallback.getPassword(); - if (password != null) { - this.password = new String(password); - } - passwordCallback.clearPassword(); - } - - private boolean performLogin() throws LoginException - { - try - { - UnixUser user = pam.authenticate(username, password); - principal = new PamPrincipal(user); - authSucceeded = true; - - if (LOG.isDebugEnabled()) - LOG.debug("user " + username ); - return true; - } - catch (PAMException ex) - { - LoginException le = new FailedLoginException("Invalid username or password"); - le.initCause(ex); - throw le; - } - } - - @Override - public boolean commit() throws LoginException - { - if (authSucceeded == false) - { - return false; - } - - if (subject.isReadOnly()) - { - cleanup(); - throw new LoginException("Subject is read-only"); - } - - Set principals = subject.getPrincipals(); - if (principals.contains(principal) == false) - { - principals.add(principal); - } - - return true; - } - - @Override - public boolean abort() throws LoginException - { - if (authSucceeded == false) - { - return false; - } - - cleanup(); - return true; - } - - @Override - public boolean logout() throws LoginException - { - if (subject.isReadOnly()) - { - cleanup(); - throw new LoginException("Subject is read-only"); - } - - subject.getPrincipals().remove(principal); - - cleanup(); - return true; - } - - private void cleanup() - { - authSucceeded = false; - username = null; - password = null; - principal = null; - pam.dispose(); - } -} diff --git a/server-common/pom.xml b/server-common/pom.xml index d23727aa44..d9dacf7d43 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -123,6 +123,15 @@ org.springframework spring-web + + org.springframework.ldap + spring-ldap-core + ${spring-ldap-core.version} + + + org.springframework.security + spring-security-config + org.springframework.security spring-security-core @@ -131,6 +140,16 @@ org.springframework.security spring-security-crypto + + org.springframework.security + spring-security-ldap + + + org.springframework.ldap + spring-ldap-core + + + org.springframework.security spring-security-web diff --git a/server-common/src/main/java/org/apache/atlas/server/common/model/User.java b/server-common/src/main/java/org/apache/atlas/server/common/model/User.java new file mode 100644 index 0000000000..be664b62e1 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/model/User.java @@ -0,0 +1,124 @@ +/* + * 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 compliRance 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.atlas.server.common.model; + +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; + +import java.util.Collection; +import java.util.List; + +public class User implements UserDetails { + private static final long serialVersionUID = 1L; + + private String username; + private String password; + private List authorities; + private boolean accountNonExpired = true; + private boolean accountNonLocked = true; + private boolean credentialsNonExpired = true; + private boolean enabled = true; + + public User(String userName2, String userPassword, List grantedAuths) { + this.username = userName2; + this.password = userPassword; + this.authorities = grantedAuths; + } + + public User() { + } + + @Override + public Collection getAuthorities() { + return this.authorities; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public boolean isAccountNonExpired() { + return this.accountNonExpired; + } + + public void setAccountNonExpired(boolean accountNonExpired) { + this.accountNonExpired = accountNonExpired; + } + + @Override + public boolean isAccountNonLocked() { + return this.accountNonLocked; + } + + public void setAccountNonLocked(boolean accountNonLocked) { + this.accountNonLocked = accountNonLocked; + } + + @Override + public boolean isCredentialsNonExpired() { + return this.credentialsNonExpired; + } + + public void setCredentialsNonExpired(boolean credentialsNonExpired) { + this.credentialsNonExpired = credentialsNonExpired; + } + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public void setPassword(String password) { + this.password = password; + } + + public void setAuthorities(List authorities) { + this.authorities = authorities; + } + + @Override + public String toString() { + return "User [username=" + + username + + ", authorities=" + + authorities + + ", accountNonExpired=" + + accountNonExpired + + ", accountNonLocked=" + + accountNonLocked + + ", credentialsNonExpired=" + + credentialsNonExpired + + ", enabled=" + + enabled + + "]"; + } +} diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasADAuthenticationProvider.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasADAuthenticationProvider.java index 8b9020185a..3c57b8dd73 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasADAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasADAuthenticationProvider.java @@ -16,10 +16,10 @@ * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.model.User; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.slf4j.Logger; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java index cc2d1587c1..852369c03a 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.commons.collections.CollectionUtils; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java similarity index 71% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java index 26dcfc9fe8..07a6f217de 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java @@ -6,16 +6,16 @@ * 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. */ -package org.apache.atlas.notification.rest.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; import org.apache.commons.configuration.Configuration; @@ -29,79 +29,47 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; - -@Component +@Component("atlasServerCommonAuthenticationProvider") @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - private static final Logger LOG = LoggerFactory - .getLogger(AtlasAuthenticationProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationProvider.class); - private boolean fileAuthenticationMethodEnabled = true; - private boolean pamAuthenticationEnabled = false; - private String ldapType = "NONE"; public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file"; public static final String LDAP_AUTH_METHOD = "atlas.authentication.method.ldap"; - public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; - public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; - - - - private boolean ssoEnabled = false; + public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; + public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; final AtlasLdapAuthenticationProvider ldapAuthenticationProvider; - final AtlasFileAuthenticationProvider fileAuthenticationProvider; + final AtlasADAuthenticationProvider adAuthenticationProvider; + final AtlasPamAuthenticationProvider pamAuthenticationProvider; - final AtlasADAuthenticationProvider adAuthenticationProvider; - - final AtlasPamAuthenticationProvider pamAuthenticationProvider; + private boolean fileAuthenticationMethodEnabled = true; + private boolean pamAuthenticationEnabled; + private String ldapType = "NONE"; + private boolean ssoEnabled; @Inject public AtlasAuthenticationProvider(AtlasLdapAuthenticationProvider ldapAuthenticationProvider, - AtlasFileAuthenticationProvider fileAuthenticationProvider, - AtlasADAuthenticationProvider adAuthenticationProvider, - AtlasPamAuthenticationProvider pamAuthenticationProvider) { + AtlasFileAuthenticationProvider fileAuthenticationProvider, AtlasADAuthenticationProvider adAuthenticationProvider, + AtlasPamAuthenticationProvider pamAuthenticationProvider) { this.ldapAuthenticationProvider = ldapAuthenticationProvider; this.fileAuthenticationProvider = fileAuthenticationProvider; - this.adAuthenticationProvider = adAuthenticationProvider; - this.pamAuthenticationProvider = pamAuthenticationProvider; - } - - @PostConstruct - void setAuthenticationMethod() { - try { - Configuration configuration = ApplicationProperties.get(); - - this.fileAuthenticationMethodEnabled = configuration.getBoolean(FILE_AUTH_METHOD, true); - - this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false); - - boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false); - - if (ldapAuthenticationEnabled) { - this.ldapType = configuration.getString(LDAP_TYPE, "NONE"); - } else { - this.ldapType = "NONE"; - } - } catch (Exception e) { - LOG.error("Error while getting atlas.login.method application properties", e); - } + this.adAuthenticationProvider = adAuthenticationProvider; + this.pamAuthenticationProvider = pamAuthenticationProvider; } @Override - public Authentication authenticate(Authentication authentication) - throws AuthenticationException { - - if(ssoEnabled){ - if (authentication != null){ + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + if (ssoEnabled) { + if (authentication != null) { authentication = getSSOAuthentication(authentication); - if(authentication!=null && authentication.isAuthenticated()){ + + if (authentication != null && authentication.isAuthenticated()) { return authentication; } } } else { - if (ldapType.equalsIgnoreCase("LDAP")) { try { authentication = ldapAuthenticationProvider.authenticate(authentication); @@ -136,9 +104,23 @@ public Authentication authenticate(Authentication authentication) } LOG.error("Authentication failed."); + throw new AtlasAuthenticationException("Authentication failed."); } + @Override + public boolean supports(Class authentication) { + if (pamAuthenticationEnabled) { + return pamAuthenticationProvider.supports(authentication); + } else if (ldapType.equalsIgnoreCase("LDAP")) { + return ldapAuthenticationProvider.supports(authentication); + } else if (ldapType.equalsIgnoreCase("AD")) { + return adAuthenticationProvider.supports(authentication); + } else { + return super.supports(authentication); + } + } + public boolean isSsoEnabled() { return ssoEnabled; } @@ -147,7 +129,27 @@ public void setSsoEnabled(boolean ssoEnabled) { this.ssoEnabled = ssoEnabled; } - private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException{ + @PostConstruct + void setAuthenticationMethod() { + try { + Configuration configuration = ApplicationProperties.get(); + + this.fileAuthenticationMethodEnabled = configuration.getBoolean(FILE_AUTH_METHOD, true); + this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false); + + boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false); + + if (ldapAuthenticationEnabled) { + this.ldapType = configuration.getString(LDAP_TYPE, "NONE"); + } else { + this.ldapType = "NONE"; + } + } catch (Exception e) { + LOG.error("Error while getting atlas.login.method application properties", e); + } + } + + private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException { return authentication; } } diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasFileAuthenticationProvider.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasFileAuthenticationProvider.java index 4b0b4d70d1..bda282009a 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasFileAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasFileAuthenticationProvider.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.server.common.dao.UserDao; import org.slf4j.Logger; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasLdapAuthenticationProvider.java similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasLdapAuthenticationProvider.java index ba26c838ee..057f764fed 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasLdapAuthenticationProvider.java @@ -16,10 +16,10 @@ * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.model.User; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasPamAuthenticationProvider.java similarity index 97% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasPamAuthenticationProvider.java index 4214e52a55..fac38d30cc 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasPamAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasPamAuthenticationProvider.java @@ -17,10 +17,10 @@ * under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.model.User; import org.apache.commons.configuration.ConfigurationConverter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +49,7 @@ public class AtlasPamAuthenticationProvider extends AtlasAbstractAuthenticationProvider { private static final Logger LOG = LoggerFactory.getLogger(AtlasPamAuthenticationProvider.class); - private static final String loginModuleName = "org.apache.atlas.web.security.PamLoginModule"; + private static final String loginModuleName = "org.apache.atlas.server.common.security.PamLoginModule"; private static final AppConfigurationEntry.LoginModuleControlFlag controlFlag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; private final Map options = new HashMap<>(); diff --git a/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java b/server-common/src/main/java/org/apache/atlas/server/common/security/PamLoginModule.java similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/PamLoginModule.java index 1878777b7b..f4b8640eff 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/PamLoginModule.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/PamLoginModule.java @@ -17,7 +17,7 @@ * under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.jvnet.libpam.PAM; import org.jvnet.libpam.PAMException; diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java index b5c497636e..a58bf71437 100644 --- a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java +++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java @@ -120,7 +120,7 @@ import static org.apache.atlas.model.instance.AtlasObjectId.KEY_TYPENAME; import static org.apache.atlas.model.instance.AtlasObjectId.KEY_UNIQUE_ATTRIBUTES; import static org.apache.atlas.notification.preprocessor.EntityPreprocessor.TYPE_HIVE_PROCESS; -import static org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI; +import static org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI; /** * Consumer of notifications from hooks e.g., hive hook etc. diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java index 40c31a03e0..1dbd206239 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java @@ -29,7 +29,12 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; @Component @Scope("prototype") diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java index 9839a21951..d639565e98 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java @@ -30,7 +30,7 @@ import java.util.Map; @Component -public class AtlasKeycloakAuthenticationProvider extends AtlasAbstractAuthenticationProvider { +public class AtlasKeycloakAuthenticationProvider extends org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider { private final boolean groupsFromUGI; private final String groupsClaim; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 0b77b933d6..0a4036a737 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -28,8 +28,6 @@ import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; -import org.apache.atlas.web.service.ActiveInstanceState; -import org.apache.atlas.web.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.keycloak.adapters.AdapterDeploymentContext; @@ -330,36 +328,6 @@ protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessin return filter; } - @Bean - public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { - return activeInstanceState::getActiveServerAddress; - } - - @Bean - public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { - return new ServiceStateProvider() { - @Override - public boolean isActive() { - return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; - } - - @Override - public boolean isInstanceInTransition() { - return serviceState.isInstanceInTransition(); - } - - @Override - public boolean isInstanceInMigration() { - return serviceState.isInstanceInMigration(); - } - - @Override - public String getStateName() { - return serviceState.getState().toString(); - } - }; - } - @Bean public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { return new AtlasAuthenticationProviderBridge() { diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java new file mode 100644 index 0000000000..cfc22e8d78 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java @@ -0,0 +1,58 @@ +/** + * 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.atlas.web.security; + +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; +import org.apache.atlas.web.service.ActiveInstanceState; +import org.apache.atlas.web.service.ServiceState; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AtlasSecurityStateProviderConfig { + @Bean + public ActiveInstanceStateProvider activeInstanceStateProvider(ActiveInstanceState activeInstanceState) { + return activeInstanceState::getActiveServerAddress; + } + + @Bean + public ServiceStateProvider serviceStateProvider(ServiceState serviceState) { + return new ServiceStateProvider() { + @Override + public boolean isActive() { + return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + } + + @Override + public boolean isInstanceInTransition() { + return serviceState.isInstanceInTransition(); + } + + @Override + public boolean isInstanceInMigration() { + return serviceState.isInstanceInMigration(); + } + + @Override + public String getStateName() { + return serviceState.getState().toString(); + } + }; + } +} diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java index 28693f767c..9da891f1d1 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java @@ -1628,9 +1628,9 @@ public void testGetAuthenticationForUser() throws Exception { Method getAuthMethod = NotificationHookConsumer.class.getDeclaredMethod("getAuthenticationForUser", String.class); getAuthMethod.setAccessible(true); - try (MockedStatic authProvider = - mockStatic(org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.class)) { - authProvider.when(() -> org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) + try (MockedStatic authProvider = + mockStatic(org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.class)) { + authProvider.when(() -> org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) .thenReturn(new ArrayList<>()); Object auth = getAuthMethod.invoke(consumer, "testUser"); @@ -1652,11 +1652,11 @@ public void testSetCurrentUser() throws Exception { Method setCurrentUserMethod = NotificationHookConsumer.class.getDeclaredMethod("setCurrentUser", String.class); setCurrentUserMethod.setAccessible(true); - try (MockedStatic authProvider = - mockStatic(org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.class); + try (MockedStatic authProvider = + mockStatic(org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.class); MockedStatic securityContext = mockStatic(org.springframework.security.core.context.SecurityContextHolder.class)) { - authProvider.when(() -> org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) + authProvider.when(() -> org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) .thenReturn(new ArrayList<>()); org.springframework.security.core.context.SecurityContext mockContext = @@ -2433,11 +2433,11 @@ public void testSetCurrentUserWithAuthorizationEnabled() throws Exception { Method setCurrentUserMethod = NotificationHookConsumer.class.getDeclaredMethod("setCurrentUser", String.class); setCurrentUserMethod.setAccessible(true); - try (MockedStatic authProvider = - mockStatic(org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.class); + try (MockedStatic authProvider = + mockStatic(org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.class); MockedStatic securityContext = mockStatic(org.springframework.security.core.context.SecurityContextHolder.class)) { - authProvider.when(() -> org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) + authProvider.when(() -> org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("testUser")) .thenReturn(new ArrayList<>()); org.springframework.security.core.context.SecurityContext mockContext = @@ -2460,9 +2460,9 @@ public void testGetAuthenticationForUserWithCache() throws Exception { Method getAuthMethod = NotificationHookConsumer.class.getDeclaredMethod("getAuthenticationForUser", String.class); getAuthMethod.setAccessible(true); - try (MockedStatic authProvider = - mockStatic(org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.class)) { - authProvider.when(() -> org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("cachedUser")) + try (MockedStatic authProvider = + mockStatic(org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.class)) { + authProvider.when(() -> org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("cachedUser")) .thenReturn(new ArrayList<>()); // First call - should cache @@ -2474,7 +2474,7 @@ public void testGetAuthenticationForUserWithCache() throws Exception { assertNotNull(auth2); // Should only call the static method once due to caching - authProvider.verify(times(1), () -> org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("cachedUser")); + authProvider.verify(times(1), () -> org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider.getAuthoritiesFromUGI("cachedUser")); } } diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java index 85d2b97ad8..16e5735472 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java @@ -24,7 +24,7 @@ import org.apache.atlas.AtlasException; import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; -import org.apache.atlas.web.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java index bb96de8662..d3db15024a 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java @@ -51,6 +51,8 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.server.common.security.AtlasAuthenticationException; public class AtlasADAuthenticationProviderTest { diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProviderTest.java index 1a9527551f..d2b2f7efaa 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAbstractAuthenticationProviderTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.hadoop.security.UserGroupInformation; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java index fc20693005..e32cc26cf1 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java @@ -49,7 +49,11 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; public class AtlasAuthenticationProviderTest { @Mock diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java index 97fceba1f5..bee51b85a9 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.ApplicationProperties; import org.apache.commons.configuration.Configuration; import org.keycloak.KeycloakSecurityContext; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProviderTest.java index 18e962ba04..08e2f50458 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasLdapAuthenticationProviderTest.java @@ -20,7 +20,9 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.model.User; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java index 0ed0e2a05b..4ec6c2f6cd 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java @@ -20,7 +20,9 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.web.model.User; +import org.apache.atlas.server.common.model.User; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.mockito.Mock; @@ -507,7 +509,7 @@ public void testStaticFields() throws Exception { // Verify static final fields Field loginModuleNameField = AtlasPamAuthenticationProvider.class.getDeclaredField("loginModuleName"); loginModuleNameField.setAccessible(true); - assertEquals(loginModuleNameField.get(null), "org.apache.atlas.web.security.PamLoginModule"); + assertEquals(loginModuleNameField.get(null), "org.apache.atlas.server.common.security.PamLoginModule"); Field controlFlagField = AtlasPamAuthenticationProvider.class.getDeclaredField("controlFlag"); controlFlagField.setAccessible(true); diff --git a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java index 5fa75ce664..4600f17a92 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.PamLoginModule; import org.jvnet.libpam.PAM; import org.jvnet.libpam.UnixUser; import org.mockito.Mock; diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index b7988b329e..425b9af64c 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -56,12 +56,12 @@ - - + + - - + + From 5dbbd0cbe363346490c760755cf7815f00d71543 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 23 Apr 2026 15:03:08 +0530 Subject: [PATCH 10/22] Updating class syntax to match earlier coding style. --- .../security/AtlasAuthenticationProvider.java | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java index 07a6f217de..3878ad26f8 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java @@ -32,44 +32,74 @@ @Component("atlasServerCommonAuthenticationProvider") @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationProvider.class); + private static final Logger LOG = LoggerFactory + .getLogger(AtlasAuthenticationProvider.class); + private boolean fileAuthenticationMethodEnabled = true; + private boolean pamAuthenticationEnabled = false; + private String ldapType = "NONE"; public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file"; public static final String LDAP_AUTH_METHOD = "atlas.authentication.method.ldap"; - public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; - public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; + public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; + public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; + + + + private boolean ssoEnabled; final AtlasLdapAuthenticationProvider ldapAuthenticationProvider; + final AtlasFileAuthenticationProvider fileAuthenticationProvider; - final AtlasADAuthenticationProvider adAuthenticationProvider; - final AtlasPamAuthenticationProvider pamAuthenticationProvider; - private boolean fileAuthenticationMethodEnabled = true; - private boolean pamAuthenticationEnabled; - private String ldapType = "NONE"; - private boolean ssoEnabled; + final AtlasADAuthenticationProvider adAuthenticationProvider; + + final AtlasPamAuthenticationProvider pamAuthenticationProvider; @Inject public AtlasAuthenticationProvider(AtlasLdapAuthenticationProvider ldapAuthenticationProvider, - AtlasFileAuthenticationProvider fileAuthenticationProvider, AtlasADAuthenticationProvider adAuthenticationProvider, - AtlasPamAuthenticationProvider pamAuthenticationProvider) { + AtlasFileAuthenticationProvider fileAuthenticationProvider, + AtlasADAuthenticationProvider adAuthenticationProvider, + AtlasPamAuthenticationProvider pamAuthenticationProvider) { this.ldapAuthenticationProvider = ldapAuthenticationProvider; this.fileAuthenticationProvider = fileAuthenticationProvider; - this.adAuthenticationProvider = adAuthenticationProvider; - this.pamAuthenticationProvider = pamAuthenticationProvider; + this.adAuthenticationProvider = adAuthenticationProvider; + this.pamAuthenticationProvider = pamAuthenticationProvider; + } + + @PostConstruct + void setAuthenticationMethod() { + try { + Configuration configuration = ApplicationProperties.get(); + + this.fileAuthenticationMethodEnabled = configuration.getBoolean(FILE_AUTH_METHOD, true); + + this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false); + + boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false); + + if (ldapAuthenticationEnabled) { + this.ldapType = configuration.getString(LDAP_TYPE, "NONE"); + } else { + this.ldapType = "NONE"; + } + } catch (Exception e) { + LOG.error("Error while getting atlas.login.method application properties", e); + } } @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (ssoEnabled) { - if (authentication != null) { - authentication = getSSOAuthentication(authentication); + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { - if (authentication != null && authentication.isAuthenticated()) { + if(ssoEnabled){ + if (authentication != null){ + authentication = getSSOAuthentication(authentication); + if(authentication!=null && authentication.isAuthenticated()){ return authentication; } } } else { + if (ldapType.equalsIgnoreCase("LDAP")) { try { authentication = ldapAuthenticationProvider.authenticate(authentication); @@ -104,7 +134,6 @@ public Authentication authenticate(Authentication authentication) throws Authent } LOG.error("Authentication failed."); - throw new AtlasAuthenticationException("Authentication failed."); } @@ -129,27 +158,7 @@ public void setSsoEnabled(boolean ssoEnabled) { this.ssoEnabled = ssoEnabled; } - @PostConstruct - void setAuthenticationMethod() { - try { - Configuration configuration = ApplicationProperties.get(); - - this.fileAuthenticationMethodEnabled = configuration.getBoolean(FILE_AUTH_METHOD, true); - this.pamAuthenticationEnabled = configuration.getBoolean(PAM_AUTH_METHOD, false); - - boolean ldapAuthenticationEnabled = configuration.getBoolean(LDAP_AUTH_METHOD, false); - - if (ldapAuthenticationEnabled) { - this.ldapType = configuration.getString(LDAP_TYPE, "NONE"); - } else { - this.ldapType = "NONE"; - } - } catch (Exception e) { - LOG.error("Error while getting atlas.login.method application properties", e); - } - } - - private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException { + private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException{ return authentication; } } From 7fcc3f11316887d6112c8b29d11d500592a6c3a9 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 23 Apr 2026 16:51:32 +0530 Subject: [PATCH 11/22] Updating listeners class and package to use server-common module for both webapp and rest-notification webapp. --- .../rest/web/listeners/LoginProcessor.java | 158 ------------------ .../rest/web/setup/KerberosAwareListener.java | 2 +- .../common}/listeners/LoginProcessor.java | 2 +- .../web/setup/KerberosAwareListener.java | 2 +- .../atlas/web/listeners/LoginProcessorIT.java | 1 + 5 files changed, 4 insertions(+), 161 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/listeners/LoginProcessor.java (99%) diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java deleted file mode 100644 index 7b131f273b..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/listeners/LoginProcessor.java +++ /dev/null @@ -1,158 +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.atlas.notification.rest.web.listeners; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.atlas.security.SecurityProperties; -import org.apache.commons.configuration.ConfigurationException; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.security.SecurityUtil; -import org.apache.hadoop.security.UserGroupInformation; -import org.apache.hadoop.util.Shell; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -/** - * A class capable of performing a simple or kerberos login. - */ -public class LoginProcessor { - - private static final Logger LOG = LoggerFactory.getLogger(LoginProcessor.class); - public static final String ATLAS_AUTHENTICATION_PREFIX = "atlas.authentication."; - public static final String AUTHENTICATION_KERBEROS_METHOD = ATLAS_AUTHENTICATION_PREFIX + "method.kerberos"; - public static final String AUTHENTICATION_PRINCIPAL = ATLAS_AUTHENTICATION_PREFIX + "principal"; - public static final String AUTHENTICATION_KEYTAB = ATLAS_AUTHENTICATION_PREFIX + "keytab"; - - /** - * Perform a SIMPLE login based on established OS identity or a kerberos based login using the configured - * principal and keytab (via atlas-application.properties). - */ - public void login() { - // first, let's see if we're running in a hadoop cluster and have the env configured - boolean isHadoopCluster = isHadoopCluster(); - Configuration hadoopConfig = isHadoopCluster ? getHadoopConfiguration() : new Configuration(false); - org.apache.commons.configuration.Configuration configuration = getApplicationConfiguration(); - if (!isHadoopCluster) { - // need to read the configured authentication choice and create the UGI configuration - setupHadoopConfiguration(hadoopConfig, configuration); - } - doServiceLogin(hadoopConfig, configuration); - } - - protected void doServiceLogin(Configuration hadoopConfig, - org.apache.commons.configuration.Configuration configuration) { - UserGroupInformation.setConfiguration(hadoopConfig); - - UserGroupInformation ugi = null; - UserGroupInformation.AuthenticationMethod authenticationMethod = - SecurityUtil.getAuthenticationMethod(hadoopConfig); - try { - if (authenticationMethod == UserGroupInformation.AuthenticationMethod.SIMPLE) { - UserGroupInformation.loginUserFromSubject(null); - } else if (authenticationMethod == UserGroupInformation.AuthenticationMethod.KERBEROS) { - String bindAddress = getHostname(configuration); - UserGroupInformation.loginUserFromKeytab( - getServerPrincipal(configuration.getString(AUTHENTICATION_PRINCIPAL), bindAddress), - configuration.getString(AUTHENTICATION_KEYTAB)); - } - LOG.info("Logged in user {}", UserGroupInformation.getLoginUser()); - } catch (IOException e) { - throw new IllegalStateException(String.format("Unable to perform %s login.", authenticationMethod), e); - } - } - - private String getHostname(org.apache.commons.configuration.Configuration configuration) { - String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS); - if (bindAddress == null) { - LOG.info("No host name configured. Defaulting to local host name."); - try { - bindAddress = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException e) { - throw new IllegalStateException(e); - } - } - return bindAddress; - } - - protected void setupHadoopConfiguration(Configuration hadoopConfig, org.apache.commons.configuration.Configuration - configuration) { - String authMethod = ""; - String kerberosAuthNEnabled = configuration != null ? configuration.getString(AUTHENTICATION_KERBEROS_METHOD) : null; - // getString may return null, and would like to log the nature of the default setting - if (kerberosAuthNEnabled == null || kerberosAuthNEnabled.equalsIgnoreCase("false")) { - LOG.info("No authentication method configured. Defaulting to simple authentication"); - authMethod = "simple"; - } else if (kerberosAuthNEnabled.equalsIgnoreCase("true")) { - authMethod = "kerberos"; - } - SecurityUtil - .setAuthenticationMethod(UserGroupInformation.AuthenticationMethod.valueOf(authMethod.toUpperCase()), - hadoopConfig); - } - - /** - * Return a server (service) principal. The token "_HOST" in the principal will be replaced with the local host - * name (e.g. dgi/_HOST will be changed to dgi/localHostName) - * @param principal the input principal containing an option "_HOST" token - * @return the service principal. - * @throws IOException - */ - private String getServerPrincipal(String principal, String host) throws IOException { - return SecurityUtil.getServerPrincipal(principal, host); - } - - /** - * Returns a Hadoop configuration instance. - * @return the configuration. - */ - protected Configuration getHadoopConfiguration() { - return new Configuration(); - } - - /** - * Returns the metadata application configuration. - * @return the metadata configuration. - * @throws ConfigurationException - */ - protected org.apache.commons.configuration.Configuration getApplicationConfiguration() { - try { - return ApplicationProperties.get(); - } catch (AtlasException e) { - LOG.warn("Error reading application configuration", e); - } - return null; - } - - /** - * Uses a hadoop shell to discern whether a hadoop cluster is available/configured. - * @return true if a hadoop cluster is detected. - */ - protected boolean isHadoopCluster() { - boolean isHadoopCluster = false; - try { - isHadoopCluster = Shell.getHadoopHome() != null; - } catch (IOException e) { - // ignore - false is default setting - } - return isHadoopCluster; - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java index f9d4e10589..3a01dd8448 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java @@ -18,7 +18,7 @@ package org.apache.atlas.notification.rest.web.setup; -import org.apache.atlas.notification.rest.web.listeners.LoginProcessor; +import org.apache.atlas.server.common.listeners.LoginProcessor; import org.springframework.web.context.ContextLoaderListener; import javax.servlet.ServletContextEvent; diff --git a/webapp/src/main/java/org/apache/atlas/web/listeners/LoginProcessor.java b/server-common/src/main/java/org/apache/atlas/server/common/listeners/LoginProcessor.java similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/listeners/LoginProcessor.java rename to server-common/src/main/java/org/apache/atlas/server/common/listeners/LoginProcessor.java index e8a198e339..066da4329f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/listeners/LoginProcessor.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/listeners/LoginProcessor.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.listeners; +package org.apache.atlas.server.common.listeners; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; diff --git a/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java b/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java index 5fb9848855..fe4b9f80ee 100644 --- a/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java +++ b/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java @@ -17,7 +17,7 @@ */ package org.apache.atlas.web.setup; -import org.apache.atlas.web.listeners.LoginProcessor; +import org.apache.atlas.server.common.listeners.LoginProcessor; import org.springframework.web.context.ContextLoaderListener; import javax.servlet.ServletContextEvent; diff --git a/webapp/src/test/java/org/apache/atlas/web/listeners/LoginProcessorIT.java b/webapp/src/test/java/org/apache/atlas/web/listeners/LoginProcessorIT.java index 37a64628e6..9129a42861 100644 --- a/webapp/src/test/java/org/apache/atlas/web/listeners/LoginProcessorIT.java +++ b/webapp/src/test/java/org/apache/atlas/web/listeners/LoginProcessorIT.java @@ -16,6 +16,7 @@ */ package org.apache.atlas.web.listeners; +import org.apache.atlas.server.common.listeners.LoginProcessor; import org.apache.atlas.web.security.BaseSecurityTest; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.hadoop.conf.Configuration; From 3762146ff81d5315ec22592106a7a10ec4039130 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 23 Apr 2026 18:10:27 +0530 Subject: [PATCH 12/22] Updating classes to use User model class from server-common module. --- .../notification/rest/web/model/User.java | 129 ------------------ .../atlas/server/common/dao/UserDao.java | 2 +- .../filters/AtlasAuthenticationFilter.java | 2 +- .../AtlasKnoxSSOAuthenticationFilter.java | 2 +- .../AtlasAbstractAuthenticationProvider.java | 2 +- .../java/org/apache/atlas/web/model/User.java | 124 ----------------- .../org/apache/atlas/web/model/UserTest.java | 1 + 7 files changed, 5 insertions(+), 257 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/model/User.java diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java deleted file mode 100644 index 8c6336fd2e..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/model/User.java +++ /dev/null @@ -1,129 +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 compliRance 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.atlas.notification.rest.web.model; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; -import java.util.List; - -public class User implements UserDetails { - private static final long serialVersionUID = 1L; - - private String username; - private String password; - private List authorities; - private boolean accountNonExpired = true; - private boolean accountNonLocked = true; - private boolean credentialsNonExpired = true; - private boolean enabled = true; - - public User(String userName2, String userPassword, - List grantedAuths) { - this.username = userName2; - this.password = userPassword; - this.authorities = grantedAuths; - - } - - public User() { - } - - @Override - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - @Override - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public Collection getAuthorities() { - return this.authorities; - } - - public void setAuthorities(List authorities) { - this.authorities = authorities; - } - - @Override - public boolean isAccountNonExpired() { - return this.accountNonExpired; - } - - public void setAccountNonExpired(boolean accountNonExpired) { - this.accountNonExpired = accountNonExpired; - } - - @Override - public boolean isAccountNonLocked() { - return this.accountNonLocked; - } - - public void setAccountNonLocked(boolean accountNonLocked) { - this.accountNonLocked = accountNonLocked; - } - - @Override - public boolean isCredentialsNonExpired() { - return this.credentialsNonExpired; - } - - public void setCredentialsNonExpired(boolean credentialsNonExpired) { - this.credentialsNonExpired = credentialsNonExpired; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("User [username="); - builder.append(username); - builder.append(", authorities="); - builder.append(authorities); - builder.append(", accountNonExpired="); - builder.append(accountNonExpired); - builder.append(", accountNonLocked="); - builder.append(accountNonLocked); - builder.append(", credentialsNonExpired="); - builder.append(credentialsNonExpired); - builder.append(", enabled="); - builder.append(enabled); - builder.append("]"); - return builder.toString(); - } - -} diff --git a/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java b/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java index eeb12f2f4f..efccdea165 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/dao/UserDao.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; +import org.apache.atlas.server.common.model.User; import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; @@ -27,7 +28,6 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCrypt; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java index 33397bdb98..56fb1e4ca3 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationFilter.java @@ -23,6 +23,7 @@ import org.apache.atlas.security.SecurityProperties; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.apache.atlas.server.common.model.User; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.iterators.IteratorEnumeration; import org.apache.commons.configuration.Configuration; @@ -49,7 +50,6 @@ 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 org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java index 6d299890c8..cbd2e08b2f 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasKnoxSSOAuthenticationFilter.java @@ -27,6 +27,7 @@ import com.nimbusds.jwt.SignedJWT; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.apache.atlas.server.common.model.User; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.http.client.utils.URIBuilder; @@ -38,7 +39,6 @@ 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; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java index 852369c03a..7c3014d4e4 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java @@ -19,6 +19,7 @@ package org.apache.atlas.server.common.security; +import org.apache.atlas.server.common.model.User; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.hadoop.conf.Configuration; @@ -31,7 +32,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import java.util.ArrayList; diff --git a/webapp/src/main/java/org/apache/atlas/web/model/User.java b/webapp/src/main/java/org/apache/atlas/web/model/User.java deleted file mode 100644 index c7bbdceb00..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/model/User.java +++ /dev/null @@ -1,124 +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 compliRance 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.atlas.web.model; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import java.util.Collection; -import java.util.List; - -public class User implements UserDetails { - private static final long serialVersionUID = 1L; - - private String username; - private String password; - private List authorities; - private boolean accountNonExpired = true; - private boolean accountNonLocked = true; - private boolean credentialsNonExpired = true; - private boolean enabled = true; - - public User(String userName2, String userPassword, List grantedAuths) { - this.username = userName2; - this.password = userPassword; - this.authorities = grantedAuths; - } - - public User() { - } - - @Override - public Collection getAuthorities() { - return this.authorities; - } - - @Override - public String getPassword() { - return password; - } - - @Override - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - @Override - public boolean isAccountNonExpired() { - return this.accountNonExpired; - } - - public void setAccountNonExpired(boolean accountNonExpired) { - this.accountNonExpired = accountNonExpired; - } - - @Override - public boolean isAccountNonLocked() { - return this.accountNonLocked; - } - - public void setAccountNonLocked(boolean accountNonLocked) { - this.accountNonLocked = accountNonLocked; - } - - @Override - public boolean isCredentialsNonExpired() { - return this.credentialsNonExpired; - } - - public void setCredentialsNonExpired(boolean credentialsNonExpired) { - this.credentialsNonExpired = credentialsNonExpired; - } - - @Override - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public void setPassword(String password) { - this.password = password; - } - - public void setAuthorities(List authorities) { - this.authorities = authorities; - } - - @Override - public String toString() { - return "User [username=" + - username + - ", authorities=" + - authorities + - ", accountNonExpired=" + - accountNonExpired + - ", accountNonLocked=" + - accountNonLocked + - ", credentialsNonExpired=" + - credentialsNonExpired + - ", enabled=" + - enabled + - "]"; - } -} diff --git a/webapp/src/test/java/org/apache/atlas/web/model/UserTest.java b/webapp/src/test/java/org/apache/atlas/web/model/UserTest.java index 2f6b4a87d8..2908857341 100644 --- a/webapp/src/test/java/org/apache/atlas/web/model/UserTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/model/UserTest.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.model; +import org.apache.atlas.server.common.model.User; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.testng.annotations.BeforeClass; From 921594b5d34b9fd0b35fd1ddda32557115f638ac Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Sat, 25 Apr 2026 23:00:42 +0530 Subject: [PATCH 13/22] Updating service classes and package to use server-common module for both webapp and rest-notification webapp. --- .../rest/RestNotificationMain.java | 2 +- ...stNotificationHighAvailabilitySupport.java | 61 +++ .../rest/web/resources/AdminResource.java | 2 +- .../AtlasSecurityStateProviderConfig.java | 4 +- .../service/ActiveInstanceElectorService.java | 203 ---------- .../rest/web/service/ActiveInstanceState.java | 146 -------- .../AtlasZookeeperSecurityProperties.java | 74 ---- .../rest/web/service/CuratorFactory.java | 202 ---------- .../rest/web/service/EmbeddedServer.java | 113 ------ .../web/service/SecureEmbeddedServer.java | 351 ------------------ .../rest/web/service/UserService.java | 43 --- server-common/pom.xml | 24 ++ .../service/ActiveInstanceElectorService.java | 88 ++--- .../common}/service/ActiveInstanceState.java | 70 +--- .../AtlasZookeeperSecurityProperties.java | 2 +- .../common}/service/CuratorFactory.java | 108 +++--- .../common}/service/EmbeddedServer.java | 42 +-- .../service/HighAvailabilityProperties.java | 79 ++++ .../service/HighAvailabilitySupport.java | 88 +++++ .../common}/service/SecureEmbeddedServer.java | 2 +- .../common/service/ServiceMetricsHook.java | 30 ++ .../server/common}/service/ServiceState.java | 91 +++-- .../server/common}/service/UserService.java | 2 +- .../src/main/java/org/apache/atlas/Atlas.java | 2 +- .../NotificationHookConsumer.java | 2 +- .../web/ha/WebappHighAvailabilitySupport.java | 61 +++ .../web/metrics/WebappServiceMetricsHook.java | 47 +++ .../atlas/web/resources/AdminResource.java | 2 +- .../AtlasSecurityStateProviderConfig.java | 4 +- .../atlas/web/service/ServiceState.java | 149 -------- .../apache/atlas/web/setup/SetupSteps.java | 4 +- .../NotificationHookConsumerKafkaTest.java | 2 +- .../NotificationHookConsumerTest.java | 2 +- .../web/filters/ActiveServerFilterTest.java | 4 +- ...AtlasAuthenticationKerberosFilterTest.java | 2 +- .../web/resources/AdminResourceTest.java | 2 +- .../web/security/BaseSSLAndKerberosTest.java | 2 +- .../apache/atlas/web/security/SSLTest.java | 2 +- .../ActiveInstanceElectorServiceTest.java | 270 ++++++-------- .../web/service/ActiveInstanceStateTest.java | 13 +- .../AtlasZookeeperSecurityPropertiesTest.java | 1 + .../atlas/web/service/CuratorFactoryTest.java | 326 +++------------- .../atlas/web/service/EmbeddedServerTest.java | 92 +---- .../web/service/SecureEmbeddedServerTest.java | 2 + .../service/SecureEmbeddedServerTestBase.java | 2 + .../atlas/web/service/ServiceStateTest.java | 161 +++----- .../atlas/web/service/UserServiceTest.java | 1 + .../atlas/web/setup/SetupStepsTest.java | 2 +- .../test/resources/test-spring-security.xml | 2 +- 49 files changed, 802 insertions(+), 2184 deletions(-) create mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilitySupport.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java delete mode 100755 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/ActiveInstanceElectorService.java (75%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/ActiveInstanceState.java (59%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/AtlasZookeeperSecurityProperties.java (98%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/CuratorFactory.java (74%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/EmbeddedServer.java (77%) mode change 100755 => 100644 create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilityProperties.java create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/SecureEmbeddedServer.java (99%) mode change 100755 => 100644 create mode 100644 server-common/src/main/java/org/apache/atlas/server/common/service/ServiceMetricsHook.java rename {rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web => server-common/src/main/java/org/apache/atlas/server/common}/service/ServiceState.java (62%) rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/service/UserService.java (96%) create mode 100644 webapp/src/main/java/org/apache/atlas/web/ha/WebappHighAvailabilitySupport.java create mode 100644 webapp/src/main/java/org/apache/atlas/web/metrics/WebappServiceMetricsHook.java delete mode 100644 webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java index 3f1711c8b5..af279de5cf 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/RestNotificationMain.java @@ -20,7 +20,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConstants; -import org.apache.atlas.notification.rest.web.service.EmbeddedServer; +import org.apache.atlas.server.common.service.EmbeddedServer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.DefaultParser; import org.apache.commons.cli.Option; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilitySupport.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilitySupport.java new file mode 100644 index 0000000000..e6116c4120 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilitySupport.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.atlas.notification.rest.ha; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.notification.rest.AtlasServerIdSelector; +import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; +import org.apache.commons.configuration.Configuration; +import org.springframework.stereotype.Component; + +/** + * Rest notification implementation of {@link HighAvailabilitySupport} using {@link RestHAConfiguration}. + */ +@Component +public class RestNotificationHighAvailabilitySupport implements HighAvailabilitySupport { + @Override + public boolean isHAEnabled(Configuration configuration) { + return RestHAConfiguration.isHAEnabled(configuration); + } + + @Override + public String selectServerId(Configuration configuration) throws AtlasException { + return AtlasServerIdSelector.selectServerId(configuration); + } + + @Override + public String getBoundAddressForId(Configuration configuration, String serverId) { + return RestHAConfiguration.getBoundAddressForId(configuration, serverId); + } + + @Override + public HighAvailabilityProperties getZookeeperProperties(Configuration configuration) { + RestHAConfiguration.ZookeeperProperties props = RestHAConfiguration.getZookeeperProperties(configuration); + + return new HighAvailabilityProperties( + props.getConnectString(), + props.getZkRoot(), + props.getRetriesSleepTimeMillis(), + props.getNumRetries(), + props.getSessionTimeout(), + props.getAcl(), + props.getAuth()); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java index 0a58c550b6..33d1fe3545 100755 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/resources/AdminResource.java @@ -21,7 +21,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasClient; import org.apache.atlas.server.common.util.Servlets; -import org.apache.atlas.notification.rest.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.utils.AtlasJson; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java index ced8b63e0e..a969617f1e 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityStateProviderConfig.java @@ -17,8 +17,8 @@ */ package org.apache.atlas.notification.rest.web.security; -import org.apache.atlas.notification.rest.web.service.ActiveInstanceState; -import org.apache.atlas.notification.rest.web.service.ServiceState; +import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.springframework.context.annotation.Bean; diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java deleted file mode 100644 index d00ae8f52e..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceElectorService.java +++ /dev/null @@ -1,203 +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.atlas.notification.rest.web.service; - -import org.apache.atlas.AtlasException; -import org.apache.atlas.listener.ActiveStateChangeHandler; -import org.apache.atlas.notification.rest.AtlasServerIdSelector; -import org.apache.atlas.notification.rest.RestHAConfiguration; -import org.apache.atlas.service.Service; -import org.apache.commons.configuration.Configuration; -import org.apache.curator.framework.recipes.leader.LeaderLatch; -import org.apache.curator.framework.recipes.leader.LeaderLatchListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Set; - - -/** - * A service that implements leader election to determine whether this Atlas server is Active. - * - * The service implements leader election through Curator's - * {@link LeaderLatch} recipe. The service also implements {@link LeaderLatchListener} to get - * notified of changes to leadership state. Upon becoming leader, this instance is treated as the - * active Atlas instance and calls {@link ActiveStateChangeHandler}s to activate them. Conversely, - * on being removed from leadership, this instance is treated as a passive instance and calls - * {@link ActiveStateChangeHandler}s to deactivate them. - */ - -@Component -// -// This should be called the last, leaving it without the @Order(Integer.MAX_VALUE) will make it get -// called after all services have their start called. -public class ActiveInstanceElectorService implements Service, LeaderLatchListener { - private static final Logger LOG = LoggerFactory.getLogger(ActiveInstanceElectorService.class); - - private final Configuration configuration; - private final ServiceState serviceState; - private final ActiveInstanceState activeInstanceState; - private Set activeStateChangeHandlerProviders; - private List activeStateChangeHandlers; - private CuratorFactory curatorFactory; - private LeaderLatch leaderLatch; - private String serverId; - - /** - * Create a new instance of {@link ActiveInstanceElectorService} - * @param activeStateChangeHandlerProviders The list of registered {@link ActiveStateChangeHandler}s that - * must be called back on state changes. - * @throws AtlasException - */ - @Inject - ActiveInstanceElectorService(Configuration configuration, - Set activeStateChangeHandlerProviders, - CuratorFactory curatorFactory, ActiveInstanceState activeInstanceState, - ServiceState serviceState) { - this.configuration = configuration; - this.activeStateChangeHandlerProviders = activeStateChangeHandlerProviders; - this.activeStateChangeHandlers = new ArrayList<>(); - this.curatorFactory = curatorFactory; - this.activeInstanceState = activeInstanceState; - this.serviceState = serviceState; - } - - /** - * Join leader election on starting up. - * - * If Atlas High Availability configuration is disabled, this operation is a no-op. - * @throws AtlasException - */ - @Override - public void start() throws AtlasException { - if (!RestHAConfiguration.isHAEnabled(configuration)) { - LOG.info("HA is not enabled, no need to start leader election service"); - return; - } - cacheActiveStateChangeHandlers(); - serverId = AtlasServerIdSelector.selectServerId(configuration); - joinElection(); - } - - private void joinElection() { - LOG.info("Starting leader election for {}", serverId); - String zkRoot = RestHAConfiguration.getZookeeperProperties(configuration).getZkRoot(); - leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); - leaderLatch.addListener(this); - try { - leaderLatch.start(); - LOG.info("Leader latch started for {}.", serverId); - } catch (Exception e) { - LOG.info("Exception while starting leader latch for {}.", serverId, e); - } - } - - /** - * Leave leader election process and clean up resources on shutting down. - * - * If Atlas High Availability configuration is disabled, this operation is a no-op. - * @throws AtlasException - */ - @Override - public void stop() { - if (!RestHAConfiguration.isHAEnabled(configuration)) { - LOG.info("HA is not enabled, no need to stop leader election service"); - return; - } - try { - leaderLatch.close(); - curatorFactory.close(); - } catch (IOException e) { - LOG.error("Error closing leader latch", e); - } - } - - /** - * Call all registered {@link ActiveStateChangeHandler}s on being elected active. - * - * In addition, shared state information about this instance becoming active is updated - * using {@link ActiveInstanceState}. - */ - @Override - public void isLeader() { - LOG.warn("Server instance with server id {} is elected as leader", serverId); - serviceState.becomingActive(); - try { - for (ActiveStateChangeHandler handler : activeStateChangeHandlers) { - handler.instanceIsActive(); - } - activeInstanceState.update(serverId); - serviceState.setActive(); - } catch (Exception e) { - LOG.error("Got exception while activating", e); - notLeader(); - rejoinElection(); - } - } - - private void cacheActiveStateChangeHandlers() { - if (activeStateChangeHandlers.size()==0) { - activeStateChangeHandlers.addAll(activeStateChangeHandlerProviders); - - LOG.info("activeStateChangeHandlers(): before reorder: " + activeStateChangeHandlers); - - Collections.sort(activeStateChangeHandlers, new Comparator() { - @Override - public int compare(ActiveStateChangeHandler lhs, ActiveStateChangeHandler rhs) { - return Integer.compare(lhs.getHandlerOrder(), rhs.getHandlerOrder()); - } - }); - - LOG.info("activeStateChangeHandlers(): after reorder: " + activeStateChangeHandlers); - } - } - - private void rejoinElection() { - try { - leaderLatch.close(); - joinElection(); - } catch (IOException e) { - LOG.error("Error rejoining election", e); - } - } - - /** - * Call all registered {@link ActiveStateChangeHandler}s on becoming passive instance. - */ - @Override - public void notLeader() { - LOG.warn("Server instance with server id {} is removed as leader", serverId); - serviceState.becomingPassive(); - for (int idx = activeStateChangeHandlers.size() - 1; idx >= 0; idx--) { - try { - activeStateChangeHandlers.get(idx).instanceIsPassive(); - } catch (AtlasException e) { - LOG.error("Error while reacting to passive state.", e); - } - } - serviceState.setPassive(); - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java deleted file mode 100644 index a22988de50..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ActiveInstanceState.java +++ /dev/null @@ -1,146 +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.atlas.notification.rest.web.service; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.AtlasException; -import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.notification.rest.RestHAConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; -import org.apache.zookeeper.CreateMode; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Id; -import org.apache.zookeeper.data.Stat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.inject.Inject; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -/** - * An object that encapsulates storing and retrieving state related to an Active Atlas server. - * - * The current implementation uses Zookeeper to store and read this state from. It does this - * under a read-write lock implemented using Curator's {@link InterProcessReadWriteLock} to - * provide for safety across multiple processes. - */ -@Component -public class ActiveInstanceState { - - private final Configuration configuration; - private final CuratorFactory curatorFactory; - - public static final String APACHE_ATLAS_ACTIVE_SERVER_INFO = "/active_server_info"; - - private static final Logger LOG = LoggerFactory.getLogger(ActiveInstanceState.class); - - /** - * Create a new instance of {@link ActiveInstanceState}. - * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} - * @throws AtlasException - */ - @Inject - public ActiveInstanceState(CuratorFactory curatorFactory) throws AtlasException { - this(ApplicationProperties.get(), curatorFactory); - } - - /** - * Create a new instance of {@link ActiveInstanceState}. - * @param configuration an instance of {@link Configuration} created from Atlas configuration - * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} - * @throws AtlasException - */ - public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory) { - this.configuration = configuration; - this.curatorFactory = curatorFactory; - } - - /** - * Update state of the active server instance. - * - * This method writes this instance's Server Address to a shared node in Zookeeper. - * This information is used by other passive instances to locate the current active server. - * @throws Exception - * @param serverId ID of this server instance - */ - public void update(String serverId) throws AtlasBaseException { - try { - CuratorFramework client = curatorFactory.clientInstance(); - RestHAConfiguration.ZookeeperProperties zookeeperProperties = - RestHAConfiguration.getZookeeperProperties(configuration); - String atlasServerAddress = RestHAConfiguration.getBoundAddressForId(configuration, serverId); - - List acls = new ArrayList(); - ACL parsedACL = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl(), - ZooDefs.Ids.OPEN_ACL_UNSAFE.get(0)); - acls.add(parsedACL); - - //adding world read permission - if (StringUtils.isNotEmpty(zookeeperProperties.getAcl())) { - ACL worldReadPermissionACL = new ACL(ZooDefs.Perms.READ, new Id("world", "anyone")); - acls.add(worldReadPermissionACL); - } - - Stat serverInfo = client.checkExists().forPath(getZnodePath(zookeeperProperties)); - if (serverInfo == null) { - client.create(). - withMode(CreateMode.EPHEMERAL). - withACL(acls). - forPath(getZnodePath(zookeeperProperties)); - } - client.setData().forPath(getZnodePath(zookeeperProperties), - atlasServerAddress.getBytes(Charset.forName("UTF-8"))); - } catch (Exception e) { - throw new AtlasBaseException(AtlasErrorCode.CURATOR_FRAMEWORK_UPDATE, e, "forPath: getZnodePath"); - } - } - - private String getZnodePath(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { - return zookeeperProperties.getZkRoot()+APACHE_ATLAS_ACTIVE_SERVER_INFO; - } - - /** - * Retrieve state of the active server instance. - * - * This method reads the active server location from the shared node in Zookeeper. - * @return the active server's address and port of form http://host-or-ip:port - */ - public String getActiveServerAddress() { - CuratorFramework client = curatorFactory.clientInstance(); - String serverAddress = null; - try { - RestHAConfiguration.ZookeeperProperties zookeeperProperties = - RestHAConfiguration.getZookeeperProperties(configuration); - byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); - serverAddress = new String(bytes, Charset.forName("UTF-8")); - } catch (Exception e) { - LOG.error("Error getting active server address", e); - } - return serverAddress; - } - -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java deleted file mode 100644 index 5f9fc840a8..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/AtlasZookeeperSecurityProperties.java +++ /dev/null @@ -1,74 +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.atlas.notification.rest.web.service; - -import com.google.common.base.Charsets; -import com.google.common.base.Preconditions; -import org.apache.commons.lang.StringUtils; -import org.apache.curator.framework.AuthInfo; -import org.apache.zookeeper.ZooDefs; -import org.apache.zookeeper.data.ACL; -import org.apache.zookeeper.data.Id; - -/** - * A class that parses configuration strings into Zookeeper ACL and Auth values. - */ -public class AtlasZookeeperSecurityProperties { - - public static ACL parseAcl(String aclString, ACL defaultAcl) { - if (StringUtils.isEmpty(aclString)) { - return defaultAcl; - } - return parseAcl(aclString); - } - - /** - * Get an {@link ACL} by parsing input string. - * @param aclString A string of the form scheme:id - * @return {@link ACL} with the perms set to {@link ZooDefs.Perms#ALL} and scheme and id - * taken from configuration values. - */ - public static ACL parseAcl(String aclString) { - String[] aclComponents = getComponents(aclString, "acl", "scheme:id"); - return new ACL(ZooDefs.Perms.ALL, new Id(aclComponents[0], aclComponents[1])); - } - - private static String[] getComponents(String securityString, String variableName, String formatExample) { - Preconditions.checkArgument(!StringUtils.isEmpty(securityString), - String.format("%s cannot be null or empty. " + - "Needs to be of form %s", variableName, formatExample)); - String[] aclComponents = securityString.split(":", 2); - if (aclComponents.length != 2) { - throw new IllegalArgumentException( - String.format("Invalid %s string. " + - "Needs to be of form %s", variableName, formatExample)); - } - return aclComponents; - } - - /** - * Get an {@link AuthInfo} by parsing input string. - * @param authString A string of the form scheme:authString - * @return {@link AuthInfo} with the scheme and auth taken from configuration values. - */ - public static AuthInfo parseAuth(String authString) { - String[] authComponents = getComponents(authString, "authString", "scheme:authString"); - return new AuthInfo(authComponents[0], authComponents[1].getBytes(Charsets.UTF_8)); - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java deleted file mode 100644 index 08fcf10a33..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/CuratorFactory.java +++ /dev/null @@ -1,202 +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.atlas.notification.rest.web.service; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Charsets; -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.atlas.notification.rest.RestHAConfiguration; -import org.apache.commons.configuration.Configuration; -import org.apache.curator.framework.AuthInfo; -import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.CuratorFrameworkFactory; -import org.apache.curator.framework.api.ACLProvider; -import org.apache.curator.framework.recipes.leader.LeaderLatch; -import org.apache.curator.framework.recipes.locks.InterProcessMutex; -import org.apache.curator.retry.ExponentialBackoffRetry; -import org.apache.hadoop.security.UserGroupInformation; -import org.apache.zookeeper.data.ACL; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import javax.inject.Singleton; -import java.io.IOException; -import java.util.Arrays; -import java.util.List; - -/** - * A factory to create objects related to Curator. - * - * Allows for stubbing in tests. - */ -@Singleton -@Component -public class CuratorFactory { - - private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); - - public static final String APACHE_ATLAS_LEADER_ELECTOR_PATH = "/leader_elector_path"; - public static final String SASL_SCHEME = "sasl"; - public static final String WORLD_SCHEME = "world"; - public static final String ANYONE_ID = "anyone"; - public static final String AUTH_SCHEME = "auth"; - public static final String DIGEST_SCHEME = "digest"; - public static final String IP_SCHEME = "ip"; - public static final String SETUP_LOCK = "/setup_lock"; - - private final Configuration configuration; - private CuratorFramework curatorFramework; - - /** - * Initializes the {@link CuratorFramework} that is used for all interaction with Zookeeper. - * @throws AtlasException - */ - public CuratorFactory() throws AtlasException { - this(ApplicationProperties.get()); - } - - public CuratorFactory(Configuration configuration) { - this.configuration = configuration; - initializeCuratorFramework(); - } - - @VisibleForTesting - protected void initializeCuratorFramework() { - RestHAConfiguration.ZookeeperProperties zookeeperProperties = - RestHAConfiguration.getZookeeperProperties(configuration); - CuratorFrameworkFactory.Builder builder = getBuilder(zookeeperProperties); - enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); - curatorFramework = builder.build(); - curatorFramework.start(); - } - - @VisibleForTesting - void enhanceBuilderWithSecurityParameters(RestHAConfiguration.ZookeeperProperties zookeeperProperties, - CuratorFrameworkFactory.Builder builder) { - - ACLProvider aclProvider = getAclProvider(zookeeperProperties); - - AuthInfo authInfo = null; - if (zookeeperProperties.hasAuth()) { - authInfo = AtlasZookeeperSecurityProperties.parseAuth(zookeeperProperties.getAuth()); - } - - if (aclProvider != null) { - LOG.info("Setting up acl provider."); - builder.aclProvider(aclProvider); - if (authInfo != null) { - byte[] auth = authInfo.getAuth(); - LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), - getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); - builder.authorization(authInfo.getScheme(), auth); - } - } - } - - private String getCurrentUser() { - try { - return UserGroupInformation.getCurrentUser().getUserName(); - } catch (IOException ioe) { - return "unknown"; - } - } - - private ACLProvider getAclProvider(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { - ACLProvider aclProvider = null; - if (zookeeperProperties.hasAcl()) { - final ACL acl = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl()); - LOG.info("Setting ACL for id {} with scheme {} and perms {}.", - getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), - acl.getId().getScheme(), acl.getPerms()); - LOG.info("Current logged in user: {}", getCurrentUser()); - final List acls = Arrays.asList(acl); - aclProvider = new ACLProvider() { - @Override - public List getDefaultAcl() { - return acls; - } - - @Override - public List getAclForPath(String path) { - return acls; - } - }; - } - return aclProvider; - } - - private String getIdForLogging(String scheme, String id) { - if (scheme.equalsIgnoreCase(SASL_SCHEME) || - scheme.equalsIgnoreCase(IP_SCHEME)) { - return id; - } else if (scheme.equalsIgnoreCase(WORLD_SCHEME)) { - return ANYONE_ID; - } else if (scheme.equalsIgnoreCase(AUTH_SCHEME) || - scheme.equalsIgnoreCase(DIGEST_SCHEME)) { - return id.split(":")[0]; - } - return "unknown"; - } - - private CuratorFrameworkFactory.Builder getBuilder(RestHAConfiguration.ZookeeperProperties zookeeperProperties) { - return CuratorFrameworkFactory.builder(). - connectString(zookeeperProperties.getConnectString()). - sessionTimeoutMs(zookeeperProperties.getSessionTimeout()). - retryPolicy(new ExponentialBackoffRetry( - zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); - } - - /** - * Cleanup resources related to {@link CuratorFramework}. - * - * After this call, no further calls to any curator objects should be done. - */ - public void close() { - curatorFramework.close(); - } - - /** - * Returns a pre-created instance of {@link CuratorFramework}. - * - * This method can be called any number of times to access the {@link CuratorFramework} used in the - * application. - * @return - */ - public CuratorFramework clientInstance() { - return curatorFramework; - } - - /** - * Create a new instance {@link LeaderLatch} - * @param serverId the ID used to register this instance with curator. - * This ID should typically be obtained using - * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} - * @param zkRoot the root znode under which the leader latch node is added. - * @return - */ - public LeaderLatch leaderLatchInstance(String serverId, String zkRoot) { - return new LeaderLatch(curatorFramework, zkRoot+APACHE_ATLAS_LEADER_ELECTOR_PATH, serverId); - } - - public InterProcessMutex lockInstance(String zkRoot) { - return new InterProcessMutex(curatorFramework, zkRoot+ SETUP_LOCK); - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java deleted file mode 100644 index e3eb211aad..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/EmbeddedServer.java +++ /dev/null @@ -1,113 +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.atlas.notification.rest.web.service; - -import org.apache.atlas.AtlasConfiguration; -import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.exception.AtlasBaseException; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.thread.ExecutorThreadPool; -import org.eclipse.jetty.webapp.WebAppContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public class EmbeddedServer { - public static final Logger LOG = LoggerFactory.getLogger(EmbeddedServer.class); - public static final String REST_DEFAULT_BIND_ADDRESS = "0.0.0.0"; - public static final Date SERVER_START_TIME = new Date(); - - protected final Server server; - - public EmbeddedServer(String host, int port, String path) throws IOException { - int queueSize = AtlasConfiguration.WEBSERVER_QUEUE_SIZE.getInt(); - LinkedBlockingQueue queue = new LinkedBlockingQueue<>(queueSize); - int minThreads = AtlasConfiguration.WEBSERVER_MIN_THREADS.getInt(); - int maxThreads = AtlasConfiguration.WEBSERVER_MAX_THREADS.getInt(); - long keepAliveTime = AtlasConfiguration.WEBSERVER_KEEPALIVE_SECONDS.getLong(); - ThreadPoolExecutor executor = new ThreadPoolExecutor(maxThreads, maxThreads, keepAliveTime, TimeUnit.SECONDS, queue); - ExecutorThreadPool pool = new ExecutorThreadPool(executor, minThreads); - - server = new Server(pool); - Connector connector = getConnector(host, port); - server.addConnector(connector); - - WebAppContext application = getWebAppContext(path); - server.setHandler(application); - - } - - protected Connector getConnector(String host, int port) throws IOException { - HttpConfiguration http_config = new HttpConfiguration(); - // this is to enable large header sizes when Kerberos is enabled with AD - //final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); - final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); - http_config.setResponseHeaderSize(bufferSize); - http_config.setRequestHeaderSize(bufferSize); - http_config.setSendServerVersion(false); - - ServerConnector connector = new ServerConnector(server, new HttpConnectionFactory(http_config)); - connector.setPort(port); - connector.setHost(host); - return connector; - } - - protected WebAppContext getWebAppContext(String path) { - WebAppContext application = new WebAppContext(path, "/"); - application.setClassLoader(Thread.currentThread().getContextClassLoader()); - // Disable directory listing - application.setInitParameter("org.eclipse.jetty.servlet.Default.dirAllowed", "false"); - return application; - } - - public void start() throws AtlasBaseException { - try { - server.start(); - server.join(); - } catch (Exception e) { - throw new AtlasBaseException(AtlasErrorCode.EMBEDDED_SERVER_START, e); - } - } - - public void stop() { - try { - server.stop(); - } catch (Exception e) { - LOG.warn("Error during shutdown", e); - } - } - - public static EmbeddedServer newServer(String host, int port, String path, boolean secure) - throws IOException { - if (secure) { - return new SecureEmbeddedServer(host, port, path); - } else { - return new EmbeddedServer(host, port, path); - } - } - -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java deleted file mode 100755 index 649dadca07..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/SecureEmbeddedServer.java +++ /dev/null @@ -1,351 +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.atlas.notification.rest.web.service; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasConfiguration; -import org.apache.atlas.AtlasException; -import org.apache.commons.lang.StringUtils; -import org.eclipse.jetty.http.HttpVersion; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.SecureRequestCustomizer; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.SslConnectionFactory; -import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; -import javax.net.ssl.SSLContext; -import javax.net.ssl.TrustManager; -import javax.net.ssl.TrustManagerFactory; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_DEFAULT_PROTOCOL; -import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_EXCLUDE_CIPHER_SUITES; -import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_EXCLUDE_PROTOCOLS; -import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_ENABLED_ALGORITHMS; -import static org.apache.atlas.security.SecurityProperties.ATLAS_SSL_ENABLED_PROTOCOLS; -import static org.apache.atlas.security.SecurityProperties.CLIENT_AUTH_KEY; -import static org.apache.atlas.security.SecurityProperties.DEFATULT_TRUSTORE_FILE_LOCATION; -import static org.apache.atlas.security.SecurityProperties.DEFAULT_CIPHER_SUITES; -import static org.apache.atlas.security.SecurityProperties.DEFAULT_EXCLUDE_PROTOCOLS; -import static org.apache.atlas.security.SecurityProperties.DEFAULT_KEYSTORE_FILE_LOCATION; -import static org.apache.atlas.security.SecurityProperties.KEYSTORE_FILE_KEY; -import static org.apache.atlas.security.SecurityProperties.KEYSTORE_PASSWORD_KEY; -import static org.apache.atlas.security.SecurityProperties.KEYSTORE_TYPE; -import static org.apache.atlas.security.SecurityProperties.SERVER_CERT_PASSWORD_KEY; -import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_FILE_KEY; -import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_PASSWORD_KEY; -import static org.apache.atlas.security.SecurityProperties.TRUSTSTORE_TYPE; -import static org.apache.atlas.security.SecurityUtil.getPassword; - - -/** - * This is a jetty server which requires client auth via certificates. - */ -public class SecureEmbeddedServer extends EmbeddedServer { - - private static final Logger LOG = LoggerFactory.getLogger(SecureEmbeddedServer.class); - - public static final String ATLAS_KEYSTORE_FILE_TYPE_DEFAULT = "jks"; - public static final String ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT = "jks"; - public static final String ATLAS_TLS_CONTEXT_ALGO_TYPE = "TLS"; - public static final String ATLAS_TLS_KEYMANAGER_DEFAULT_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); - public static final String ATLAS_TLS_TRUSTMANAGER_DEFAULT_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); - - - public SecureEmbeddedServer(String host, int port, String path) throws IOException { - super(host, port, path); - } - - @Override - protected Connector getConnector(String host, int port) throws IOException { - org.apache.commons.configuration.Configuration config = getConfiguration(); - - SSLContext sslContext = getSSLContext(); - if (sslContext != null) { - SSLContext.setDefault(sslContext); - } - - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(config.getString(KEYSTORE_FILE_KEY, - System.getProperty(KEYSTORE_FILE_KEY, DEFAULT_KEYSTORE_FILE_LOCATION))); - sslContextFactory.setKeyStorePassword(getPassword(config, KEYSTORE_PASSWORD_KEY)); - sslContextFactory.setKeyManagerPassword(getPassword(config, SERVER_CERT_PASSWORD_KEY)); - sslContextFactory.setKeyStoreType(config.getString(KEYSTORE_TYPE, ATLAS_KEYSTORE_FILE_TYPE_DEFAULT)); - sslContextFactory.setTrustStorePath(config.getString(TRUSTSTORE_FILE_KEY, - System.getProperty(TRUSTSTORE_FILE_KEY, DEFATULT_TRUSTORE_FILE_LOCATION))); - sslContextFactory.setTrustStorePassword(getPassword(config, TRUSTSTORE_PASSWORD_KEY)); - sslContextFactory.setTrustStoreType(config.getString(TRUSTSTORE_TYPE, ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT)); - sslContextFactory.setWantClientAuth(config.getBoolean(CLIENT_AUTH_KEY, Boolean.getBoolean(CLIENT_AUTH_KEY))); - - List cipherList = config.getList(ATLAS_SSL_EXCLUDE_CIPHER_SUITES, DEFAULT_CIPHER_SUITES); - sslContextFactory.setExcludeCipherSuites(cipherList.toArray(new String[cipherList.size()])); - sslContextFactory.setRenegotiationAllowed(false); - - String[] excludedProtocols = config.containsKey(ATLAS_SSL_EXCLUDE_PROTOCOLS) ? - config.getStringArray(ATLAS_SSL_EXCLUDE_PROTOCOLS) : DEFAULT_EXCLUDE_PROTOCOLS; - if (excludedProtocols != null && excludedProtocols.length > 0) { - sslContextFactory.addExcludeProtocols(excludedProtocols); - } - - List rawCipherValues = config.getList(ATLAS_SSL_ENABLED_ALGORITHMS); - List enabledCiphersList = Collections.emptyList(); - - if (rawCipherValues != null && !rawCipherValues.isEmpty()) { - List parsedCiphers = new ArrayList<>(); - - for (Object rawCipherValue : rawCipherValues) { - if (rawCipherValue == null) { - continue; - } - - String value = String.valueOf(rawCipherValue).trim(); - if (value.isEmpty()) { - continue; - } - - parsedCiphers.addAll(Arrays.asList(value.split("\\s*[:,;\\s]+\\s*"))); - } - - enabledCiphersList = parsedCiphers; - } - - if (!enabledCiphersList.isEmpty()) { - sslContextFactory.setIncludeCipherSuites(enabledCiphersList.toArray(new String[enabledCiphersList.size()])); - } - - String[] enabledProtocols = config.containsKey(ATLAS_SSL_ENABLED_PROTOCOLS) ? - config.getStringArray(ATLAS_SSL_ENABLED_PROTOCOLS) : ATLAS_SSL_DEFAULT_PROTOCOL; - - if (enabledProtocols != null && enabledProtocols.length > 0) { - sslContextFactory.setIncludeProtocols(enabledProtocols); - } - - // SSL HTTP Configuration - // HTTP Configuration - HttpConfiguration http_config = new HttpConfiguration(); - http_config.setSecureScheme("https"); - final int bufferSize = AtlasConfiguration.WEBSERVER_REQUEST_BUFFER_SIZE.getInt(); - http_config.setSecurePort(port); - http_config.setRequestHeaderSize(bufferSize); - http_config.setResponseHeaderSize(bufferSize); - http_config.setSendServerVersion(false); - http_config.setSendDateHeader(false); - - HttpConfiguration https_config = new HttpConfiguration(http_config); - https_config.addCustomizer(new SecureRequestCustomizer()); - https_config.setSendServerVersion(false); - - // SSL Connector - ServerConnector sslConnector = new ServerConnector(server, - new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()), - new HttpConnectionFactory(https_config)); - sslConnector.setPort(port); - server.addConnector(sslConnector); - - return sslConnector; - } - - - /** - * Returns the application configuration. - * @return - */ - protected org.apache.commons.configuration.Configuration getConfiguration() { - try { - return ApplicationProperties.get(); - } catch (AtlasException e) { - throw new RuntimeException("Unable to load configuration: " + ApplicationProperties.APPLICATION_PROPERTIES); - } - } - - /** - * creates the SSLContext using the available keystore and truststore. - * @return - */ - private SSLContext getSSLContext() { - KeyManager[] kmList = getKeyManagers(); - TrustManager[] tmList = getTrustManagers(); - SSLContext sslContext = null; - if (tmList != null) { - try { - sslContext = SSLContext.getInstance(ATLAS_TLS_CONTEXT_ALGO_TYPE); - sslContext.init(kmList, tmList, new SecureRandom()); - } catch (NoSuchAlgorithmException e) { - LOG.error("SSL algorithm is not available in the environment. Reason: " + e.toString()); - } catch (KeyManagementException e) { - LOG.error("Unable to initials the SSLContext. Reason: " + e.toString()); - } - } - return sslContext; - } - - /** - * Generating the KeyManager using the provided keystore. - * @return - */ - private KeyManager[] getKeyManagers() { - KeyManager[] kmList = null; - try { - - String keyStoreFile = getConfiguration().getString(KEYSTORE_FILE_KEY, - System.getProperty(KEYSTORE_FILE_KEY, DEFAULT_KEYSTORE_FILE_LOCATION)); - String keyStoreFilepwd = getPassword(getConfiguration(), KEYSTORE_PASSWORD_KEY); - - if (StringUtils.isNotEmpty(keyStoreFile) && StringUtils.isNotEmpty(keyStoreFilepwd)) { - InputStream in = null; - - try { - in = getFileInputStream(keyStoreFile); - - if (in != null) { - KeyStore keyStore = KeyStore.getInstance(getConfiguration().getString(KEYSTORE_TYPE, ATLAS_KEYSTORE_FILE_TYPE_DEFAULT)); - - keyStore.load(in, keyStoreFilepwd.toCharArray()); - - KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ATLAS_TLS_KEYMANAGER_DEFAULT_ALGO_TYPE); - - keyManagerFactory.init(keyStore, keyStoreFilepwd.toCharArray()); - - kmList = keyManagerFactory.getKeyManagers(); - } else { - LOG.error("Unable to obtain keystore from file [" + keyStoreFile + "]"); - } - } catch (KeyStoreException e) { - LOG.error("Unable to obtain from KeyStore :" + e.getMessage(), e); - } catch (NoSuchAlgorithmException e) { - LOG.error("SSL algorithm is NOT available in the environment", e); - } catch (CertificateException e) { - LOG.error("Unable to obtain the requested certification ", e); - } catch (FileNotFoundException e) { - LOG.error("Unable to find the necessary TLS Keystore Files", e); - } catch (IOException e) { - LOG.error("Unable to read the necessary TLS Keystore Files", e); - } catch (UnrecoverableKeyException e) { - LOG.error("Unable to recover the key from keystore", e); - } finally { - close(in, keyStoreFile); - } - } - - } catch (IOException exception) { - LOG.error(exception.getMessage()); - } - return kmList; - } - - /** - * Generating the TrustManager using the provided truststore - * @return - */ - private TrustManager[] getTrustManagers() { - TrustManager[] tmList = null; - try { - String truststoreFile = getConfiguration().getString(TRUSTSTORE_FILE_KEY, - System.getProperty(TRUSTSTORE_FILE_KEY, DEFATULT_TRUSTORE_FILE_LOCATION)); - String trustStoreFilepwd = getPassword(getConfiguration(), TRUSTSTORE_PASSWORD_KEY); - - if (StringUtils.isNotEmpty(truststoreFile) && StringUtils.isNotEmpty(trustStoreFilepwd)) { - InputStream in = null; - - try { - in = getFileInputStream(truststoreFile); - - if (in != null) { - KeyStore trustStore = KeyStore.getInstance(getConfiguration().getString(TRUSTSTORE_TYPE, ATLAS_TRUSTSTORE_FILE_TYPE_DEFAULT)); - - trustStore.load(in, trustStoreFilepwd.toCharArray()); - - TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(ATLAS_TLS_TRUSTMANAGER_DEFAULT_ALGO_TYPE); - - trustManagerFactory.init(trustStore); - - tmList = trustManagerFactory.getTrustManagers(); - } else { - LOG.error("Unable to obtain truststore from file [" + truststoreFile + "]"); - } - } catch (KeyStoreException e) { - LOG.error("Unable to obtain from KeyStore", e); - } catch (NoSuchAlgorithmException e) { - LOG.error("SSL algorithm is NOT available in the environment :" + e.getMessage(), e); - } catch (CertificateException e) { - LOG.error("Unable to obtain the requested certification :" + e.getMessage(), e); - } catch (FileNotFoundException e) { - LOG.error("Unable to find the necessary TLS TrustStore File:" + truststoreFile, e); - } catch (IOException e) { - LOG.error("Unable to read the necessary TLS TrustStore Files :" + truststoreFile, e); - } finally { - close(in, truststoreFile); - } - } - - } catch (IOException exception) { - LOG.error(exception.getMessage()); - } - return tmList; - } - - private InputStream getFileInputStream(String fileName) throws IOException { - InputStream in = null; - if (StringUtils.isNotEmpty(fileName)) { - File f = new File(fileName); - if (f.exists()) { - in = new FileInputStream(f); - } else { - in = ClassLoader.getSystemResourceAsStream(fileName); - } - } - return in; - } - - /** - * Closing file-stream. - * @param str - * @param filename - */ - private void close(InputStream str, String filename) { - if (str != null) { - try { - str.close(); - } catch (IOException excp) { - LOG.error("Error while closing file: [" + filename + "]", excp); - } - } - } -} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java deleted file mode 100644 index 205237627a..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/UserService.java +++ /dev/null @@ -1,43 +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.atlas.notification.rest.web.service; - -import org.apache.atlas.server.common.dao.UserDao; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; - -import javax.inject.Inject; - -@Service -public class UserService implements UserDetailsService { - - private final UserDao userDao; - - @Inject - public UserService(UserDao userDao) { - this.userDao = userDao; - } - - @Override - public UserDetails loadUserByUsername(final String username) - throws UsernameNotFoundException { - return userDao.loadUserByUsername(username); - } -} diff --git a/server-common/pom.xml b/server-common/pom.xml index d9dacf7d43..78758f6e7b 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -106,6 +106,18 @@ org.apache.commons commons-lang3 + + org.apache.curator + curator-client + + + org.apache.curator + curator-framework + + + org.apache.curator + curator-recipes + org.apache.hadoop hadoop-common @@ -114,6 +126,18 @@ org.apache.httpcomponents httpclient + + org.apache.zookeeper + zookeeper + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-webapp + org.kohsuke libpam4j diff --git a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceElectorService.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java similarity index 75% rename from webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceElectorService.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java index ac9ecd5c9c..4e8eb522c3 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceElectorService.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java @@ -15,15 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import org.apache.atlas.AtlasException; -import org.apache.atlas.ha.AtlasServerIdSelector; -import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.RequestContext; import org.apache.atlas.listener.ActiveStateChangeHandler; import org.apache.atlas.service.Service; -import org.apache.atlas.util.AtlasMetricsUtil; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.framework.recipes.leader.LeaderLatchListener; @@ -32,16 +29,16 @@ import org.springframework.stereotype.Component; import javax.inject.Inject; - import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; /** * A service that implements leader election to determine whether this Atlas server is Active. - * + *

* The service implements leader election through Curator's * {@link LeaderLatch} recipe. The service also implements {@link LeaderLatchListener} to get * notified of changes to leadership state. Upon becoming leader, this instance is treated as the @@ -49,7 +46,6 @@ * on being removed from leadership, this instance is treated as a passive instance and calls * {@link ActiveStateChangeHandler}s to deactivate them. */ - @Component // // This should be called the last, leaving it without the @Order(Integer.MAX_VALUE) will make it get @@ -60,66 +56,57 @@ public class ActiveInstanceElectorService implements Service, LeaderLatchListene private final Configuration configuration; private final ServiceState serviceState; private final ActiveInstanceState activeInstanceState; - private final AtlasMetricsUtil metricsUtil; + private final HighAvailabilitySupport haSupport; + private final List metricsHooks; private final Set activeStateChangeHandlerProviders; private final List activeStateChangeHandlers; private final CuratorFactory curatorFactory; private LeaderLatch leaderLatch; private String serverId; - /** - * Create a new instance of {@link ActiveInstanceElectorService} - * @param activeStateChangeHandlerProviders The list of registered {@link ActiveStateChangeHandler}s that - * must be called back on state changes. - * @throws AtlasException - */ @Inject - ActiveInstanceElectorService(Configuration configuration, Set activeStateChangeHandlerProviders, - CuratorFactory curatorFactory, ActiveInstanceState activeInstanceState, ServiceState serviceState, AtlasMetricsUtil metricsUtil) { + public ActiveInstanceElectorService(Configuration configuration, + Set activeStateChangeHandlerProviders, + List metricsHooks, + CuratorFactory curatorFactory, + ActiveInstanceState activeInstanceState, + ServiceState serviceState, + HighAvailabilitySupport haSupport) { this.configuration = configuration; this.activeStateChangeHandlerProviders = activeStateChangeHandlerProviders; + this.metricsHooks = metricsHooks != null ? metricsHooks : Collections.emptyList(); this.activeStateChangeHandlers = new ArrayList<>(); this.curatorFactory = curatorFactory; this.activeInstanceState = activeInstanceState; this.serviceState = serviceState; - this.metricsUtil = metricsUtil; + this.haSupport = haSupport; } - /** - * Join leader election on starting up. - * - * If Atlas High Availability configuration is disabled, this operation is a no-op. - * @throws AtlasException - */ @Override public void start() throws AtlasException { - metricsUtil.onServerStart(); + boolean haEnabled = haSupport.isHAEnabled(configuration); - if (!HAConfiguration.isHAEnabled(configuration)) { - metricsUtil.onServerActivation(); + for (ServiceMetricsHook hook : metricsHooks) { + hook.onServerStart(); + if (!haEnabled) { + hook.onServerActivation(); + } + } + if (!haEnabled) { LOG.info("HA is not enabled, no need to start leader election service"); - return; } cacheActiveStateChangeHandlers(); - - serverId = AtlasServerIdSelector.selectServerId(configuration); - + serverId = haSupport.selectServerId(configuration); joinElection(); } - /** - * Leave leader election process and clean up resources on shutting down. - * - * If Atlas High Availability configuration is disabled, this operation is a no-op. - */ @Override public void stop() { - if (!HAConfiguration.isHAEnabled(configuration)) { + if (!haSupport.isHAEnabled(configuration)) { LOG.info("HA is not enabled, no need to stop leader election service"); - return; } @@ -131,41 +118,32 @@ public void stop() { } } - /** - * Call all registered {@link ActiveStateChangeHandler}s on being elected active. - * - * In addition, shared state information about this instance becoming active is updated - * using {@link ActiveInstanceState}. - */ @Override public void isLeader() { LOG.warn("Server instance with server id {} is elected as leader", serverId); - serviceState.becomingActive(); try { for (ActiveStateChangeHandler handler : activeStateChangeHandlers) { handler.instanceIsActive(); } - activeInstanceState.update(serverId); serviceState.setActive(); - metricsUtil.onServerActivation(); + for (ServiceMetricsHook metricsHook : metricsHooks) { + metricsHook.onServerActivation(); + } } catch (Exception e) { LOG.error("Got exception while activating", e); - notLeader(); rejoinElection(); + } finally { + RequestContext.clear(); } } - /** - * Call all registered {@link ActiveStateChangeHandler}s on becoming passive instance. - */ @Override public void notLeader() { LOG.warn("Server instance with server id {} is removed as leader", serverId); - serviceState.becomingPassive(); for (int idx = activeStateChangeHandlers.size() - 1; idx >= 0; idx--) { @@ -182,15 +160,12 @@ public void notLeader() { private void joinElection() { LOG.info("Starting leader election for {}", serverId); - String zkRoot = HAConfiguration.getZookeeperProperties(configuration).getZkRoot(); - + String zkRoot = haSupport.getZookeeperProperties(configuration).getZkRoot(); leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); - leaderLatch.addListener(this); try { leaderLatch.start(); - LOG.info("Leader latch started for {}.", serverId); } catch (Exception e) { LOG.info("Exception while starting leader latch for {}.", serverId, e); @@ -212,7 +187,6 @@ private void cacheActiveStateChangeHandlers() { private void rejoinElection() { try { leaderLatch.close(); - joinElection(); } catch (IOException e) { LOG.error("Error rejoining election", e); diff --git a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java similarity index 59% rename from webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceState.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java index 8e79dbe648..d1ec75d893 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java @@ -15,18 +15,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package org.apache.atlas.server.common.service; -package org.apache.atlas.web.service; - -import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.AtlasException; import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; -import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; @@ -46,53 +42,31 @@ * An object that encapsulates storing and retrieving state related to an Active Atlas server. * * The current implementation uses Zookeeper to store and read this state from. It does this - * under a read-write lock implemented using Curator's {@link InterProcessReadWriteLock} to + * under a read-write lock implemented using Curator's {@link org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock} to * provide for safety across multiple processes. */ @Component -public class ActiveInstanceState { +public class ActiveInstanceState implements ActiveInstanceStateProvider { private static final Logger LOG = LoggerFactory.getLogger(ActiveInstanceState.class); public static final String APACHE_ATLAS_ACTIVE_SERVER_INFO = "/active_server_info"; - private final Configuration configuration; - private final CuratorFactory curatorFactory; + private final Configuration configuration; + private final CuratorFactory curatorFactory; + private final HighAvailabilitySupport haSupport; - /** - * Create a new instance of {@link ActiveInstanceState}. - * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} - * @throws AtlasException - */ @Inject - public ActiveInstanceState(CuratorFactory curatorFactory) throws AtlasException { - this(ApplicationProperties.get(), curatorFactory); - } - - /** - * Create a new instance of {@link ActiveInstanceState}. - * @param configuration an instance of {@link Configuration} created from Atlas configuration - * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} - */ - public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory) { + public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory, HighAvailabilitySupport haSupport) { this.configuration = configuration; - this.curatorFactory = curatorFactory; + this.curatorFactory = curatorFactory; + this.haSupport = haSupport; } - /** - * Update state of the active server instance. - * - * This method writes this instance's Server Address to a shared node in Zookeeper. - * This information is used by other passive instances to locate the current active server. - * @throws AtlasBaseException - * @param serverId ID of this server instance - */ public void update(String serverId) throws AtlasBaseException { try { - CuratorFramework client = curatorFactory.clientInstance(); - - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); - - String atlasServerAddress = HAConfiguration.getBoundAddressForId(configuration, serverId); + CuratorFramework client = curatorFactory.clientInstance(); + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); + String atlasServerAddress = haSupport.getBoundAddressForId(configuration, serverId); List acls = new ArrayList<>(); @@ -122,20 +96,14 @@ public void update(String serverId) throws AtlasBaseException { } } - /** - * Retrieve state of the active server instance. - * - * This method reads the active server location from the shared node in Zookeeper. - * @return the active server's address and port of form http://host-or-ip:port - */ + @Override public String getActiveServerAddress() { - CuratorFramework client = curatorFactory.clientInstance(); - String serverAddress = null; + CuratorFramework client = curatorFactory.clientInstance(); + String serverAddress = null; try { - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); - - byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); + byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); serverAddress = new String(bytes, StandardCharsets.UTF_8); } catch (Exception e) { @@ -145,7 +113,7 @@ public String getActiveServerAddress() { return serverAddress; } - private String getZnodePath(HAConfiguration.ZookeeperProperties zookeeperProperties) { + private String getZnodePath(HighAvailabilityProperties zookeeperProperties) { return zookeeperProperties.getZkRoot() + APACHE_ATLAS_ACTIVE_SERVER_INFO; } } diff --git a/webapp/src/main/java/org/apache/atlas/web/service/AtlasZookeeperSecurityProperties.java b/server-common/src/main/java/org/apache/atlas/server/common/service/AtlasZookeeperSecurityProperties.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/service/AtlasZookeeperSecurityProperties.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/AtlasZookeeperSecurityProperties.java index fc77ee3f8b..884217c5a7 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/AtlasZookeeperSecurityProperties.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/AtlasZookeeperSecurityProperties.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import com.google.common.base.Charsets; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/service/CuratorFactory.java b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java similarity index 74% rename from webapp/src/main/java/org/apache/atlas/web/service/CuratorFactory.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java index 7a390f4985..8674152724 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/CuratorFactory.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java @@ -11,18 +11,15 @@ *

* 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. + * 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.atlas.web.service; +package org.apache.atlas.server.common.service; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; -import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; -import org.apache.atlas.ha.HAConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.AuthInfo; import org.apache.curator.framework.CuratorFramework; @@ -37,21 +34,18 @@ import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import javax.inject.Inject; import javax.inject.Singleton; - import java.io.IOException; -import java.util.Collections; +import java.util.Arrays; import java.util.List; /** * A factory to create objects related to Curator. - * - * Allows for stubbing in tests. */ @Singleton @Component public class CuratorFactory { - private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); public static final String APACHE_ATLAS_LEADER_ELECTOR_PATH = "/leader_elector_path"; public static final String SASL_SCHEME = "sasl"; public static final String WORLD_SCHEME = "world"; @@ -61,39 +55,42 @@ public class CuratorFactory { public static final String IP_SCHEME = "ip"; public static final String SETUP_LOCK = "/setup_lock"; - private final Configuration configuration; - private CuratorFramework curatorFramework; + private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); + + private final Configuration configuration; + private final HighAvailabilitySupport haSupport; + private CuratorFramework curatorFramework; /** * Initializes the {@link CuratorFramework} that is used for all interaction with Zookeeper. + * * @throws AtlasException */ - public CuratorFactory() throws AtlasException { - this(ApplicationProperties.get()); - } - - public CuratorFactory(Configuration configuration) { + @Inject + public CuratorFactory(Configuration configuration, HighAvailabilitySupport haSupport) { this.configuration = configuration; + this.haSupport = haSupport; - if (configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY) && configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)) { - initializeCuratorFramework(); - } + initializeCuratorFramework(); } /** * Cleanup resources related to {@link CuratorFramework}. - * + *

* After this call, no further calls to any curator objects should be done. */ public void close() { - curatorFramework.close(); + if (curatorFramework != null) { + curatorFramework.close(); + } } /** * Returns a pre-created instance of {@link CuratorFramework}. - * + *

* This method can be called any number of times to access the {@link CuratorFramework} used in the * application. + * * @return */ public CuratorFramework clientInstance() { @@ -102,9 +99,10 @@ public CuratorFramework clientInstance() { /** * Create a new instance {@link LeaderLatch} + * * @param serverId the ID used to register this instance with curator. - * This ID should typically be obtained using - * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} + * This ID should typically be obtained using + * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} * @param zkRoot the root znode under which the leader latch node is added. * @return */ @@ -118,58 +116,50 @@ public InterProcessMutex lockInstance(String zkRoot) { @VisibleForTesting protected void initializeCuratorFramework() { - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); - CuratorFrameworkFactory.Builder builder = getBuilder(zookeeperProperties); + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); + CuratorFrameworkFactory.Builder builder = getBuilder(zookeeperProperties); enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); curatorFramework = builder.build(); - curatorFramework.start(); } @VisibleForTesting - void enhanceBuilderWithSecurityParameters(HAConfiguration.ZookeeperProperties zookeeperProperties, CuratorFrameworkFactory.Builder builder) { + void enhanceBuilderWithSecurityParameters(HighAvailabilityProperties zookeeperProperties, + CuratorFrameworkFactory.Builder builder) { ACLProvider aclProvider = getAclProvider(zookeeperProperties); - AuthInfo authInfo = null; + AuthInfo authInfo = null; if (zookeeperProperties.hasAuth()) { authInfo = AtlasZookeeperSecurityProperties.parseAuth(zookeeperProperties.getAuth()); } if (aclProvider != null) { LOG.info("Setting up acl provider."); - builder.aclProvider(aclProvider); if (authInfo != null) { byte[] auth = authInfo.getAuth(); - - LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); - + LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), + getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); builder.authorization(authInfo.getScheme(), auth); } } } - private String getCurrentUser() { - try { - return UserGroupInformation.getCurrentUser().getUserName(); - } catch (IOException ioe) { - return "unknown"; - } - } - - private ACLProvider getAclProvider(HAConfiguration.ZookeeperProperties zookeeperProperties) { + private ACLProvider getAclProvider(HighAvailabilityProperties zookeeperProperties) { ACLProvider aclProvider = null; if (zookeeperProperties.hasAcl()) { final ACL acl = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl()); - LOG.info("Setting ACL for id {} with scheme {} and perms {}.", getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), acl.getId().getScheme(), acl.getPerms()); + LOG.info("Setting ACL for id {} with scheme {} and perms {}.", + getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), + acl.getId().getScheme(), acl.getPerms()); LOG.info("Current logged in user: {}", getCurrentUser()); - final List acls = Collections.singletonList(acl); + final List acls = Arrays.asList(acl); aclProvider = new ACLProvider() { @Override @@ -183,9 +173,26 @@ public List getAclForPath(String path) { } }; } + return aclProvider; } + private CuratorFrameworkFactory.Builder getBuilder(HighAvailabilityProperties zookeeperProperties) { + return CuratorFrameworkFactory.builder() + .connectString(zookeeperProperties.getConnectString()) + .sessionTimeoutMs(zookeeperProperties.getSessionTimeout()) + .retryPolicy(new ExponentialBackoffRetry( + zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); + } + + private String getCurrentUser() { + try { + return UserGroupInformation.getCurrentUser().getUserName(); + } catch (IOException ioe) { + return "unknown"; + } + } + private String getIdForLogging(String scheme, String id) { if (scheme.equalsIgnoreCase(SASL_SCHEME) || scheme.equalsIgnoreCase(IP_SCHEME)) { return id; @@ -197,11 +204,4 @@ private String getIdForLogging(String scheme, String id) { return "unknown"; } - - private CuratorFrameworkFactory.Builder getBuilder(HAConfiguration.ZookeeperProperties zookeeperProperties) { - return CuratorFrameworkFactory.builder() - .connectString(zookeeperProperties.getConnectString()) - .sessionTimeoutMs(zookeeperProperties.getSessionTimeout()) - .retryPolicy(new ExponentialBackoffRetry(zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); - } } diff --git a/webapp/src/main/java/org/apache/atlas/web/service/EmbeddedServer.java b/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java old mode 100755 new mode 100644 similarity index 77% rename from webapp/src/main/java/org/apache/atlas/web/service/EmbeddedServer.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java index 37165c44b5..a0254d9d7c --- a/webapp/src/main/java/org/apache/atlas/web/service/EmbeddedServer.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java @@ -15,16 +15,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.AtlasErrorCode; -import org.apache.atlas.RequestContext; import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.model.audit.AtlasAuditEntry; -import org.apache.atlas.repository.audit.AtlasAuditService; -import org.apache.atlas.util.BeanUtil; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; @@ -48,12 +43,11 @@ public class EmbeddedServer { public static final Logger LOG = LoggerFactory.getLogger(EmbeddedServer.class); public static final String ATLAS_DEFAULT_BIND_ADDRESS = "0.0.0.0"; + public static final String REST_DEFAULT_BIND_ADDRESS = "0.0.0.0"; + public static final String DEFAULT_BIND_ADDRESS = ATLAS_DEFAULT_BIND_ADDRESS; public static final Date SERVER_START_TIME = new Date(); - protected final Server server; - - private AtlasAuditService auditService; - private ServiceState serviceState; + public final Server server; public EmbeddedServer(String host, int port, String path) throws IOException { int queueSize = AtlasConfiguration.WEBSERVER_QUEUE_SIZE.getInt(); @@ -78,17 +72,14 @@ public EmbeddedServer(String host, int port, String path) throws IOException { public static EmbeddedServer newServer(String host, int port, String path, boolean secure) throws IOException { if (secure) { return new SecureEmbeddedServer(host, port, path); - } else { - return new EmbeddedServer(host, port, path); } + + return new EmbeddedServer(host, port, path); } public void start() throws AtlasBaseException { try { server.start(); - - auditServerStatus(); - server.join(); } catch (Exception e) { throw new AtlasBaseException(AtlasErrorCode.EMBEDDED_SERVER_START, e); @@ -130,25 +121,4 @@ protected Connector getConnector(String host, int port) throws IOException { return connector; } - - private void auditServerStatus() { - auditService = BeanUtil.getBean(AtlasAuditService.class); - serviceState = BeanUtil.getBean(ServiceState.class); - - ServiceState.ServiceStateValue serviceStateValue = serviceState.getState(); - - if (serviceStateValue == ServiceState.ServiceStateValue.ACTIVE) { - Date date = new Date(); - - try { - auditService.add(AtlasAuditEntry.AuditOperation.SERVER_START, SERVER_START_TIME, date, null, null, 0); - auditService.add(AtlasAuditEntry.AuditOperation.SERVER_STATE_ACTIVE, date, date, null, null, 0); - } catch (AtlasBaseException e) { - LOG.error("Exception occurred during audit", e); - } finally { - // After server related audits are added, the request created and now cleared here. - RequestContext.clear(); - } - } - } } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilityProperties.java b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilityProperties.java new file mode 100644 index 0000000000..f6caf4f85a --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilityProperties.java @@ -0,0 +1,79 @@ +/** + * 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.atlas.server.common.service; + +/** + * ZooKeeper and HA connection parameters in a form shared code can use without importing + * webapp-specific or rest-notification-specific configuration classes. + */ +public class HighAvailabilityProperties { + private final String connectString; + private final String zkRoot; + private final int retriesSleepTimeMillis; + private final int numRetries; + private final int sessionTimeout; + private final String acl; + private final String auth; + + public HighAvailabilityProperties(String connectString, String zkRoot, int retriesSleepTimeMillis, int numRetries, + int sessionTimeout, String acl, String auth) { + this.connectString = connectString; + this.zkRoot = zkRoot; + this.retriesSleepTimeMillis = retriesSleepTimeMillis; + this.numRetries = numRetries; + this.sessionTimeout = sessionTimeout; + this.acl = acl; + this.auth = auth; + } + + public String getConnectString() { + return connectString; + } + + public String getZkRoot() { + return zkRoot; + } + + public int getRetriesSleepTimeMillis() { + return retriesSleepTimeMillis; + } + + public int getNumRetries() { + return numRetries; + } + + public int getSessionTimeout() { + return sessionTimeout; + } + + public String getAcl() { + return acl; + } + + public String getAuth() { + return auth; + } + + public boolean hasAcl() { + return acl != null; + } + + public boolean hasAuth() { + return auth != null; + } +} diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java new file mode 100644 index 0000000000..df046a0b82 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java @@ -0,0 +1,88 @@ +/** + * 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.atlas.server.common.service; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.ha.AtlasServerIdSelector; +import org.apache.atlas.ha.HAConfiguration; +import org.apache.commons.configuration.Configuration; + +/** + * Interface to abstract High Availability (HA) configuration retrieval. + * Enables shared services in 'server-common' to operate without direct + * dependencies on application-specific configuration classes. + */ +public interface HighAvailabilitySupport { + + /** + * Determines if HA mode is active based on the provided configuration. + */ + boolean isHAEnabled(Configuration configuration); + + /** + * Resolves the unique ID for the current server instance. + * @throws AtlasException if the server ID cannot be resolved. + */ + String selectServerId(Configuration configuration) throws AtlasException; + + /** + * Retrieves the network address bound to a specific server ID. + */ + String getBoundAddressForId(Configuration configuration, String serverId); + + /** + * Extracts ZooKeeper connection and properties required. + */ + HighAvailabilityProperties getZookeeperProperties(Configuration configuration); + + /** + * {@link HAConfiguration}-backed implementation for unit tests and call sites + * that need a default without Spring (same contract as the webapp + * {@code @Component} adapter). + */ + final class AtlasConfigurationDefaults implements HighAvailabilitySupport { + @Override + public boolean isHAEnabled(Configuration configuration) { + return HAConfiguration.isHAEnabled(configuration); + } + + @Override + public String selectServerId(Configuration configuration) throws AtlasException { + return AtlasServerIdSelector.selectServerId(configuration); + } + + @Override + public String getBoundAddressForId(Configuration configuration, String serverId) { + return HAConfiguration.getBoundAddressForId(configuration, serverId); + } + + @Override + public HighAvailabilityProperties getZookeeperProperties(Configuration configuration) { + HAConfiguration.ZookeeperProperties p = HAConfiguration.getZookeeperProperties(configuration); + + return new HighAvailabilityProperties( + p.getConnectString(), + p.getZkRoot(), + p.getRetriesSleepTimeMillis(), + p.getNumRetries(), + p.getSessionTimeout(), + p.getAcl(), + p.getAuth()); + } + } +} diff --git a/webapp/src/main/java/org/apache/atlas/web/service/SecureEmbeddedServer.java b/server-common/src/main/java/org/apache/atlas/server/common/service/SecureEmbeddedServer.java old mode 100755 new mode 100644 similarity index 99% rename from webapp/src/main/java/org/apache/atlas/web/service/SecureEmbeddedServer.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/SecureEmbeddedServer.java index c898861d57..ac81bcf8ca --- a/webapp/src/main/java/org/apache/atlas/web/service/SecureEmbeddedServer.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/SecureEmbeddedServer.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConfiguration; diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceMetricsHook.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceMetricsHook.java new file mode 100644 index 0000000000..3a01a86221 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceMetricsHook.java @@ -0,0 +1,30 @@ +/** + * 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.atlas.server.common.service; + +/** + * Reacts to server lifecycle events; WARs may register beans (e.g. delegating to {@code AtlasMetricsUtil}). + */ +public interface ServiceMetricsHook { + + default void onServerStart() { + } + + default void onServerActivation() { + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java similarity index 62% rename from rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java index 60373f5036..0be60ac961 100644 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/service/ServiceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java @@ -6,30 +6,28 @@ * 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. + * 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.atlas.server.common.service; -package org.apache.atlas.notification.rest.web.service; - -import com.google.common.base.Preconditions; -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.atlas.notification.rest.RestHAConfiguration; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; +import javax.inject.Inject; import javax.inject.Singleton; +import static com.google.common.base.Preconditions.checkState; import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; /** @@ -40,30 +38,21 @@ */ @Singleton @Component -public class ServiceState { +public class ServiceState implements ServiceStateProvider { private static final Logger LOG = LoggerFactory.getLogger(ServiceState.class); - public enum ServiceStateValue { - ACTIVE, - PASSIVE, - BECOMING_ACTIVE, - BECOMING_PASSIVE, - MIGRATING - } + private final Configuration configuration; + private final HighAvailabilitySupport haSupport; + private volatile ServiceStateValue state; - private Configuration configuration; - private volatile ServiceStateValue state; - - public ServiceState() throws AtlasException { - this(ApplicationProperties.get()); - } - - public ServiceState(Configuration configuration) { + @Inject + public ServiceState(Configuration configuration, HighAvailabilitySupport haSupport) { this.configuration = configuration; + this.haSupport = haSupport; - state = !RestHAConfiguration.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; + state = !haSupport.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; - if(!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { + if (!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { state = ServiceStateValue.MIGRATING; } } @@ -77,13 +66,6 @@ public void becomingActive() { setState(ServiceStateValue.BECOMING_ACTIVE); } - private void setState(ServiceStateValue newState) { - Preconditions.checkState(RestHAConfiguration.isHAEnabled(configuration), "Cannot change state as requested, as HA is not enabled for this instance."); - - state = newState; - - } - public void setActive() { LOG.warn("Instance is active from {}", state); setState(ServiceStateValue.ACTIVE); @@ -99,10 +81,11 @@ public void setPassive() { setState(ServiceStateValue.PASSIVE); } + @Override public boolean isInstanceInTransition() { - ServiceStateValue state = getState(); - return state == ServiceStateValue.BECOMING_ACTIVE - || state == ServiceStateValue.BECOMING_PASSIVE; + ServiceStateValue currentState = getState(); + + return currentState == ServiceStateValue.BECOMING_ACTIVE || currentState == ServiceStateValue.BECOMING_PASSIVE; } public void setMigration() { @@ -110,7 +93,33 @@ public void setMigration() { setState(ServiceStateValue.MIGRATING); } + @Override public boolean isInstanceInMigration() { return getState() == ServiceStateValue.MIGRATING; } + + @Override + public boolean isActive() { + return getState() == ServiceStateValue.ACTIVE; + } + + @Override + public String getStateName() { + return getState().toString(); + } + + private void setState(ServiceStateValue newState) { + checkState(haSupport.isHAEnabled(configuration), + "Cannot change state as requested, as HA is not enabled for this instance."); + + state = newState; + } + + public enum ServiceStateValue { + ACTIVE, + PASSIVE, + BECOMING_ACTIVE, + BECOMING_PASSIVE, + MIGRATING + } } diff --git a/webapp/src/main/java/org/apache/atlas/web/service/UserService.java b/server-common/src/main/java/org/apache/atlas/server/common/service/UserService.java similarity index 96% rename from webapp/src/main/java/org/apache/atlas/web/service/UserService.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/UserService.java index 900163fe06..ab1eb820b3 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/UserService.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/UserService.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import org.apache.atlas.server.common.dao.UserDao; import org.springframework.security.core.userdetails.UserDetails; diff --git a/webapp/src/main/java/org/apache/atlas/Atlas.java b/webapp/src/main/java/org/apache/atlas/Atlas.java index 07f4c30fab..d7f94624c7 100755 --- a/webapp/src/main/java/org/apache/atlas/Atlas.java +++ b/webapp/src/main/java/org/apache/atlas/Atlas.java @@ -19,7 +19,7 @@ package org.apache.atlas; import org.apache.atlas.security.SecurityProperties; -import org.apache.atlas.web.service.EmbeddedServer; +import org.apache.atlas.server.common.service.EmbeddedServer; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.GnuParser; import org.apache.commons.cli.Option; diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java index a58bf71437..61155785bf 100644 --- a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java +++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java @@ -74,7 +74,7 @@ import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityUpdateRequest; import org.apache.atlas.server.common.filters.AuditFilter; import org.apache.atlas.server.common.filters.AuditFilter.AuditLog; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.map.PassiveExpiringMap; diff --git a/webapp/src/main/java/org/apache/atlas/web/ha/WebappHighAvailabilitySupport.java b/webapp/src/main/java/org/apache/atlas/web/ha/WebappHighAvailabilitySupport.java new file mode 100644 index 0000000000..ad24fafa68 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/ha/WebappHighAvailabilitySupport.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.atlas.web.ha; + +import org.apache.atlas.AtlasException; +import org.apache.atlas.ha.AtlasServerIdSelector; +import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; +import org.apache.commons.configuration.Configuration; +import org.springframework.stereotype.Component; + +/** + * Webapp implementation of {@link HighAvailabilitySupport} using {@link HAConfiguration}. + */ +@Component +public class WebappHighAvailabilitySupport implements HighAvailabilitySupport { + @Override + public boolean isHAEnabled(Configuration configuration) { + return HAConfiguration.isHAEnabled(configuration); + } + + @Override + public String selectServerId(Configuration configuration) throws AtlasException { + return AtlasServerIdSelector.selectServerId(configuration); + } + + @Override + public String getBoundAddressForId(Configuration configuration, String serverId) { + return HAConfiguration.getBoundAddressForId(configuration, serverId); + } + + @Override + public HighAvailabilityProperties getZookeeperProperties(Configuration configuration) { + HAConfiguration.ZookeeperProperties props = HAConfiguration.getZookeeperProperties(configuration); + + return new HighAvailabilityProperties( + props.getConnectString(), + props.getZkRoot(), + props.getRetriesSleepTimeMillis(), + props.getNumRetries(), + props.getSessionTimeout(), + props.getAcl(), + props.getAuth()); + } +} diff --git a/webapp/src/main/java/org/apache/atlas/web/metrics/WebappServiceMetricsHook.java b/webapp/src/main/java/org/apache/atlas/web/metrics/WebappServiceMetricsHook.java new file mode 100644 index 0000000000..c2a5e00340 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/metrics/WebappServiceMetricsHook.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.atlas.web.metrics; + +import org.apache.atlas.server.common.service.ServiceMetricsHook; +import org.apache.atlas.util.AtlasMetricsUtil; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +/** + * Bridges {@link ServiceMetricsHook} to {@link AtlasMetricsUtil} in the main webapp. + */ +@Component +public class WebappServiceMetricsHook implements ServiceMetricsHook { + private final AtlasMetricsUtil metricsUtil; + + @Inject + public WebappServiceMetricsHook(AtlasMetricsUtil metricsUtil) { + this.metricsUtil = metricsUtil; + } + + @Override + public void onServerStart() { + metricsUtil.onServerStart(); + } + + @Override + public void onServerActivation() { + metricsUtil.onServerActivation(); + } +} diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java index 460e8888b9..b778ade379 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java @@ -79,7 +79,7 @@ import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java index cfc22e8d78..d6cdb639be 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java @@ -19,8 +19,8 @@ import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; -import org.apache.atlas.web.service.ActiveInstanceState; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.ServiceState; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java b/webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java deleted file mode 100644 index b2e7dd705a..0000000000 --- a/webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java +++ /dev/null @@ -1,149 +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.atlas.web.service; - -import org.apache.atlas.ApplicationProperties; -import org.apache.atlas.AtlasException; -import org.apache.atlas.RequestContext; -import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.ha.HAConfiguration; -import org.apache.atlas.model.audit.AtlasAuditEntry; -import org.apache.atlas.repository.audit.AtlasAuditService; -import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; - -import javax.inject.Singleton; - -import java.util.Date; - -import static com.google.common.base.Preconditions.checkState; -import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; - -/** - * A class that maintains the state of this instance. - * - * The states are maintained at a granular level, including in-transition states. The transitions are - * directed by {@link ActiveInstanceElectorService}. - */ -@Singleton -@Component -public class ServiceState { - private static final Logger LOG = LoggerFactory.getLogger(ServiceState.class); - - @Autowired - AtlasAuditService auditService; - - private final Configuration configuration; - - private volatile ServiceStateValue state; - - public ServiceState() throws AtlasException { - this(ApplicationProperties.get()); - } - - public ServiceState(Configuration configuration) { - this.configuration = configuration; - - state = !HAConfiguration.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; - - if (!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { - state = ServiceStateValue.MIGRATING; - } - } - - public ServiceStateValue getState() { - return state; - } - - private void setState(ServiceStateValue newState) { - checkState(HAConfiguration.isHAEnabled(configuration), "Cannot change state as requested, as HA is not enabled for this instance."); - - state = newState; - - auditServerStatus(); - } - - public void becomingActive() { - LOG.warn("Instance becoming active from {}", state); - - setState(ServiceStateValue.BECOMING_ACTIVE); - } - - public void setActive() { - LOG.warn("Instance is active from {}", state); - - setState(ServiceStateValue.ACTIVE); - } - - public void becomingPassive() { - LOG.warn("Instance becoming passive from {}", state); - - setState(ServiceStateValue.BECOMING_PASSIVE); - } - - public void setPassive() { - LOG.warn("Instance is passive from {}", state); - - setState(ServiceStateValue.PASSIVE); - } - - public boolean isInstanceInTransition() { - ServiceStateValue state = getState(); - - return state == ServiceStateValue.BECOMING_ACTIVE || state == ServiceStateValue.BECOMING_PASSIVE; - } - - public void setMigration() { - LOG.warn("Instance in {}", state); - - setState(ServiceStateValue.MIGRATING); - } - - public boolean isInstanceInMigration() { - return getState() == ServiceStateValue.MIGRATING; - } - - private void auditServerStatus() { - if (state == ServiceState.ServiceStateValue.ACTIVE) { - Date date = new Date(); - - try { - auditService.add(AtlasAuditEntry.AuditOperation.SERVER_START, EmbeddedServer.SERVER_START_TIME, date, null, null, 0); - auditService.add(AtlasAuditEntry.AuditOperation.SERVER_STATE_ACTIVE, date, date, null, null, 0); - } catch (AtlasBaseException e) { - LOG.error("Exception occurred during audit", e); - } finally { - // In HA environment, after the server related audits are added, the request created are now cleared. - RequestContext.clear(); - } - } - } - - public enum ServiceStateValue { - ACTIVE, - PASSIVE, - BECOMING_ACTIVE, - BECOMING_PASSIVE, - MIGRATING - } -} diff --git a/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java b/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java index b7f5ec4a39..a02243ece0 100644 --- a/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java +++ b/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java @@ -26,8 +26,8 @@ import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.setup.SetupException; import org.apache.atlas.setup.SetupStep; -import org.apache.atlas.web.service.AtlasZookeeperSecurityProperties; -import org.apache.atlas.web.service.CuratorFactory; +import org.apache.atlas.server.common.service.AtlasZookeeperSecurityProperties; +import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java index 529d894ffa..44d8542440 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java @@ -40,7 +40,7 @@ import org.apache.atlas.util.AtlasMetricsUtil; import org.apache.atlas.v1.model.instance.Referenceable; import org.apache.atlas.v1.model.notification.HookNotificationV1; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.RandomStringUtils; import org.apache.kafka.clients.consumer.KafkaConsumer; diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java index 9da891f1d1..0865d82cb3 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java @@ -57,7 +57,7 @@ import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityCreateRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityDeleteRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityUpdateRequest; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.kafka.common.TopicPartition; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java index 95fbc3011b..19268cdded 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java @@ -20,8 +20,8 @@ import org.apache.atlas.server.common.filters.ActiveServerFilter; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; -import org.apache.atlas.web.service.ActiveInstanceState; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.ServiceState; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java index c8571b4f6f..89d86ffdc2 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java @@ -18,7 +18,7 @@ import org.apache.atlas.RequestContext; import org.apache.atlas.web.security.BaseSecurityTest; -import org.apache.atlas.web.service.EmbeddedServer; +import org.apache.atlas.server.common.service.EmbeddedServer; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.hadoop.hdfs.web.URLConnectionFactory; diff --git a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java index 9b578a9986..069b74a18f 100644 --- a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java @@ -67,7 +67,7 @@ import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/BaseSSLAndKerberosTest.java b/webapp/src/test/java/org/apache/atlas/web/security/BaseSSLAndKerberosTest.java index dc7503e052..07d26bcf25 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/BaseSSLAndKerberosTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/BaseSSLAndKerberosTest.java @@ -17,7 +17,7 @@ package org.apache.atlas.web.security; import org.apache.atlas.security.SecurityProperties; -import org.apache.atlas.web.service.SecureEmbeddedServer; +import org.apache.atlas.server.common.service.SecureEmbeddedServer; import org.apache.commons.io.FileUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java b/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java index 5ac970a345..8271cd6caa 100755 --- a/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java @@ -20,7 +20,7 @@ import org.apache.atlas.AtlasClient; import org.apache.atlas.web.TestUtils; -import org.apache.atlas.web.service.SecureEmbeddedServer; +import org.apache.atlas.server.common.service.SecureEmbeddedServer; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceElectorServiceTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceElectorServiceTest.java index 9efbc63f2c..f546cd8568 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceElectorServiceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceElectorServiceTest.java @@ -18,12 +18,17 @@ package org.apache.atlas.web.service; -import org.apache.atlas.AtlasConstants; import org.apache.atlas.AtlasException; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.listener.ActiveStateChangeHandler; -import org.apache.atlas.util.AtlasMetricsUtil; +import org.apache.atlas.server.common.service.ActiveInstanceElectorService; +import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.CuratorFactory; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; +import org.apache.atlas.server.common.service.ServiceMetricsHook; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.mockito.InOrder; @@ -33,17 +38,22 @@ import org.testng.annotations.Test; import java.io.IOException; +import java.util.Collections; import java.util.HashSet; import java.util.Set; +import static org.mockito.Matchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; public class ActiveInstanceElectorServiceTest { + private static final String DEFAULT_ZK_ROOT = HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT; + @Mock private Configuration configuration; @@ -57,137 +67,120 @@ public class ActiveInstanceElectorServiceTest { private ServiceState serviceState; @Mock - private AtlasMetricsUtil metricsUtil; + private HighAvailabilitySupport haSupport; + + @Mock + private HighAvailabilityProperties haProperties; + + @Mock + private ServiceMetricsHook metricsHook; @BeforeMethod - public void setup() { - System.setProperty(AtlasConstants.SYSTEM_PROPERTY_APP_PORT, "21000"); + public void setup() throws Exception { MockitoAnnotations.initMocks(this); + when(haSupport.isHAEnabled(any(Configuration.class))).thenReturn(true); + when(haSupport.selectServerId(any(Configuration.class))).thenReturn("id1"); + when(haSupport.getZookeeperProperties(any(Configuration.class))).thenReturn(haProperties); + when(haProperties.getZkRoot()).thenReturn(DEFAULT_ZK_ROOT); + } + + private ActiveInstanceElectorService newElector(Set handlers) { + return new ActiveInstanceElectorService( + configuration, + handlers, + Collections.singletonList(metricsHook), + curatorFactory, + activeInstanceState, + serviceState, + haSupport); } @Test public void testLeaderElectionIsJoinedOnStart() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); verify(leaderLatch).start(); } @Test public void testListenerIsAddedForActiveInstanceCallbacks() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); - activeInstanceElectorService.start(); - - verify(leaderLatch).addListener(activeInstanceElectorService); + verify(leaderLatch).addListener(service); } @Test public void testLeaderElectionIsNotStartedIfNotInHAMode() throws AtlasException { + reset(haSupport); + when(haSupport.isHAEnabled(any(Configuration.class))).thenReturn(false); when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + verify(metricsHook).onServerStart(); + verify(metricsHook).onServerActivation(); verifyZeroInteractions(curatorFactory); } @Test public void testLeaderElectionIsLeftOnStop() throws IOException, AtlasException { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.stop(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + service.stop(); verify(leaderLatch).close(); } @Test public void testCuratorFactoryIsClosedOnStop() throws AtlasException { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.stop(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + service.stop(); verify(curatorFactory).close(); } @Test public void testNoActionOnStopIfHAModeIsDisabled() { + reset(haSupport); + when(haSupport.isHAEnabled(any(Configuration.class))).thenReturn(false); when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.stop(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.stop(); verifyZeroInteractions(curatorFactory); } @Test public void testRegisteredHandlersAreNotifiedWhenInstanceIsActive() throws AtlasException { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - Set changeHandlers = new HashSet<>(); - final ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); - final ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); - + Set changeHandlers = new HashSet(); + ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); + ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); changeHandlers.add(handler1); changeHandlers.add(handler2); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, changeHandlers, curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(changeHandlers); + service.start(); + service.isLeader(); verify(handler1).instanceIsActive(); verify(handler2).instanceIsActive(); @@ -195,49 +188,31 @@ public void testRegisteredHandlersAreNotifiedWhenInstanceIsActive() throws Atlas @Test public void testSharedStateIsUpdatedWhenInstanceIsActive() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + service.isLeader(); verify(activeInstanceState).update("id1"); } @Test public void testRegisteredHandlersAreNotifiedOfPassiveWhenStateUpdateFails() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - Set changeHandlers = new HashSet<>(); - final ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); - final ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); - + Set changeHandlers = new HashSet(); + ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); + ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); changeHandlers.add(handler1); changeHandlers.add(handler2); - doThrow(new AtlasBaseException()).when(activeInstanceState).update("id1"); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, changeHandlers, curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(changeHandlers); + service.start(); + service.isLeader(); verify(handler1).instanceIsPassive(); verify(handler2).instanceIsPassive(); @@ -245,54 +220,35 @@ public void testRegisteredHandlersAreNotifiedOfPassiveWhenStateUpdateFails() thr @Test public void testElectionIsRejoinedWhenStateUpdateFails() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); - - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); doThrow(new AtlasBaseException()).when(activeInstanceState).update("id1"); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + service.isLeader(); InOrder inOrder = inOrder(leaderLatch, curatorFactory); - inOrder.verify(leaderLatch).close(); - inOrder.verify(curatorFactory).leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - inOrder.verify(leaderLatch).addListener(activeInstanceElectorService); + inOrder.verify(curatorFactory).leaderLatchInstance("id1", DEFAULT_ZK_ROOT); + inOrder.verify(leaderLatch).addListener(service); inOrder.verify(leaderLatch).start(); } @Test public void testRegisteredHandlersAreNotifiedOfPassiveWhenInstanceIsPassive() throws AtlasException { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - - Set changeHandlers = new HashSet<>(); - final ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); - final ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); - + Set changeHandlers = new HashSet(); + ActiveStateChangeHandler handler1 = mock(ActiveStateChangeHandler.class); + ActiveStateChangeHandler handler2 = mock(ActiveStateChangeHandler.class); changeHandlers.add(handler1); changeHandlers.add(handler2); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, changeHandlers, curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.notLeader(); + ActiveInstanceElectorService service = newElector(changeHandlers); + service.start(); + service.notLeader(); verify(handler1).instanceIsPassive(); verify(handler2).instanceIsPassive(); @@ -300,51 +256,51 @@ public void testRegisteredHandlersAreNotifiedOfPassiveWhenInstanceIsPassive() th @Test public void testActiveStateSetOnBecomingLeader() { - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.isLeader(); InOrder inOrder = inOrder(serviceState); - inOrder.verify(serviceState).becomingActive(); inOrder.verify(serviceState).setActive(); } @Test public void testPassiveStateSetOnLoosingLeadership() { - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.notLeader(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.notLeader(); InOrder inOrder = inOrder(serviceState); - inOrder.verify(serviceState).becomingPassive(); inOrder.verify(serviceState).setPassive(); } @Test public void testPassiveStateSetIfActivationFails() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getStringArray(HAConfiguration.ATLAS_SERVER_IDS)).thenReturn(new String[] {"id1"}); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_ADDRESS_PREFIX + "id1")).thenReturn("127.0.0.1:21000"); - when(configuration.getString(HAConfiguration.ATLAS_SERVER_HA_ZK_ROOT_KEY, HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT); - LeaderLatch leaderLatch = mock(LeaderLatch.class); - - when(curatorFactory.leaderLatchInstance("id1", HAConfiguration.ATLAS_SERVER_ZK_ROOT_DEFAULT)).thenReturn(leaderLatch); - + when(curatorFactory.leaderLatchInstance("id1", DEFAULT_ZK_ROOT)).thenReturn(leaderLatch); doThrow(new AtlasBaseException()).when(activeInstanceState).update("id1"); - ActiveInstanceElectorService activeInstanceElectorService = new ActiveInstanceElectorService(configuration, new HashSet<>(), curatorFactory, activeInstanceState, serviceState, metricsUtil); - - activeInstanceElectorService.start(); - activeInstanceElectorService.isLeader(); + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + service.isLeader(); InOrder inOrder = inOrder(serviceState); - inOrder.verify(serviceState).becomingActive(); inOrder.verify(serviceState).becomingPassive(); inOrder.verify(serviceState).setPassive(); } + + @Test + public void testLeaderLatchUsesCustomZkRootFromHaProperties() throws Exception { + String customRoot = "/atlas-custom-zk"; + when(haProperties.getZkRoot()).thenReturn(customRoot); + LeaderLatch leaderLatch = mock(LeaderLatch.class); + when(curatorFactory.leaderLatchInstance("id1", customRoot)).thenReturn(leaderLatch); + + ActiveInstanceElectorService service = newElector(new HashSet()); + service.start(); + + verify(curatorFactory).leaderLatchInstance("id1", customRoot); + verify(leaderLatch).start(); + } } diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java index 68f7ee7559..cf64d11163 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java @@ -19,6 +19,9 @@ package org.apache.atlas.web.service; import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; +import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CreateBuilder; @@ -84,7 +87,7 @@ public void testSharedPathIsCreatedIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); activeInstanceState.update("id1"); @@ -118,7 +121,7 @@ public void testSharedPathIsCreatedWithRightACLIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); activeInstanceState.update("id1"); @@ -141,7 +144,7 @@ public void testDataIsUpdatedWithAtlasServerAddress() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); activeInstanceState.update("id1"); @@ -158,7 +161,7 @@ public void testShouldReturnActiveServerAddress() throws Exception { when(curatorFramework.getData()).thenReturn(getDataBuilder); when(getDataBuilder.forPath(getPath())).thenReturn(SERVER_ADDRESS.getBytes(StandardCharsets.UTF_8)); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); String actualServerAddress = activeInstanceState.getActiveServerAddress(); assertEquals(actualServerAddress, SERVER_ADDRESS); @@ -174,7 +177,7 @@ public void testShouldHandleExceptionsInFetchingServerAddress() throws Exception when(curatorFramework.getData()).thenReturn(getDataBuilder); when(getDataBuilder.forPath(getPath())).thenThrow(new Exception()); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); assertNull(activeInstanceState.getActiveServerAddress()); } diff --git a/webapp/src/test/java/org/apache/atlas/web/service/AtlasZookeeperSecurityPropertiesTest.java b/webapp/src/test/java/org/apache/atlas/web/service/AtlasZookeeperSecurityPropertiesTest.java index d225a7e2ce..7f3e43ae54 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/AtlasZookeeperSecurityPropertiesTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/AtlasZookeeperSecurityPropertiesTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.service; import com.google.common.base.Charsets; +import org.apache.atlas.server.common.service.AtlasZookeeperSecurityProperties; import org.apache.curator.framework.AuthInfo; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java b/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java index 58aef3e0c3..b63558ebfc 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java @@ -19,30 +19,25 @@ package org.apache.atlas.web.service; import com.google.common.base.Charsets; -import org.apache.atlas.AtlasException; -import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.server.common.service.CuratorFactory; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; import org.apache.commons.configuration.Configuration; -import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.ACLProvider; -import org.apache.curator.framework.recipes.leader.LeaderLatch; -import org.apache.curator.framework.recipes.locks.InterProcessMutex; -import org.apache.zookeeper.data.ACL; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import java.lang.reflect.Field; import java.lang.reflect.Method; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -53,403 +48,186 @@ public class CuratorFactoryTest { private Configuration configuration; @Mock - private HAConfiguration.ZookeeperProperties zookeeperProperties; + private HighAvailabilitySupport haSupport; @Mock - private CuratorFrameworkFactory.Builder builder; + private HighAvailabilityProperties zookeeperProperties; @Mock - private CuratorFramework curatorFramework; + private CuratorFrameworkFactory.Builder builder; @BeforeMethod public void setup() { MockitoAnnotations.initMocks(this); } + private CuratorFactory buildCuratorFactory() { + return new CuratorFactory(configuration, haSupport) { + @Override + protected void initializeCuratorFramework() { + } + }; + } + + private void invokeEnhance(CuratorFactory curatorFactory) throws Exception { + Method method = CuratorFactory.class.getDeclaredMethod("enhanceBuilderWithSecurityParameters", + HighAvailabilityProperties.class, CuratorFrameworkFactory.Builder.class); + method.setAccessible(true); + method.invoke(curatorFactory, zookeeperProperties, builder); + } + @Test - public void shouldAddAuthorization() { + public void shouldAddAuthorization() throws Exception { when(zookeeperProperties.hasAcl()).thenReturn(true); when(zookeeperProperties.getAcl()).thenReturn("sasl:myclient@EXAMPLE.COM"); when(zookeeperProperties.hasAuth()).thenReturn(true); when(zookeeperProperties.getAuth()).thenReturn("sasl:myclient@EXAMPLE.COM"); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + CuratorFactory curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); verify(builder).aclProvider(any(ACLProvider.class)); verify(builder).authorization(eq("sasl"), eq("myclient@EXAMPLE.COM".getBytes(Charsets.UTF_8))); } @Test - public void shouldAddAclProviderWithRightACL() { + public void shouldAddAclProviderWithRightACL() throws Exception { when(zookeeperProperties.hasAcl()).thenReturn(true); when(zookeeperProperties.getAcl()).thenReturn("sasl:myclient@EXAMPLE.COM"); when(zookeeperProperties.hasAuth()).thenReturn(false); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + CuratorFactory curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); - verify(builder).aclProvider(argThat(aclProvider -> { - ACL acl = aclProvider.getDefaultAcl().get(0); - - return acl.getId().getId().equals("myclient@EXAMPLE.COM") && acl.getId().getScheme().equals("sasl"); - })); + verify(builder).aclProvider(any(ACLProvider.class)); } @Test - public void shouldNotAddAnySecureParameters() { + public void shouldNotAddAnySecureParameters() throws Exception { when(zookeeperProperties.hasAcl()).thenReturn(false); when(zookeeperProperties.hasAuth()).thenReturn(false); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + CuratorFactory curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); - verify(builder, never()).aclProvider(any(ACLProvider.class)); - verify(builder, never()).authorization(anyString(), any(byte[].class)); + verifyNoInteractions(builder); } @Test - public void testDefaultConstructor() throws AtlasException { - CuratorFactory curatorFactory = new CuratorFactory() { - @Override - protected void initializeCuratorFramework() { - } - }; + public void testDefaultConstructor() { + CuratorFactory curatorFactory = buildCuratorFactory(); assertNotNull(curatorFactory); } - @Test - public void testClientInstance() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(true); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - try { - Field field = CuratorFactory.class.getDeclaredField("curatorFramework"); - field.setAccessible(true); - field.set(this, curatorFramework); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - - CuratorFramework result = curatorFactory.clientInstance(); - assertEquals(result, curatorFramework); - } - - @Test - public void testLeaderLatchInstance() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(true); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - try { - Field field = CuratorFactory.class.getDeclaredField("curatorFramework"); - field.setAccessible(true); - field.set(this, curatorFramework); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - - String serverId = "server1"; - String zkRoot = "/test"; - - LeaderLatch leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); - assertNotNull(leaderLatch); - } - - @Test - public void testLockInstance() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(true); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - try { - Field field = CuratorFactory.class.getDeclaredField("curatorFramework"); - field.setAccessible(true); - field.set(this, curatorFramework); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - - String zkRoot = "/test"; - - InterProcessMutex mutex = curatorFactory.lockInstance(zkRoot); - assertNotNull(mutex); - } - - @Test - public void testClose() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(true); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - try { - Field field = CuratorFactory.class.getDeclaredField("curatorFramework"); - field.setAccessible(true); - field.set(this, curatorFramework); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - - doNothing().when(curatorFramework).close(); - curatorFactory.close(); - verify(curatorFramework).close(); - } - - @Test - public void testConstructorDoesNotInitializeWhenHAKeyIsMissing() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(false); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - throw new AssertionError("initializeCuratorFramework() should not be called when HA key is missing"); - } - }; - - assertNull(curatorFactory.clientInstance()); - } - - @Test - public void testConstructorDoesNotInitializeWhenHAIsDisabled() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(false); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - throw new AssertionError("initializeCuratorFramework() should not be called when HA is disabled"); - } - }; - - assertNull(curatorFactory.clientInstance()); - } - - @Test - public void testConstructorInitializesWhenHAIsEnabled() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)).thenReturn(true); - - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - try { - Field field = CuratorFactory.class.getDeclaredField("curatorFramework"); - field.setAccessible(true); - field.set(this, curatorFramework); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - }; - - assertEquals(curatorFactory.clientInstance(), curatorFramework); - } - @Test public void testGetIdForLoggingSaslScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "sasl", "user@EXAMPLE.COM"); assertEquals(result, "user@EXAMPLE.COM"); } @Test public void testGetIdForLoggingIpScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "ip", "192.168.1.1"); assertEquals(result, "192.168.1.1"); } @Test public void testGetIdForLoggingWorldScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "world", "anyone"); assertEquals(result, "anyone"); } @Test public void testGetIdForLoggingAuthScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "auth", "user:password"); assertEquals(result, "user"); } @Test public void testGetIdForLoggingDigestScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "digest", "user:password"); assertEquals(result, "user"); } @Test public void testGetIdForLoggingUnknownScheme() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getIdForLogging", String.class, String.class); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory, "unknown", "somevalue"); assertEquals(result, "unknown"); } @Test public void testGetCurrentUser() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); Method method = CuratorFactory.class.getDeclaredMethod("getCurrentUser"); method.setAccessible(true); - String result = (String) method.invoke(curatorFactory); assertNotNull(result); } @Test public void testGetAclProviderWithoutAcl() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); when(zookeeperProperties.hasAcl()).thenReturn(false); - Method method = CuratorFactory.class.getDeclaredMethod("getAclProvider", HAConfiguration.ZookeeperProperties.class); + Method method = CuratorFactory.class.getDeclaredMethod("getAclProvider", HighAvailabilityProperties.class); method.setAccessible(true); - ACLProvider result = (ACLProvider) method.invoke(curatorFactory, zookeeperProperties); assertNull(result); } @Test public void testGetBuilderMethod() throws Exception { - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - + CuratorFactory curatorFactory = buildCuratorFactory(); when(zookeeperProperties.getConnectString()).thenReturn("localhost:2181"); when(zookeeperProperties.getSessionTimeout()).thenReturn(30000); when(zookeeperProperties.getRetriesSleepTimeMillis()).thenReturn(1000); when(zookeeperProperties.getNumRetries()).thenReturn(3); - Method method = CuratorFactory.class.getDeclaredMethod("getBuilder", HAConfiguration.ZookeeperProperties.class); + Method method = CuratorFactory.class.getDeclaredMethod("getBuilder", HighAvailabilityProperties.class); method.setAccessible(true); - CuratorFrameworkFactory.Builder result = (CuratorFrameworkFactory.Builder) method.invoke(curatorFactory, zookeeperProperties); assertNotNull(result); } @Test - public void testEnhanceBuilderWithSecurityParametersWithAclOnly() { + public void testEnhanceBuilderWithSecurityParametersWithAclOnly() throws Exception { when(zookeeperProperties.hasAcl()).thenReturn(true); when(zookeeperProperties.getAcl()).thenReturn("digest:user:password"); when(zookeeperProperties.hasAuth()).thenReturn(false); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + CuratorFactory curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); verify(builder).aclProvider(any(ACLProvider.class)); verify(builder, never()).authorization(anyString(), any(byte[].class)); } @Test - public void testEnhanceBuilderWithSecurityParametersWithAuthOnly() { + public void testEnhanceBuilderWithSecurityParametersWithAuthOnly() throws Exception { when(zookeeperProperties.hasAcl()).thenReturn(false); when(zookeeperProperties.hasAuth()).thenReturn(true); when(zookeeperProperties.getAuth()).thenReturn("digest:user:password"); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { - @Override - protected void initializeCuratorFramework() { - } - }; - - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + CuratorFactory curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); verify(builder, never()).aclProvider(any(ACLProvider.class)); verify(builder, never()).authorization(anyString(), any(byte[].class)); diff --git a/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java b/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java index 6e55dbca62..095f808854 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java @@ -19,7 +19,8 @@ package org.apache.atlas.web.service; import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.repository.audit.AtlasAuditService; +import org.apache.atlas.server.common.service.EmbeddedServer; +import org.apache.atlas.server.common.service.SecureEmbeddedServer; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; @@ -34,23 +35,13 @@ import java.lang.reflect.Method; import java.util.Date; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; public class EmbeddedServerTest { - @Mock - private AtlasAuditService auditService; - - @Mock - private ServiceState serviceState; - @Mock private Server server; @@ -209,85 +200,6 @@ public void start() throws AtlasBaseException { embeddedServer.start(); } - @Test - public void testAuditServerStatusActive() throws Exception { - String host = "localhost"; - int port = 8080; - String path = "/test"; - - embeddedServer = new EmbeddedServer(host, port, path); - - // Set up mocks for audit - setFieldValue(getField(EmbeddedServer.class, "auditService"), embeddedServer, auditService); - setFieldValue(getField(EmbeddedServer.class, "serviceState"), embeddedServer, serviceState); - - when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.ACTIVE); - - Method method = EmbeddedServer.class.getDeclaredMethod("auditServerStatus"); - method.setAccessible(true); - - try { - method.invoke(embeddedServer); - // Test passes if no exception is thrown - } catch (InvocationTargetException e) { - // Expected to fail due to BeanUtil dependency in test environment - assertTrue(e.getCause() instanceof NullPointerException); - } - } - - @Test - public void testAuditServerStatusNotActive() throws Exception { - String host = "localhost"; - int port = 8080; - String path = "/test"; - - embeddedServer = new EmbeddedServer(host, port, path); - - // Set up mocks for audit - setFieldValue(getField(EmbeddedServer.class, "auditService"), embeddedServer, auditService); - setFieldValue(getField(EmbeddedServer.class, "serviceState"), embeddedServer, serviceState); - - when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.PASSIVE); - - Method method = EmbeddedServer.class.getDeclaredMethod("auditServerStatus"); - method.setAccessible(true); - - try { - method.invoke(embeddedServer); - // Test passes if no exception is thrown - } catch (InvocationTargetException e) { - // Expected to fail due to BeanUtil dependency in test environment - assertTrue(e.getCause() instanceof NullPointerException); - } - } - - @Test - public void testAuditServerStatusWithException() throws Exception { - String host = "localhost"; - int port = 8080; - String path = "/test"; - - embeddedServer = new EmbeddedServer(host, port, path); - - // Set up mocks for audit - setFieldValue(getField(EmbeddedServer.class, "auditService"), embeddedServer, auditService); - setFieldValue(getField(EmbeddedServer.class, "serviceState"), embeddedServer, serviceState); - - when(serviceState.getState()).thenReturn(ServiceState.ServiceStateValue.ACTIVE); - doThrow(new AtlasBaseException("Test exception")).when(auditService).add(any(), any(), any(), anyObject(), anyObject(), anyInt()); - - Method method = EmbeddedServer.class.getDeclaredMethod("auditServerStatus"); - method.setAccessible(true); - - try { - method.invoke(embeddedServer); - // Test passes if no exception is thrown - } catch (InvocationTargetException e) { - // Expected to fail due to BeanUtil dependency in test environment - assertTrue(e.getCause() instanceof NullPointerException); - } - } - @Test public void testConstants() { assertEquals(EmbeddedServer.ATLAS_DEFAULT_BIND_ADDRESS, "0.0.0.0"); diff --git a/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTest.java b/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTest.java index 57ab4cbec1..a63b70c089 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTest.java @@ -19,6 +19,8 @@ package org.apache.atlas.web.service; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.service.EmbeddedServer; +import org.apache.atlas.server.common.service.SecureEmbeddedServer; import org.apache.atlas.web.TestUtils; import org.apache.atlas.web.security.BaseSecurityTest; import org.apache.commons.configuration.PropertiesConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTestBase.java b/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTestBase.java index 5c209fa3f5..b14fb3df59 100755 --- a/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTestBase.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/SecureEmbeddedServerTestBase.java @@ -22,6 +22,8 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.Atlas; import org.apache.atlas.AtlasException; +import org.apache.atlas.server.common.service.EmbeddedServer; +import org.apache.atlas.server.common.service.SecureEmbeddedServer; import org.apache.atlas.web.TestUtils; import org.apache.atlas.web.integration.AdminJerseyResourceIT; import org.apache.atlas.web.integration.EntityJerseyResourceIT; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java index fa532a5c37..aaf1996902 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java @@ -19,13 +19,14 @@ package org.apache.atlas.web.service; import org.apache.atlas.AtlasConstants; -import org.apache.atlas.AtlasException; import org.apache.atlas.exception.AtlasBaseException; -import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.repository.audit.AtlasAuditService; +import org.apache.atlas.server.common.service.HighAvailabilitySupport; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.testng.SkipException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -33,10 +34,7 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyLong; -import static org.mockito.Matchers.anyObject; import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; @@ -51,235 +49,170 @@ public class ServiceStateTest { @Mock private AtlasAuditService auditService; + @Mock + private HighAvailabilitySupport haSupport; + @BeforeMethod public void setup() { MockitoAnnotations.initMocks(this); } + private ServiceState newServiceState(boolean haEnabled) { + when(haSupport.isHAEnabled(configuration)).thenReturn(haEnabled); + return new ServiceState(configuration, haSupport); + } + @Test public void testShouldBeActiveIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.ACTIVE); + ServiceState serviceState = newServiceState(false); + assertEquals(ServiceState.ServiceStateValue.ACTIVE, serviceState.getState()); } @Test(expectedExceptions = IllegalStateException.class) public void testShouldDisallowTransitionIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); serviceState.becomingPassive(); - fail("Should not allow transition"); } @Test public void testShouldChangeStateIfHAIsEnabled() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(true); serviceState.becomingPassive(); - - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.BECOMING_PASSIVE); + assertEquals(ServiceState.ServiceStateValue.BECOMING_PASSIVE, serviceState.getState()); } @Test - public void testDefaultConstructor() throws AtlasException { - ServiceState serviceState = new ServiceState(); + public void testConstructor() { + ServiceState serviceState = newServiceState(true); assertNotNull(serviceState); } @Test public void testShouldBePassiveIfHAIsEnabled() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(true); assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.PASSIVE); } @Test public void testShouldBeMigratingIfMigrationModeSet() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); when(configuration.getString(AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME, "")).thenReturn("migration.txt"); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.MIGRATING); } @Test public void testBecomingActive() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - setAuditService(serviceState, auditService); - + ServiceState serviceState = newServiceState(true); serviceState.becomingActive(); - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.BECOMING_ACTIVE); } @Test public void testSetActive() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - setAuditService(serviceState, auditService); - + ServiceState serviceState = newServiceState(true); serviceState.setActive(); - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.ACTIVE); - // Verify that audit service was called twice (SERVER_START and SERVER_STATE_ACTIVE) - verify(auditService, org.mockito.Mockito.times(2)).add(any(), any(), any(), anyObject(), anyObject(), anyLong()); } @Test public void testSetPassive() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - setAuditService(serviceState, auditService); - + ServiceState serviceState = newServiceState(true); serviceState.setPassive(); - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.PASSIVE); } @Test public void testSetMigration() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - setAuditService(serviceState, auditService); - + ServiceState serviceState = newServiceState(true); serviceState.setMigration(); - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.MIGRATING); } @Test public void testIsInstanceInTransitionBecomingActive() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); + ServiceState serviceState = newServiceState(true); serviceState.becomingActive(); - assertTrue(serviceState.isInstanceInTransition()); } @Test public void testIsInstanceInTransitionBecomingPassive() { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); + ServiceState serviceState = newServiceState(true); serviceState.becomingPassive(); - assertTrue(serviceState.isInstanceInTransition()); } @Test public void testIsInstanceInTransitionActive() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - - ServiceState serviceState = new ServiceState(configuration); - setAuditService(serviceState, auditService); + ServiceState serviceState = newServiceState(true); serviceState.setActive(); - assertFalse(serviceState.isInstanceInTransition()); } @Test public void testIsInstanceInMigration() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); when(configuration.getString(AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME, "")).thenReturn("migration.txt"); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); assertTrue(serviceState.isInstanceInMigration()); } @Test public void testIsInstanceInMigrationFalse() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); assertFalse(serviceState.isInstanceInMigration()); } @Test(expectedExceptions = IllegalStateException.class) public void testShouldDisallowBecomingActiveIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); serviceState.becomingActive(); - fail("Should not allow transition"); } @Test(expectedExceptions = IllegalStateException.class) public void testShouldDisallowSetActiveIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); serviceState.setActive(); - fail("Should not allow transition"); } @Test(expectedExceptions = IllegalStateException.class) public void testShouldDisallowSetPassiveIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); serviceState.setPassive(); - fail("Should not allow transition"); } @Test(expectedExceptions = IllegalStateException.class) public void testShouldDisallowSetMigrationIfHAIsDisabled() { - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, false)).thenReturn(false); - - ServiceState serviceState = new ServiceState(configuration); - + ServiceState serviceState = newServiceState(false); serviceState.setMigration(); - fail("Should not allow transition"); } @Test public void testAuditServerStatusWithException() throws Exception { - when(configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); - when(configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY)).thenReturn(true); + if (!hasAuditServiceField()) { + throw new SkipException("ServiceState no longer exposes auditService field in server-common design."); + } - ServiceState serviceState = new ServiceState(configuration); + ServiceState serviceState = newServiceState(true); setAuditService(serviceState, auditService); - - doThrow(new AtlasBaseException("Test exception")).when(auditService).add(any(), any(), any(), anyObject(), anyObject(), anyInt()); - + doThrow(new AtlasBaseException("Test exception")).when(auditService).add(any(), any(), any(), any(), any(), anyInt()); serviceState.setActive(); - assertEquals(serviceState.getState(), ServiceState.ServiceStateValue.ACTIVE); } + private boolean hasAuditServiceField() { + try { + ServiceState.class.getDeclaredField("auditService"); + return true; + } catch (NoSuchFieldException e) { + return false; + } + } + private void setAuditService(ServiceState serviceState, AtlasAuditService auditService) throws Exception { Field field = ServiceState.class.getDeclaredField("auditService"); field.setAccessible(true); diff --git a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java index 607bee53f9..3cbba70b99 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.service; import org.apache.atlas.server.common.dao.UserDao; +import org.apache.atlas.server.common.service.UserService; import org.springframework.security.core.userdetails.UserDetails; import org.mockito.Mock; import org.mockito.MockitoAnnotations; diff --git a/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java b/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java index ba8f025509..1a3673f022 100644 --- a/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java @@ -23,7 +23,7 @@ import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.setup.SetupException; import org.apache.atlas.setup.SetupStep; -import org.apache.atlas.web.service.CuratorFactory; +import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.tuple.Pair; import org.apache.curator.framework.CuratorFramework; diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index 425b9af64c..11b44a056f 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -53,7 +53,7 @@ - + From 549ff3a6d0a2530c34fce8e582aba58d69e532fc Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Sun, 26 Apr 2026 00:06:12 +0530 Subject: [PATCH 14/22] Updating class and methods syntax to match earlier coding style in server-common module. --- .../service/ActiveInstanceElectorService.java | 41 +++++++++- .../common/service/ActiveInstanceState.java | 29 ++++++- .../server/common/service/CuratorFactory.java | 76 +++++++++---------- .../server/common/service/EmbeddedServer.java | 1 + .../server/common/service/ServiceState.java | 41 +++++----- 5 files changed, 125 insertions(+), 63 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java index 4e8eb522c3..4e78263bdf 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java @@ -15,6 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.apache.atlas.server.common.service; import org.apache.atlas.AtlasException; @@ -29,6 +30,7 @@ import org.springframework.stereotype.Component; import javax.inject.Inject; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -38,7 +40,7 @@ /** * A service that implements leader election to determine whether this Atlas server is Active. - *

+ * * The service implements leader election through Curator's * {@link LeaderLatch} recipe. The service also implements {@link LeaderLatchListener} to get * notified of changes to leadership state. Upon becoming leader, this instance is treated as the @@ -46,6 +48,7 @@ * on being removed from leadership, this instance is treated as a passive instance and calls * {@link ActiveStateChangeHandler}s to deactivate them. */ + @Component // // This should be called the last, leaving it without the @Order(Integer.MAX_VALUE) will make it get @@ -64,6 +67,12 @@ public class ActiveInstanceElectorService implements Service, LeaderLatchListene private LeaderLatch leaderLatch; private String serverId; + /** + * Create a new instance of {@link ActiveInstanceElectorService} + * @param activeStateChangeHandlerProviders The list of registered {@link ActiveStateChangeHandler}s that + * must be called back on state changes. + * @throws AtlasException + */ @Inject public ActiveInstanceElectorService(Configuration configuration, Set activeStateChangeHandlerProviders, @@ -82,6 +91,12 @@ public ActiveInstanceElectorService(Configuration configuration, this.haSupport = haSupport; } + /** + * Join leader election on starting up. + * + * If Atlas High Availability configuration is disabled, this operation is a no-op. + * @throws AtlasException + */ @Override public void start() throws AtlasException { boolean haEnabled = haSupport.isHAEnabled(configuration); @@ -95,18 +110,27 @@ public void start() throws AtlasException { if (!haEnabled) { LOG.info("HA is not enabled, no need to start leader election service"); + return; } cacheActiveStateChangeHandlers(); + serverId = haSupport.selectServerId(configuration); + joinElection(); } + /** + * Leave leader election process and clean up resources on shutting down. + * + * If Atlas High Availability configuration is disabled, this operation is a no-op. + */ @Override public void stop() { if (!haSupport.isHAEnabled(configuration)) { LOG.info("HA is not enabled, no need to stop leader election service"); + return; } @@ -118,6 +142,12 @@ public void stop() { } } + /** + * Call all registered {@link ActiveStateChangeHandler}s on being elected active. + * + * In addition, shared state information about this instance becoming active is updated + * using {@link ActiveInstanceState}. + */ @Override public void isLeader() { LOG.warn("Server instance with server id {} is elected as leader", serverId); @@ -134,6 +164,7 @@ public void isLeader() { } } catch (Exception e) { LOG.error("Got exception while activating", e); + notLeader(); rejoinElection(); } finally { @@ -141,9 +172,13 @@ public void isLeader() { } } + /** + * Call all registered {@link ActiveStateChangeHandler}s on becoming passive instance. + */ @Override public void notLeader() { LOG.warn("Server instance with server id {} is removed as leader", serverId); + serviceState.becomingPassive(); for (int idx = activeStateChangeHandlers.size() - 1; idx >= 0; idx--) { @@ -161,11 +196,14 @@ private void joinElection() { LOG.info("Starting leader election for {}", serverId); String zkRoot = haSupport.getZookeeperProperties(configuration).getZkRoot(); + leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); + leaderLatch.addListener(this); try { leaderLatch.start(); + LOG.info("Leader latch started for {}.", serverId); } catch (Exception e) { LOG.info("Exception while starting leader latch for {}.", serverId, e); @@ -187,6 +225,7 @@ private void cacheActiveStateChangeHandlers() { private void rejoinElection() { try { leaderLatch.close(); + joinElection(); } catch (IOException e) { LOG.error("Error rejoining election", e); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java index d1ec75d893..294e737310 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java @@ -18,11 +18,13 @@ package org.apache.atlas.server.common.service; import org.apache.atlas.AtlasErrorCode; +import org.apache.atlas.AtlasException; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.ZooDefs; import org.apache.zookeeper.data.ACL; @@ -42,7 +44,7 @@ * An object that encapsulates storing and retrieving state related to an Active Atlas server. * * The current implementation uses Zookeeper to store and read this state from. It does this - * under a read-write lock implemented using Curator's {@link org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock} to + * under a read-write lock implemented using Curator's {@link InterProcessReadWriteLock} to * provide for safety across multiple processes. */ @Component @@ -51,10 +53,15 @@ public class ActiveInstanceState implements ActiveInstanceStateProvider { public static final String APACHE_ATLAS_ACTIVE_SERVER_INFO = "/active_server_info"; - private final Configuration configuration; - private final CuratorFactory curatorFactory; + private final Configuration configuration; + private final CuratorFactory curatorFactory; private final HighAvailabilitySupport haSupport; + /** + * Create a new instance of {@link ActiveInstanceState}. + * @param curatorFactory an instance of {@link CuratorFactory} to get the {@link InterProcessReadWriteLock} + * @throws AtlasException + */ @Inject public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory, HighAvailabilitySupport haSupport) { this.configuration = configuration; @@ -62,10 +69,20 @@ public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFa this.haSupport = haSupport; } + /** + * Update state of the active server instance. + * + * This method writes this instance's Server Address to a shared node in Zookeeper. + * This information is used by other passive instances to locate the current active server. + * @throws AtlasBaseException + * @param serverId ID of this server instance + */ public void update(String serverId) throws AtlasBaseException { try { CuratorFramework client = curatorFactory.clientInstance(); + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); + String atlasServerAddress = haSupport.getBoundAddressForId(configuration, serverId); List acls = new ArrayList<>(); @@ -96,6 +113,12 @@ public void update(String serverId) throws AtlasBaseException { } } + /** + * Retrieve state of the active server instance. + * + * This method reads the active server location from the shared node in Zookeeper. + * @return the active server's address and port of form http://host-or-ip:port + */ @Override public String getActiveServerAddress() { CuratorFramework client = curatorFactory.clientInstance(); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java index 8674152724..41ce2de370 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java @@ -11,10 +11,11 @@ *

* 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. + * 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.atlas.server.common.service; import com.google.common.annotations.VisibleForTesting; @@ -36,16 +37,20 @@ import javax.inject.Inject; import javax.inject.Singleton; + import java.io.IOException; import java.util.Arrays; import java.util.List; /** * A factory to create objects related to Curator. + * + * Allows for stubbing in tests. */ @Singleton @Component public class CuratorFactory { + private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); public static final String APACHE_ATLAS_LEADER_ELECTOR_PATH = "/leader_elector_path"; public static final String SASL_SCHEME = "sasl"; public static final String WORLD_SCHEME = "world"; @@ -55,15 +60,12 @@ public class CuratorFactory { public static final String IP_SCHEME = "ip"; public static final String SETUP_LOCK = "/setup_lock"; - private static final Logger LOG = LoggerFactory.getLogger(CuratorFactory.class); - - private final Configuration configuration; + private final Configuration configuration; + private CuratorFramework curatorFramework; private final HighAvailabilitySupport haSupport; - private CuratorFramework curatorFramework; /** * Initializes the {@link CuratorFramework} that is used for all interaction with Zookeeper. - * * @throws AtlasException */ @Inject @@ -76,7 +78,7 @@ public CuratorFactory(Configuration configuration, HighAvailabilitySupport haSup /** * Cleanup resources related to {@link CuratorFramework}. - *

+ * * After this call, no further calls to any curator objects should be done. */ public void close() { @@ -87,10 +89,9 @@ public void close() { /** * Returns a pre-created instance of {@link CuratorFramework}. - *

+ * * This method can be called any number of times to access the {@link CuratorFramework} used in the * application. - * * @return */ public CuratorFramework clientInstance() { @@ -99,10 +100,9 @@ public CuratorFramework clientInstance() { /** * Create a new instance {@link LeaderLatch} - * * @param serverId the ID used to register this instance with curator. - * This ID should typically be obtained using - * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} + * This ID should typically be obtained using + * {@link org.apache.atlas.ha.AtlasServerIdSelector#selectServerId(Configuration)} * @param zkRoot the root znode under which the leader latch node is added. * @return */ @@ -122,41 +122,49 @@ protected void initializeCuratorFramework() { enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); curatorFramework = builder.build(); + curatorFramework.start(); } @VisibleForTesting - void enhanceBuilderWithSecurityParameters(HighAvailabilityProperties zookeeperProperties, - CuratorFrameworkFactory.Builder builder) { + void enhanceBuilderWithSecurityParameters(HighAvailabilityProperties zookeeperProperties, CuratorFrameworkFactory.Builder builder) { ACLProvider aclProvider = getAclProvider(zookeeperProperties); + AuthInfo authInfo = null; - AuthInfo authInfo = null; if (zookeeperProperties.hasAuth()) { authInfo = AtlasZookeeperSecurityProperties.parseAuth(zookeeperProperties.getAuth()); } if (aclProvider != null) { LOG.info("Setting up acl provider."); + builder.aclProvider(aclProvider); if (authInfo != null) { byte[] auth = authInfo.getAuth(); - LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), - getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); + + LOG.info("Setting up auth provider with scheme: {} and id: {}", authInfo.getScheme(), getIdForLogging(authInfo.getScheme(), new String(auth, Charsets.UTF_8))); + builder.authorization(authInfo.getScheme(), auth); } } } + private String getCurrentUser() { + try { + return UserGroupInformation.getCurrentUser().getUserName(); + } catch (IOException ioe) { + return "unknown"; + } + } + private ACLProvider getAclProvider(HighAvailabilityProperties zookeeperProperties) { ACLProvider aclProvider = null; if (zookeeperProperties.hasAcl()) { final ACL acl = AtlasZookeeperSecurityProperties.parseAcl(zookeeperProperties.getAcl()); - LOG.info("Setting ACL for id {} with scheme {} and perms {}.", - getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), - acl.getId().getScheme(), acl.getPerms()); + LOG.info("Setting ACL for id {} with scheme {} and perms {}.", getIdForLogging(acl.getId().getScheme(), acl.getId().getId()), acl.getId().getScheme(), acl.getPerms()); LOG.info("Current logged in user: {}", getCurrentUser()); final List acls = Arrays.asList(acl); @@ -173,26 +181,9 @@ public List getAclForPath(String path) { } }; } - return aclProvider; } - private CuratorFrameworkFactory.Builder getBuilder(HighAvailabilityProperties zookeeperProperties) { - return CuratorFrameworkFactory.builder() - .connectString(zookeeperProperties.getConnectString()) - .sessionTimeoutMs(zookeeperProperties.getSessionTimeout()) - .retryPolicy(new ExponentialBackoffRetry( - zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); - } - - private String getCurrentUser() { - try { - return UserGroupInformation.getCurrentUser().getUserName(); - } catch (IOException ioe) { - return "unknown"; - } - } - private String getIdForLogging(String scheme, String id) { if (scheme.equalsIgnoreCase(SASL_SCHEME) || scheme.equalsIgnoreCase(IP_SCHEME)) { return id; @@ -204,4 +195,11 @@ private String getIdForLogging(String scheme, String id) { return "unknown"; } + + private CuratorFrameworkFactory.Builder getBuilder(HighAvailabilityProperties zookeeperProperties) { + return CuratorFrameworkFactory.builder() + .connectString(zookeeperProperties.getConnectString()) + .sessionTimeoutMs(zookeeperProperties.getSessionTimeout()) + .retryPolicy(new ExponentialBackoffRetry(zookeeperProperties.getRetriesSleepTimeMillis(), zookeeperProperties.getNumRetries())); + } } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java b/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java index a0254d9d7c..9df6fb0ac9 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/EmbeddedServer.java @@ -39,6 +39,7 @@ /** * This class embeds a Jetty server and a connector. */ + public class EmbeddedServer { public static final Logger LOG = LoggerFactory.getLogger(EmbeddedServer.class); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java index 0be60ac961..0c7a0ec470 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java @@ -6,15 +6,16 @@ * 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. + * 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.atlas.server.common.service; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; @@ -41,9 +42,17 @@ public class ServiceState implements ServiceStateProvider { private static final Logger LOG = LoggerFactory.getLogger(ServiceState.class); - private final Configuration configuration; + public enum ServiceStateValue { + ACTIVE, + PASSIVE, + BECOMING_ACTIVE, + BECOMING_PASSIVE, + MIGRATING + } + + private Configuration configuration; + private volatile ServiceStateValue state; private final HighAvailabilitySupport haSupport; - private volatile ServiceStateValue state; @Inject public ServiceState(Configuration configuration, HighAvailabilitySupport haSupport) { @@ -52,7 +61,7 @@ public ServiceState(Configuration configuration, HighAvailabilitySupport haSuppo state = !haSupport.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; - if (!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { + if(!StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME, ""))) { state = ServiceStateValue.MIGRATING; } } @@ -83,9 +92,9 @@ public void setPassive() { @Override public boolean isInstanceInTransition() { - ServiceStateValue currentState = getState(); - - return currentState == ServiceStateValue.BECOMING_ACTIVE || currentState == ServiceStateValue.BECOMING_PASSIVE; + ServiceStateValue state = getState(); + return state == ServiceStateValue.BECOMING_ACTIVE + || state == ServiceStateValue.BECOMING_PASSIVE; } public void setMigration() { @@ -114,12 +123,4 @@ private void setState(ServiceStateValue newState) { state = newState; } - - public enum ServiceStateValue { - ACTIVE, - PASSIVE, - BECOMING_ACTIVE, - BECOMING_PASSIVE, - MIGRATING - } } From 31665931fb38d75b8e6c35084d0d8da70e7239bb Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Mon, 27 Apr 2026 10:57:43 +0530 Subject: [PATCH 15/22] Updating setup class KerberosAwareListener and package to use server-common module for both webapp and rest-notification webapp. --- .../rest/web/setup/KerberosAwareListener.java | 34 ------------------- .../src/main/webapp/WEB-INF/web.xml | 2 +- .../common}/setup/KerberosAwareListener.java | 2 +- webapp/src/main/webapp/WEB-INF/web.xml | 2 +- .../web/setup/KerberosAwareListenerTest.java | 1 + 5 files changed, 4 insertions(+), 37 deletions(-) delete mode 100644 rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java rename {webapp/src/main/java/org/apache/atlas/web => server-common/src/main/java/org/apache/atlas/server/common}/setup/KerberosAwareListener.java (96%) diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java deleted file mode 100644 index 3a01dd8448..0000000000 --- a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/setup/KerberosAwareListener.java +++ /dev/null @@ -1,34 +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.atlas.notification.rest.web.setup; - - -import org.apache.atlas.server.common.listeners.LoginProcessor; -import org.springframework.web.context.ContextLoaderListener; - -import javax.servlet.ServletContextEvent; - -public class KerberosAwareListener extends ContextLoaderListener { - @Override - public void contextInitialized(ServletContextEvent event) { - LoginProcessor loginProcessor = new LoginProcessor(); - loginProcessor.login(); - - super.contextInitialized(event); - } -} diff --git a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml index 6307047f8b..06647d59e4 100644 --- a/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -62,7 +62,7 @@ - org.apache.atlas.notification.rest.web.setup.KerberosAwareListener + org.apache.atlas.server.common.setup.KerberosAwareListener diff --git a/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java b/server-common/src/main/java/org/apache/atlas/server/common/setup/KerberosAwareListener.java similarity index 96% rename from webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java rename to server-common/src/main/java/org/apache/atlas/server/common/setup/KerberosAwareListener.java index fe4b9f80ee..c89a7803c5 100644 --- a/webapp/src/main/java/org/apache/atlas/web/setup/KerberosAwareListener.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/setup/KerberosAwareListener.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.setup; +package org.apache.atlas.server.common.setup; import org.apache.atlas.server.common.listeners.LoginProcessor; import org.springframework.web.context.ContextLoaderListener; diff --git a/webapp/src/main/webapp/WEB-INF/web.xml b/webapp/src/main/webapp/WEB-INF/web.xml index 683cc23eb5..f340d990ea 100755 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -102,7 +102,7 @@ - org.apache.atlas.web.setup.KerberosAwareListener + org.apache.atlas.server.common.setup.KerberosAwareListener diff --git a/webapp/src/test/java/org/apache/atlas/web/setup/KerberosAwareListenerTest.java b/webapp/src/test/java/org/apache/atlas/web/setup/KerberosAwareListenerTest.java index cea6bea7bf..8c5231462f 100644 --- a/webapp/src/test/java/org/apache/atlas/web/setup/KerberosAwareListenerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/setup/KerberosAwareListenerTest.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.setup; +import org.apache.atlas.server.common.setup.KerberosAwareListener; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeClass; From b93fb632a43f6b5c06f4a3f25935429fc080daa2 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Mon, 27 Apr 2026 11:33:11 +0530 Subject: [PATCH 16/22] Updated test class ImportTaskListenerImplTest in webapp module removed redundant lines. --- .../apache/atlas/notification/ImportTaskListenerImplTest.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/webapp/src/test/java/org/apache/atlas/notification/ImportTaskListenerImplTest.java b/webapp/src/test/java/org/apache/atlas/notification/ImportTaskListenerImplTest.java index 76020d2da2..92e4efbb5a 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/ImportTaskListenerImplTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/ImportTaskListenerImplTest.java @@ -290,7 +290,6 @@ public void testStartAsyncImportIfAvailable_WithInvalidStatus() throws Exception @Test public void testStartImportConsumer_Successful() throws Exception { - Mockito.doReturn("import123").when(importRequest).getImportId(); when(importRequest.getStatus()).thenReturn(WAITING); when(importRequest.getTopicName()).thenReturn("topic1"); @@ -572,7 +571,6 @@ public void testStartInternalIsNonBlocking() throws InterruptedException { @Test public void testImportNotProcessedWhenPassive() throws Exception { - Mockito.doReturn("import123").when(importRequest).getImportId(); when(importRequest.getStatus()).thenReturn(WAITING); when(requestQueue.poll(anyLong(), any(TimeUnit.class))).thenReturn("import123"); importTaskListener.instanceIsPassive(); @@ -583,7 +581,6 @@ public void testImportNotProcessedWhenPassive() throws Exception { @Test public void testExecutorNotRecreatedWhenPassive() throws Exception { - Mockito.doReturn("import123").when(importRequest).getImportId(); when(importRequest.getStatus()).thenReturn(WAITING); when(requestQueue.poll(anyLong(), any(TimeUnit.class))).thenReturn("import123"); when(importRequest.getStatus()).thenReturn(WAITING); From aced7bf445d0ce1f95a4fb89d9a73235e35fcb98 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Tue, 28 Apr 2026 10:58:15 +0530 Subject: [PATCH 17/22] Restoring admin SERVER_START/SERVER_STATE_ACTIVE via WebappAdminAuditHook --- .../web/metrics/WebappAdminAuditHook.java | 112 +++++++++++++++++ .../web/metrics/WebappAdminAuditHookTest.java | 118 ++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 webapp/src/main/java/org/apache/atlas/web/metrics/WebappAdminAuditHook.java create mode 100644 webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java diff --git a/webapp/src/main/java/org/apache/atlas/web/metrics/WebappAdminAuditHook.java b/webapp/src/main/java/org/apache/atlas/web/metrics/WebappAdminAuditHook.java new file mode 100644 index 0000000000..2613430382 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/metrics/WebappAdminAuditHook.java @@ -0,0 +1,112 @@ +/** + * 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.atlas.web.metrics; + +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.audit.AtlasAuditEntry.AuditOperation; +import org.apache.atlas.repository.audit.AtlasAuditService; +import org.apache.atlas.server.common.service.EmbeddedServer; +import org.apache.atlas.server.common.service.ServiceMetricsHook; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Date; + +/** + * Persists {@code SERVER_START} and {@code SERVER_STATE_ACTIVE} rows for Administration → Audits. + *

+ * Registered alongside {@link WebappServiceMetricsHook} as a {@link ServiceMetricsHook} so lifecycle + * matches {@link org.apache.atlas.server.common.service.ActiveInstanceElectorService}: non-HA invokes + * both hooks in sequence; HA emits {@code SERVER_START} at process init and {@code SERVER_STATE_ACTIVE} + * when this instance becomes leader. + *

+ * Uses the eight-argument {@link AtlasAuditService#add} overload so user and client id are explicit + * and do not rely on {@link org.apache.atlas.RequestContext} during bootstrap. + */ +@Component +public class WebappAdminAuditHook implements ServiceMetricsHook { + private static final Logger LOG = LoggerFactory.getLogger(WebappAdminAuditHook.class); + + private static final String ADMIN_AUDIT_USER = "atlas"; + + private final AtlasAuditService auditService; + + @Inject + public WebappAdminAuditHook(AtlasAuditService auditService) { + this.auditService = auditService; + } + + @Override + public void onServerStart() { + try { + Date endTime = new Date(); + auditService.add( + ADMIN_AUDIT_USER, + AuditOperation.SERVER_START, + clientIdForAudit(), + EmbeddedServer.SERVER_START_TIME, + endTime, + null, + null, + 0); + } catch (AtlasBaseException e) { + LOG.warn("Failed to write SERVER_START admin audit", e); + } + } + + @Override + public void onServerActivation() { + try { + Date now = new Date(); + auditService.add( + ADMIN_AUDIT_USER, + AuditOperation.SERVER_STATE_ACTIVE, + clientIdForAudit(), + now, + now, + null, + null, + 0); + } catch (AtlasBaseException e) { + LOG.warn("Failed to write SERVER_STATE_ACTIVE admin audit", e); + } + } + + /** + * Aligns with {@link AtlasAuditService#add} behavior when no HTTP client is present: host:address. + */ + private static String clientIdForAudit() { + try { + InetAddress local = InetAddress.getLocalHost(); + String hostName = StringUtils.defaultString(local.getHostName()); + String hostAddress = StringUtils.defaultString(local.getHostAddress()); + if (StringUtils.isNotEmpty(hostName) && StringUtils.isNotEmpty(hostAddress)) { + return hostName + ":" + hostAddress; + } + } catch (UnknownHostException e) { + LOG.debug("Could not resolve local host for audit client id", e); + } + return "unknown"; + } +} diff --git a/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java b/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java new file mode 100644 index 0000000000..957157dfcc --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java @@ -0,0 +1,118 @@ +/** + * 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.atlas.web.metrics; + +import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.audit.AtlasAuditEntry.AuditOperation; +import org.apache.atlas.repository.audit.AtlasAuditService; +import org.apache.atlas.server.common.service.EmbeddedServer; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.util.Date; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; + +public class WebappAdminAuditHookTest { + private static final String ATLAS_USER = "atlas"; + + @Mock + private AtlasAuditService auditService; + + private WebappAdminAuditHook hook; + + @BeforeMethod + public void setup() { + MockitoAnnotations.initMocks(this); + hook = new WebappAdminAuditHook(auditService); + } + + @Test + public void testOnServerStartInvokesAuditWithServerStartOperation() throws AtlasBaseException { + hook.onServerStart(); + + verify(auditService, times(1)).add( + eq(ATLAS_USER), + eq(AuditOperation.SERVER_START), + anyString(), + eq(EmbeddedServer.SERVER_START_TIME), + any(Date.class), + isNull(), + isNull(), + eq(0)); + } + + @Test + public void testOnServerActivationInvokesAuditWithServerStateActiveOperation() throws AtlasBaseException { + hook.onServerActivation(); + + ArgumentCaptor startCaptor = ArgumentCaptor.forClass(Date.class); + ArgumentCaptor endCaptor = ArgumentCaptor.forClass(Date.class); + + verify(auditService, times(1)).add( + eq(ATLAS_USER), + eq(AuditOperation.SERVER_STATE_ACTIVE), + anyString(), + startCaptor.capture(), + endCaptor.capture(), + isNull(), + isNull(), + eq(0)); + + assertNotNull(startCaptor.getValue()); + assertNotNull(endCaptor.getValue()); + assertEquals(startCaptor.getValue(), endCaptor.getValue()); + } + + @Test + public void testOnServerStartSwallowsAtlasBaseException() throws AtlasBaseException { + doThrow(new AtlasBaseException("x")).when(auditService).add( + anyString(), + any(AuditOperation.class), + anyString(), + any(Date.class), + any(Date.class), + isNull(), + isNull(), + anyLong()); + + hook.onServerStart(); + + verify(auditService, times(1)).add( + eq(ATLAS_USER), + eq(AuditOperation.SERVER_START), + anyString(), + eq(EmbeddedServer.SERVER_START_TIME), + any(Date.class), + isNull(), + isNull(), + eq(0)); + } +} From 1e2d8bdb7db1d80a3586aae7cc79498adcbcd712 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Tue, 28 Apr 2026 11:43:20 +0530 Subject: [PATCH 18/22] Server-Common: align ActiveServerFilter with passive vs migration admin URI policy --- .../common/filters/ActiveServerFilter.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java index 873babc941..de800f4328 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java @@ -50,9 +50,15 @@ public class ActiveServerFilter implements Filter { private static final String MIGRATION_STATUS_STATIC_PAGE = "migration-status.html"; - private final String[] adminUriNotFiltered = {"/admin/export", "/admin/import", "/admin/importfile", "/admin/audits", + private final String[] adminUriNotSupportedInPassive = { + "/admin/export", "/admin/import", "/admin/importfile", "/admin/audits", "/admin/purge", "/admin/expimp/audit", "/admin/metrics", "/admin/server", "/admin/audit/", "admin/tasks", - "/admin/debug/metrics", "/admin/audits/ageout", "admin/async/import", "admin/async/import/status"}; + "/admin/debug/metrics", "/admin/audits/ageout", "admin/audits/rules", "admin/async/import", "admin/async/import/status" + }; + private final String[] adminUriNotSupportedInMigration = { + "/admin/export", "/admin/import", "/admin/importfile", "admin/async/import" + }; + private final ActiveInstanceStateProvider activeInstanceState; private final ServiceStateProvider serviceState; @@ -77,10 +83,9 @@ public void init(FilterConfig filterConfig) throws ServletException { */ @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - if (isFilteredURI(servletRequest)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Is a filtered URI: {}. Passing request downstream.", ((HttpServletRequest) servletRequest).getRequestURI()); - } + if (isAdminURISupportedInCurrentState(servletRequest)) { + LOG.debug("URL {} is supported when the instance is in {} state. Passing request downstream.", + ((HttpServletRequest) servletRequest).getRequestURI(), serviceState.getStateName()); filterChain.doFilter(servletRequest, servletResponse); } else if (isInstanceActive()) { @@ -125,12 +130,14 @@ boolean isInstanceActive() { return serviceState.isActive(); } - private boolean isFilteredURI(ServletRequest servletRequest) { + private boolean isAdminURISupportedInCurrentState(ServletRequest servletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; String requestURI = httpServletRequest.getRequestURI(); if (requestURI.contains("/admin/")) { - for (String s : adminUriNotFiltered) { + String[] uriNotSupported = serviceState.isInstanceInMigration() ? adminUriNotSupportedInMigration : adminUriNotSupportedInPassive; + + for (String s : uriNotSupported) { if (requestURI.contains(s)) { LOG.trace("URL not supported in HA mode: {}", requestURI); @@ -139,9 +146,9 @@ private boolean isFilteredURI(ServletRequest servletRequest) { } return true; - } else { - return false; } + + return false; } private boolean isRootURI(ServletRequest servletRequest) { From d3d880d50fe33cb6b9783c08cc0d0b9f28e85d92 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Tue, 28 Apr 2026 15:19:18 +0530 Subject: [PATCH 19/22] Resolving Testcase Failure:- Fix UserAuthorityGranter.grant null return; align WebappAdminAuditHookTest with long resultCount --- .../atlas/server/common/security/UserAuthorityGranter.java | 3 +-- .../apache/atlas/web/metrics/WebappAdminAuditHookTest.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java index cb89add61d..b691692ed8 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java @@ -29,7 +29,6 @@ public class UserAuthorityGranter implements AuthorityGranter { @Override public Set grant(Principal principal) { - Collections.singleton("DATA_SCIENTIST"); - return null; + return Collections.singleton("DATA_SCIENTIST"); } } diff --git a/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java b/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java index 957157dfcc..1d4766f250 100644 --- a/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/metrics/WebappAdminAuditHookTest.java @@ -66,7 +66,7 @@ public void testOnServerStartInvokesAuditWithServerStartOperation() throws Atlas any(Date.class), isNull(), isNull(), - eq(0)); + eq(0L)); } @Test @@ -84,7 +84,7 @@ public void testOnServerActivationInvokesAuditWithServerStateActiveOperation() t endCaptor.capture(), isNull(), isNull(), - eq(0)); + eq(0L)); assertNotNull(startCaptor.getValue()); assertNotNull(endCaptor.getValue()); @@ -113,6 +113,6 @@ public void testOnServerStartSwallowsAtlasBaseException() throws AtlasBaseExcept any(Date.class), isNull(), isNull(), - eq(0)); + eq(0L)); } } From 2fe0e23de5cb86fbb8d00d4a663c2e744842b504 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Tue, 28 Apr 2026 18:16:02 +0530 Subject: [PATCH 20/22] Resolving Testcases Failure Fix 503 in SecureEmbeddedServerTest: profile-gate test security, split filter beans, Curator only if HA. --- .../common/service/ActiveInstanceState.java | 12 ++++ .../server/common/service/CuratorFactory.java | 4 +- .../web/security/AtlasSecurityConfig.java | 43 ----------- .../AtlasSecurityFilterBeansConfig.java | 72 +++++++++++++++++++ .../web/security/FileAuthenticationTest.java | 9 ++- .../TestSpringSecurityBridgeConfig.java | 6 ++ .../web/service/ActiveInstanceStateTest.java | 37 ++++++++-- .../atlas/web/service/CuratorFactoryTest.java | 2 + 8 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java index 294e737310..89486c8ebd 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java @@ -78,6 +78,10 @@ public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFa * @param serverId ID of this server instance */ public void update(String serverId) throws AtlasBaseException { + if (!haSupport.isHAEnabled(configuration)) { + return; + } + try { CuratorFramework client = curatorFactory.clientInstance(); @@ -121,9 +125,17 @@ public void update(String serverId) throws AtlasBaseException { */ @Override public String getActiveServerAddress() { + if (!haSupport.isHAEnabled(configuration)) { + return null; + } + CuratorFramework client = curatorFactory.clientInstance(); String serverAddress = null; + if (client == null) { + return null; + } + try { HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java index 41ce2de370..c7917a081d 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/CuratorFactory.java @@ -73,7 +73,9 @@ public CuratorFactory(Configuration configuration, HighAvailabilitySupport haSup this.configuration = configuration; this.haSupport = haSupport; - initializeCuratorFramework(); + if (haSupport.isHAEnabled(configuration)) { + initializeCuratorFramework(); + } } /** diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 0a4036a737..29e44ae867 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -24,9 +24,6 @@ import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.server.common.filters.HeadersUtil; -import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; -import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; -import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; @@ -327,44 +324,4 @@ protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessin return filter; } - - @Bean - public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { - return new AtlasAuthenticationProviderBridge() { - @Override - public java.util.List getAuthoritiesFromUGI(String userName) { - return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); - } - - @Override - public void setSsoEnabled(boolean enabled) { - authenticationProvider.setSsoEnabled(enabled); - } - - @Override - public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) { - return authenticationProvider.authenticate(authentication); - } - }; - } - - @Bean - public ActiveServerFilter commonActiveServerFilter(ActiveInstanceStateProvider activeInstanceStateProvider, ServiceStateProvider serviceStateProvider) { - return new ActiveServerFilter(activeInstanceStateProvider, serviceStateProvider); - } - - @Bean - public AtlasAuthenticationFilter commonAtlasAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { - return new AtlasAuthenticationFilter(atlasAuthenticationProviderBridge); - } - - @Bean - public AtlasKnoxSSOAuthenticationFilter commonAtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { - return new AtlasKnoxSSOAuthenticationFilter(atlasAuthenticationProviderBridge); - } - - @Bean - public AtlasCSRFPreventionFilter commonAtlasCSRFPreventionFilter() { - return new AtlasCSRFPreventionFilter(); - } } diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java new file mode 100644 index 0000000000..e81f175189 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java @@ -0,0 +1,72 @@ +/** + * 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.atlas.web.security; + +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; +import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class AtlasSecurityFilterBeansConfig { + + @Bean + public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { + return new AtlasAuthenticationProviderBridge() { + @Override + public java.util.List getAuthoritiesFromUGI(String userName) { + return AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName); + } + + @Override + public void setSsoEnabled(boolean enabled) { + authenticationProvider.setSsoEnabled(enabled); + } + + @Override + public org.springframework.security.core.Authentication authenticate(org.springframework.security.core.Authentication authentication) { + return authenticationProvider.authenticate(authentication); + } + }; + } + + @Bean + public ActiveServerFilter commonActiveServerFilter(ActiveInstanceStateProvider activeInstanceStateProvider, ServiceStateProvider serviceStateProvider) { + return new ActiveServerFilter(activeInstanceStateProvider, serviceStateProvider); + } + + @Bean + public AtlasAuthenticationFilter commonAtlasAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasKnoxSSOAuthenticationFilter commonAtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasKnoxSSOAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasCSRFPreventionFilter commonAtlasCSRFPreventionFilter() { + return new AtlasCSRFPreventionFilter(); + } +} diff --git a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java index 0ed6977ca7..2c3e18ba2f 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -77,7 +78,13 @@ public void setup() throws Exception { System.setProperty("atlas.conf", persistDir); - applicationContext = new AnnotationConfigApplicationContext(TestSpringSecurityBridgeConfig.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + + ((ConfigurableEnvironment) ctx.getEnvironment()).setActiveProfiles("testSpringSecurityBridge"); + ctx.register(TestSpringSecurityBridgeConfig.class); + ctx.refresh(); + + applicationContext = ctx; authProvider = applicationContext.getBean(org.apache.atlas.web.security.AtlasAuthenticationProvider.class); } diff --git a/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java index ed500716c0..bc1366bcd1 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java @@ -21,6 +21,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.ImportResource; +import org.springframework.context.annotation.Profile; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -30,8 +31,13 @@ * Loads {@code test-spring-security.xml} for tests (e.g. {@link FileAuthenticationTest}) and * registers {@link AtlasAuthenticationProviderBridge} for Knox/ Kerberos filters declared in XML. * XML alone cannot express the bridge; production uses {@link AtlasSecurityConfig} instead. + *

+ * Profile-gated so classpath scanning for embedded-server / full webapp tests does not import + * {@code test-spring-security.xml} alongside {@link AtlasSecurityConfig} (that overlap creates a + * circular dependency between the Knox SSO filter and {@code atlasAuthenticationProviderBridge}). */ @Configuration +@Profile("testSpringSecurityBridge") @ImportResource("classpath:test-spring-security.xml") public class TestSpringSecurityBridgeConfig { diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java index cf64d11163..83bba800f5 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java @@ -18,8 +18,10 @@ package org.apache.atlas.web.service; +import org.apache.atlas.AtlasException; import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; import org.apache.atlas.server.common.service.HighAvailabilitySupport; import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; @@ -51,6 +53,31 @@ public class ActiveInstanceStateTest { private static final String HOST_PORT = "127.0.0.1:21000"; public static final String SERVER_ADDRESS = "http://" + HOST_PORT; + /** Tests exercise ZK/Curator paths used only when HA is enabled. */ + private static final HighAvailabilitySupport HA_SUPPORT_FOR_TESTS = new HighAvailabilitySupport() { + private final HighAvailabilitySupport defaults = new HighAvailabilitySupport.AtlasConfigurationDefaults(); + + @Override + public boolean isHAEnabled(Configuration configuration) { + return true; + } + + @Override + public String selectServerId(Configuration configuration) throws AtlasException { + return defaults.selectServerId(configuration); + } + + @Override + public String getBoundAddressForId(Configuration configuration, String serverId) { + return defaults.getBoundAddressForId(configuration, serverId); + } + + @Override + public HighAvailabilityProperties getZookeeperProperties(Configuration configuration) { + return defaults.getZookeeperProperties(configuration); + } + }; + @Mock private Configuration configuration; @@ -87,7 +114,7 @@ public void testSharedPathIsCreatedIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -121,7 +148,7 @@ public void testSharedPathIsCreatedWithRightACLIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -144,7 +171,7 @@ public void testDataIsUpdatedWithAtlasServerAddress() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -161,7 +188,7 @@ public void testShouldReturnActiveServerAddress() throws Exception { when(curatorFramework.getData()).thenReturn(getDataBuilder); when(getDataBuilder.forPath(getPath())).thenReturn(SERVER_ADDRESS.getBytes(StandardCharsets.UTF_8)); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); String actualServerAddress = activeInstanceState.getActiveServerAddress(); assertEquals(actualServerAddress, SERVER_ADDRESS); @@ -177,7 +204,7 @@ public void testShouldHandleExceptionsInFetchingServerAddress() throws Exception when(curatorFramework.getData()).thenReturn(getDataBuilder); when(getDataBuilder.forPath(getPath())).thenThrow(new Exception()); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, new HighAvailabilitySupport.AtlasConfigurationDefaults()); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); assertNull(activeInstanceState.getActiveServerAddress()); } diff --git a/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java b/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java index b63558ebfc..25e66e61c5 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/CuratorFactoryTest.java @@ -62,6 +62,8 @@ public void setup() { } private CuratorFactory buildCuratorFactory() { + when(haSupport.isHAEnabled(configuration)).thenReturn(true); + return new CuratorFactory(configuration, haSupport) { @Override protected void initializeCuratorFramework() { From 74711de7e997b4f6234dfc79add21b0ce0979d60 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Mon, 4 May 2026 11:32:02 +0530 Subject: [PATCH 21/22] Resolving checkstyle error's in webapp module. --- .../notification/NotificationHookConsumer.java | 6 +++--- .../apache/atlas/web/resources/AdminResource.java | 6 +++--- .../atlas/web/resources/DataSetLineageResource.java | 2 +- .../apache/atlas/web/resources/EntityResource.java | 2 +- .../apache/atlas/web/resources/LineageResource.java | 2 +- .../web/resources/MetadataDiscoveryResource.java | 2 +- .../apache/atlas/web/resources/TypesResource.java | 2 +- .../org/apache/atlas/web/rest/DiscoveryREST.java | 2 +- .../java/org/apache/atlas/web/rest/EntityREST.java | 2 +- .../org/apache/atlas/web/rest/GlossaryREST.java | 2 +- .../apache/atlas/web/rest/IndexRecoveryREST.java | 2 +- .../java/org/apache/atlas/web/rest/LineageREST.java | 2 +- .../org/apache/atlas/web/rest/NotificationREST.java | 2 +- .../org/apache/atlas/web/rest/RelationshipREST.java | 2 +- .../java/org/apache/atlas/web/rest/TypesREST.java | 2 +- .../web/security/AtlasAuthenticationProvider.java | 13 ++++++------- .../atlas/web/security/AtlasSecurityConfig.java | 5 ++--- .../security/AtlasSecurityFilterBeansConfig.java | 3 +-- .../java/org/apache/atlas/web/setup/SetupSteps.java | 4 ++-- .../apache/atlas/web/util/AtlasJsonProvider.java | 3 +-- .../NotificationHookConsumerKafkaTest.java | 2 +- .../notification/NotificationHookConsumerTest.java | 2 +- .../filters/AtlasAuthenticationEntryPointTest.java | 3 +-- .../web/filters/AtlasAuthenticationFilterTest.java | 9 ++++----- .../AtlasAuthenticationKerberosFilterTest.java | 2 +- .../atlas/web/filters/AtlasHeaderFilterTest.java | 7 +++---- .../AtlasKnoxSSOAuthenticationFilterTest.java | 3 +-- .../apache/atlas/web/filters/AuditFilterTest.java | 2 +- .../apache/atlas/web/filters/HeaderUtilsTest.java | 5 ++--- .../org/apache/atlas/web/filters/RestUtilTest.java | 3 +-- .../web/integration/EntityV2JerseyResourceIT.java | 2 -- .../atlas/web/resources/AdminResourceTest.java | 4 ++-- .../org/apache/atlas/web/rest/EntityRESTTest.java | 2 +- .../apache/atlas/web/rest/RelationshipRESTTest.java | 2 +- .../org/apache/atlas/web/rest/TypesRESTTest.java | 2 +- .../security/AtlasADAuthenticationProviderTest.java | 7 +++---- .../AtlasAuthenticationFailureHandlerTest.java | 3 +-- .../security/AtlasAuthenticationProviderTest.java | 11 +++++------ .../AtlasAuthenticationSuccessHandlerTest.java | 3 +-- .../AtlasKeycloakAuthenticationProviderTest.java | 2 +- .../AtlasPamAuthenticationProviderTest.java | 5 ++--- .../atlas/web/security/AtlasSecurityConfigTest.java | 5 ++--- .../atlas/web/security/FileAuthenticationTest.java | 3 +-- .../atlas/web/security/PamLoginModuleTest.java | 3 +-- .../apache/atlas/web/security/PamPrincipalTest.java | 3 +-- .../java/org/apache/atlas/web/security/SSLTest.java | 2 +- .../security/TestSpringSecurityBridgeConfig.java | 1 - .../web/security/UserAuthorityGranterTest.java | 3 +-- .../atlas/web/service/ActiveInstanceStateTest.java | 2 +- .../atlas/web/service/EmbeddedServerTest.java | 1 - .../apache/atlas/web/service/UserServiceTest.java | 2 +- .../org/apache/atlas/web/setup/SetupStepsTest.java | 2 +- .../apache/atlas/web/util/DateTimeHelperTest.java | 3 +-- .../org/apache/atlas/web/util/ServletsTest.java | 3 +-- 54 files changed, 77 insertions(+), 103 deletions(-) diff --git a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java index 61155785bf..5fcff85dca 100644 --- a/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java +++ b/webapp/src/main/java/org/apache/atlas/notification/NotificationHookConsumer.java @@ -57,6 +57,9 @@ import org.apache.atlas.repository.store.graph.EntityCorrelationStore; import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2; +import org.apache.atlas.server.common.filters.AuditFilter; +import org.apache.atlas.server.common.filters.AuditFilter.AuditLog; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.service.Service; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasStructType.AtlasAttribute; @@ -72,9 +75,6 @@ import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityDeleteRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityPartialUpdateRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityUpdateRequest; -import org.apache.atlas.server.common.filters.AuditFilter; -import org.apache.atlas.server.common.filters.AuditFilter.AuditLog; -import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.map.PassiveExpiringMap; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java index b778ade379..9df94d9688 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/AdminResource.java @@ -67,6 +67,9 @@ import org.apache.atlas.repository.impexp.ZipSink; import org.apache.atlas.repository.patches.AtlasPatchManager; import org.apache.atlas.repository.store.graph.AtlasEntityStore; +import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; +import org.apache.atlas.server.common.service.ServiceState; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.services.MetricsService; import org.apache.atlas.services.PurgeService; import org.apache.atlas.tasks.TaskManagement; @@ -76,11 +79,8 @@ import org.apache.atlas.util.SearchTracker; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; -import org.apache.atlas.server.common.service.ServiceState; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java index ecc21aa916..469c161a79 100644 --- a/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java @@ -25,13 +25,13 @@ import org.apache.atlas.model.lineage.AtlasLineageInfo; import org.apache.atlas.model.lineage.AtlasLineageInfo.LineageDirection; import org.apache.atlas.repository.store.graph.AtlasEntityStore; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.v1.model.lineage.DataSetLineageResponse; import org.apache.atlas.v1.model.lineage.SchemaResponse; import org.apache.atlas.web.util.LineageUtils; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java index 8e600e6b04..ccf66c5950 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/EntityResource.java @@ -39,6 +39,7 @@ import org.apache.atlas.repository.store.graph.AtlasEntityStore; import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasType; import org.apache.atlas.type.AtlasTypeRegistry; @@ -50,7 +51,6 @@ import org.apache.atlas.v1.model.instance.Referenceable; import org.apache.atlas.v1.model.instance.Struct; import org.apache.atlas.web.rest.EntityREST; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java index fdfac61bed..6dca03e32d 100644 --- a/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/LineageResource.java @@ -22,12 +22,12 @@ import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.lineage.AtlasLineageInfo; import org.apache.atlas.model.lineage.AtlasLineageInfo.LineageDirection; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.v1.model.lineage.LineageResponse; import org.apache.atlas.v1.model.lineage.SchemaResponse; import org.apache.atlas.web.util.LineageUtils; -import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java index a355820576..da6fb90740 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/MetadataDiscoveryResource.java @@ -27,13 +27,13 @@ import org.apache.atlas.query.QueryParams; import org.apache.atlas.repository.converters.AtlasInstanceConverter; import org.apache.atlas.repository.store.graph.AtlasEntityStore; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.utils.ParamChecker; import org.apache.atlas.v1.model.discovery.DSLSearchResult; import org.apache.atlas.v1.model.discovery.FullTextSearchResult; import org.apache.atlas.v1.model.instance.Referenceable; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java index 5d6d22e094..b05bd4dc08 100755 --- a/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java +++ b/webapp/src/main/java/org/apache/atlas/web/resources/TypesResource.java @@ -24,13 +24,13 @@ import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.typedef.AtlasTypesDef; import org.apache.atlas.repository.converters.TypeConverterUtil; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.store.AtlasTypeDefStore; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.v1.model.typedef.TypesDef; import org.apache.atlas.web.rest.TypesREST; -import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java index ca59fd7996..1719653b77 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/DiscoveryREST.java @@ -38,12 +38,12 @@ import org.apache.atlas.model.profile.AtlasUserSavedSearch; import org.apache.atlas.repository.Constants; import org.apache.atlas.repository.store.graph.v2.tasks.searchdownload.SearchResultDownloadTask; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasStructType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java index 09ac618f54..1f0e380cda 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/EntityREST.java @@ -45,12 +45,12 @@ import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; import org.apache.atlas.repository.store.graph.v2.ClassificationAssociator; import org.apache.atlas.repository.store.graph.v2.EntityStream; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasClassificationType; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.util.FileUtils; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java index 2c874bc8d2..e83a10d16f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/GlossaryREST.java @@ -32,8 +32,8 @@ import org.apache.atlas.model.glossary.relations.AtlasRelatedCategoryHeader; import org.apache.atlas.model.glossary.relations.AtlasRelatedTermHeader; import org.apache.atlas.model.instance.AtlasRelatedObjectId; -import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java index 8059216d96..df379f8290 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/IndexRecoveryREST.java @@ -27,9 +27,9 @@ import org.apache.atlas.repository.graph.IndexRecoveryService; import org.apache.atlas.repository.graphdb.AtlasGraph; import org.apache.atlas.repository.graphdb.AtlasVertex; -import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.server.common.util.DateTimeHelper; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.springframework.format.annotation.DateTimeFormat; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java index 583b70a8c2..ef3a94b46e 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/LineageREST.java @@ -28,10 +28,10 @@ import org.apache.atlas.model.lineage.AtlasLineageInfo.LineageDirection; import org.apache.atlas.model.lineage.LineageOnDemandConstraints; import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java index f477716425..6c437ea98b 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/NotificationREST.java @@ -29,8 +29,8 @@ import org.apache.atlas.kafka.KafkaNotification; import org.apache.atlas.notification.NotificationException; import org.apache.atlas.notification.NotificationInterface; -import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AtlasJson; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java index 80f6f565df..6f79c3442c 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/RelationshipREST.java @@ -23,8 +23,8 @@ import org.apache.atlas.model.instance.AtlasRelationship; import org.apache.atlas.model.instance.AtlasRelationship.AtlasRelationshipWithExtInfo; import org.apache.atlas.repository.store.graph.AtlasRelationshipStore; -import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AtlasPerfTracer; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java index 36d284a5cc..d68bf1b7ac 100644 --- a/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java +++ b/webapp/src/main/java/org/apache/atlas/web/rest/TypesREST.java @@ -30,10 +30,10 @@ import org.apache.atlas.model.typedef.AtlasTypeDefHeader; import org.apache.atlas.model.typedef.AtlasTypesDef; import org.apache.atlas.repository.util.FilterUtil; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.store.AtlasTypeDefStore; import org.apache.atlas.type.AtlasTypeUtil; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.util.Servlets; import org.apache.http.annotation.Experimental; import org.slf4j.Logger; import org.springframework.stereotype.Service; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java index 1dbd206239..c16efc6317 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java @@ -18,6 +18,12 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,13 +35,6 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; -import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; -import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; - @Component @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java index 29e44ae867..de7218b911 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityConfig.java @@ -24,6 +24,8 @@ import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.StringUtils; @@ -72,9 +74,6 @@ import javax.inject.Inject; -import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; -import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; - import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java index e81f175189..5bdb667864 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java @@ -21,15 +21,14 @@ import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; -import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.server.common.filters.spi.ActiveInstanceStateProvider; +import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AtlasSecurityFilterBeansConfig { - @Bean public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { return new AtlasAuthenticationProviderBridge() { diff --git a/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java b/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java index a02243ece0..b789160428 100644 --- a/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java +++ b/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java @@ -24,10 +24,10 @@ import org.apache.atlas.AtlasException; import org.apache.atlas.ha.AtlasServerIdSelector; import org.apache.atlas.ha.HAConfiguration; -import org.apache.atlas.setup.SetupException; -import org.apache.atlas.setup.SetupStep; import org.apache.atlas.server.common.service.AtlasZookeeperSecurityProperties; import org.apache.atlas.server.common.service.CuratorFactory; +import org.apache.atlas.setup.SetupException; +import org.apache.atlas.setup.SetupStep; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.recipes.locks.InterProcessMutex; diff --git a/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java b/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java index 9de0f425bb..a34c405e95 100644 --- a/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java +++ b/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; +import org.apache.atlas.server.common.util.Servlets; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @@ -39,8 +40,6 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; -import org.apache.atlas.server.common.util.Servlets; - @Provider @Produces(MediaType.APPLICATION_JSON) @Component diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java index 44d8542440..89bbe8a49c 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerKafkaTest.java @@ -35,12 +35,12 @@ import org.apache.atlas.repository.impexp.AsyncImporter; import org.apache.atlas.repository.store.graph.AtlasEntityStore; import org.apache.atlas.repository.store.graph.v2.EntityStream; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.type.AtlasType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.util.AtlasMetricsUtil; import org.apache.atlas.v1.model.instance.Referenceable; import org.apache.atlas.v1.model.notification.HookNotificationV1; -import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.RandomStringUtils; import org.apache.kafka.clients.consumer.KafkaConsumer; diff --git a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java index 0865d82cb3..eccd676345 100644 --- a/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java +++ b/webapp/src/test/java/org/apache/atlas/notification/NotificationHookConsumerTest.java @@ -47,6 +47,7 @@ import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; import org.apache.atlas.repository.store.graph.v2.AtlasGraphUtilsV2; import org.apache.atlas.repository.store.graph.v2.EntityStream; +import org.apache.atlas.server.common.service.ServiceState; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasStructType; import org.apache.atlas.type.AtlasType; @@ -57,7 +58,6 @@ import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityCreateRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityDeleteRequest; import org.apache.atlas.v1.model.notification.HookNotificationV1.EntityUpdateRequest; -import org.apache.atlas.server.common.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.kafka.common.TopicPartition; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java index 5d9b7e9235..ff9ebd17b8 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.security.core.AuthenticationException; @@ -34,8 +35,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; - public class AtlasAuthenticationEntryPointTest { @Mock private HttpServletRequest request; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java index 16e5735472..1ee895844e 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java @@ -18,14 +18,16 @@ package org.apache.atlas.web.filters; -import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.AtlasException; import org.apache.atlas.security.SecurityProperties; -import org.apache.atlas.utils.AuthenticationUtil; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.RestUtil; import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AuthenticationUtil; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; import org.apache.commons.configuration.PropertiesConfiguration; @@ -98,9 +100,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.RestUtil; - public class AtlasAuthenticationFilterTest { private static final String AUTH_COOKIE = AuthenticatedURL.AUTH_COOKIE; private static final String TIMEOUT_ACTION = "timeout"; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java index 89d86ffdc2..cec8853837 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationKerberosFilterTest.java @@ -17,8 +17,8 @@ package org.apache.atlas.web.filters; import org.apache.atlas.RequestContext; -import org.apache.atlas.web.security.BaseSecurityTest; import org.apache.atlas.server.common.service.EmbeddedServer; +import org.apache.atlas.web.security.BaseSecurityTest; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; import org.apache.hadoop.hdfs.web.URLConnectionFactory; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java index b047799cd7..353ffa4264 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java @@ -17,6 +17,9 @@ */ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AtlasHeaderFilter; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; @@ -44,10 +47,6 @@ import static org.mockito.Mockito.verifyNoInteractions; import static org.testng.Assert.assertNotNull; -import org.apache.atlas.server.common.filters.AtlasHeaderFilter; -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.HeadersUtil; - public class AtlasHeaderFilterTest { @Mock private FilterConfig mockFilterConfig; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java index 4aaabb4f00..a30b467511 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasKnoxSSOAuthenticationFilterTest.java @@ -24,6 +24,7 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; import org.apache.atlas.server.common.filters.spi.AtlasAuthenticationProviderBridge; import org.apache.atlas.web.security.AtlasAuthenticationProvider; import org.apache.commons.configuration.Configuration; @@ -71,8 +72,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; - public class AtlasKnoxSSOAuthenticationFilterTest { @Mock private HttpServletRequest servletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java index ced9128888..bb8a777afb 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/AuditFilterTest.java @@ -17,9 +17,9 @@ */ package org.apache.atlas.web.filters; -import org.apache.atlas.server.common.filters.AuditFilter; import org.apache.atlas.DeleteType; import org.apache.atlas.RequestContext; +import org.apache.atlas.server.common.filters.AuditFilter; import org.apache.atlas.util.AtlasRepositoryConfiguration; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java index 3502ab0c61..167c6ca75c 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/HeaderUtilsTest.java @@ -17,6 +17,8 @@ */ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -30,9 +32,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; -import org.apache.atlas.server.common.filters.HeadersUtil; - public class HeaderUtilsTest { private Map originalHeaders; diff --git a/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java index 196181acf0..a95e4d2028 100644 --- a/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/filters/RestUtilTest.java @@ -17,6 +17,7 @@ */ package org.apache.atlas.web.filters; +import org.apache.atlas.server.common.filters.RestUtil; import org.testng.annotations.Test; import javax.servlet.http.HttpServletRequest; @@ -28,8 +29,6 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; -import org.apache.atlas.server.common.filters.RestUtil; - public class RestUtilTest { private static Enumeration enumeration(T... values) { return Collections.enumeration(java.util.Arrays.asList(values)); diff --git a/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java b/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java index 346f552707..3d2ba97d51 100755 --- a/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java +++ b/webapp/src/test/java/org/apache/atlas/web/integration/EntityV2JerseyResourceIT.java @@ -79,8 +79,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.util.Servlets; - /** * Integration tests for Entity Jersey Resource. */ diff --git a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java index 069b74a18f..755df2ab33 100644 --- a/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/resources/AdminResourceTest.java @@ -57,6 +57,8 @@ import org.apache.atlas.repository.impexp.MigrationProgressService; import org.apache.atlas.repository.patches.AtlasPatchManager; import org.apache.atlas.repository.store.graph.AtlasEntityStore; +import org.apache.atlas.server.common.service.ServiceState; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.services.MetricsService; import org.apache.atlas.services.PurgeService; import org.apache.atlas.tasks.TaskManagement; @@ -67,8 +69,6 @@ import org.apache.atlas.utils.AtlasJson; import org.apache.atlas.web.model.DebugMetrics; import org.apache.atlas.web.service.AtlasDebugMetricsSink; -import org.apache.atlas.server.common.service.ServiceState; -import org.apache.atlas.server.common.util.Servlets; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; import org.mockito.MockedStatic; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java index bbd06f7c50..1851da89a9 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/EntityRESTTest.java @@ -35,11 +35,11 @@ import org.apache.atlas.repository.converters.AtlasInstanceConverter; import org.apache.atlas.repository.store.graph.AtlasEntityStore; import org.apache.atlas.repository.store.graph.v2.EntityStream; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.type.AtlasClassificationType; import org.apache.atlas.type.AtlasEntityType; import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.atlas.util.FileUtils; -import org.apache.atlas.server.common.util.Servlets; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java index 35d44304ea..7d93eff314 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/RelationshipRESTTest.java @@ -22,8 +22,8 @@ import org.apache.atlas.model.instance.AtlasRelationship; import org.apache.atlas.model.instance.AtlasRelationship.AtlasRelationshipWithExtInfo; import org.apache.atlas.repository.store.graph.AtlasRelationshipStore; -import org.apache.atlas.utils.AtlasPerfTracer; import org.apache.atlas.server.common.util.Servlets; +import org.apache.atlas.utils.AtlasPerfTracer; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; diff --git a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java index 24189d257d..909b4bc7a9 100644 --- a/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/rest/TypesRESTTest.java @@ -30,10 +30,10 @@ import org.apache.atlas.model.typedef.AtlasTypeDefHeader; import org.apache.atlas.model.typedef.AtlasTypesDef; import org.apache.atlas.repository.util.FilterUtil; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.store.AtlasTypeDefStore; import org.apache.atlas.type.AtlasTypeUtil; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.server.common.util.Servlets; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java index d3db15024a..1cd6c20487 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasADAuthenticationProviderTest.java @@ -20,6 +20,9 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.mockito.Mock; @@ -51,10 +54,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; - public class AtlasADAuthenticationProviderTest { @Mock private Configuration mockConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java index 9b88d166cd..4dc6a855ca 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; import org.json.simple.JSONObject; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -44,8 +45,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; - public class AtlasAuthenticationFailureHandlerTest { @Mock private HttpServletRequest mockHttpServletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java index e32cc26cf1..c4107763ca 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java @@ -20,6 +20,11 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; import org.mockito.MockedStatic; @@ -49,12 +54,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.security.AtlasADAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; -import org.apache.atlas.server.common.security.AtlasFileAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasLdapAuthenticationProvider; -import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; - public class AtlasAuthenticationProviderTest { @Mock private AtlasLdapAuthenticationProvider mockLdapAuthenticationProvider; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java index 14574a5539..807ded4843 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandlerTest.java @@ -20,6 +20,7 @@ package org.apache.atlas.web.security; import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; import org.json.simple.JSONObject; import org.mockito.Mock; import org.mockito.MockedStatic; @@ -53,8 +54,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; - public class AtlasAuthenticationSuccessHandlerTest { @Mock private HttpServletRequest mockHttpServletRequest; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java index bee51b85a9..11e02e4aa1 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProviderTest.java @@ -19,8 +19,8 @@ package org.apache.atlas.web.security; -import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.keycloak.KeycloakSecurityContext; import org.keycloak.adapters.OidcKeycloakAccount; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java index 4ec6c2f6cd..c5c64c4e2f 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasPamAuthenticationProviderTest.java @@ -22,7 +22,9 @@ import org.apache.atlas.ApplicationProperties; import org.apache.atlas.server.common.model.User; import org.apache.atlas.server.common.security.AtlasAbstractAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.atlas.server.common.security.AtlasPamAuthenticationProvider; +import org.apache.atlas.server.common.security.UserAuthorityGranter; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationConverter; import org.mockito.Mock; @@ -62,9 +64,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; -import org.apache.atlas.server.common.security.UserAuthorityGranter; - public class AtlasPamAuthenticationProviderTest { @Mock private Configuration mockConfiguration; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java index 809c94af50..70e4e436b2 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java @@ -25,6 +25,8 @@ import org.apache.atlas.server.common.filters.AtlasCSRFPreventionFilter; import org.apache.atlas.server.common.filters.AtlasDelegatingAuthenticationEntryPoint; import org.apache.atlas.server.common.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; import org.apache.atlas.web.filters.StaleTransactionCleanupFilter; import org.apache.commons.configuration.Configuration; import org.keycloak.adapters.AdapterDeploymentContext; @@ -96,9 +98,6 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; -import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; - @SuppressWarnings("deprecation") public class AtlasSecurityConfigTest { @Mock diff --git a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java index 2c3e18ba2f..644e2e3d6a 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; import org.apache.atlas.web.TestUtils; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; @@ -47,8 +48,6 @@ import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNotNull; -import org.apache.atlas.server.common.security.AtlasAuthenticationException; - public class FileAuthenticationTest { private static final Logger LOG = LoggerFactory.getLogger(FileAuthenticationTest.class); diff --git a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java index 4600f17a92..99a0b3845d 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java @@ -20,6 +20,7 @@ package org.apache.atlas.web.security; import org.apache.atlas.server.common.security.PamLoginModule; +import org.apache.atlas.server.common.security.PamPrincipal; import org.jvnet.libpam.PAM; import org.jvnet.libpam.UnixUser; import org.mockito.Mock; @@ -55,8 +56,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.security.PamPrincipal; - public class PamLoginModuleTest { @Mock private Subject mockSubject; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java index e7c71b7f9b..890d9dc8fc 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/PamPrincipalTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.PamPrincipal; import org.jvnet.libpam.UnixUser; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -38,8 +39,6 @@ import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; -import org.apache.atlas.server.common.security.PamPrincipal; - public class PamPrincipalTest { @Mock private UnixUser mockUnixUser; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java b/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java index 8271cd6caa..ac2245367f 100755 --- a/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/SSLTest.java @@ -19,8 +19,8 @@ package org.apache.atlas.web.security; import org.apache.atlas.AtlasClient; -import org.apache.atlas.web.TestUtils; import org.apache.atlas.server.common.service.SecureEmbeddedServer; +import org.apache.atlas.web.TestUtils; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; diff --git a/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java index bc1366bcd1..94275f93fe 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java @@ -40,7 +40,6 @@ @Profile("testSpringSecurityBridge") @ImportResource("classpath:test-spring-security.xml") public class TestSpringSecurityBridgeConfig { - @Bean public AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge(AtlasAuthenticationProvider authenticationProvider) { return new AtlasAuthenticationProviderBridge() { diff --git a/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java b/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java index a1aded171d..d3a8bd8a66 100644 --- a/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java @@ -19,6 +19,7 @@ package org.apache.atlas.web.security; +import org.apache.atlas.server.common.security.UserAuthorityGranter; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.testng.annotations.BeforeMethod; @@ -32,8 +33,6 @@ import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; -import org.apache.atlas.server.common.security.UserAuthorityGranter; - public class UserAuthorityGranterTest { @Mock private Principal mockPrincipal; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java index 83bba800f5..c38bf70b22 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ActiveInstanceStateTest.java @@ -21,9 +21,9 @@ import org.apache.atlas.AtlasException; import org.apache.atlas.ha.HAConfiguration; import org.apache.atlas.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.atlas.server.common.service.HighAvailabilityProperties; import org.apache.atlas.server.common.service.HighAvailabilitySupport; -import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CreateBuilder; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java b/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java index 095f808854..5de6e20976 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/EmbeddedServerTest.java @@ -31,7 +31,6 @@ import java.io.IOException; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Date; diff --git a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java index 3cbba70b99..230125e360 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/UserServiceTest.java @@ -20,9 +20,9 @@ import org.apache.atlas.server.common.dao.UserDao; import org.apache.atlas.server.common.service.UserService; -import org.springframework.security.core.userdetails.UserDetails; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; diff --git a/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java b/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java index 1a3673f022..2356c629ca 100644 --- a/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/setup/SetupStepsTest.java @@ -21,9 +21,9 @@ import com.google.common.base.Charsets; import org.apache.atlas.AtlasConstants; import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.atlas.setup.SetupException; import org.apache.atlas.setup.SetupStep; -import org.apache.atlas.server.common.service.CuratorFactory; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang3.tuple.Pair; import org.apache.curator.framework.CuratorFramework; diff --git a/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java b/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java index 9b76bbe7df..6563902061 100644 --- a/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/util/DateTimeHelperTest.java @@ -18,6 +18,7 @@ package org.apache.atlas.web.util; +import org.apache.atlas.server.common.util.DateTimeHelper; import org.junit.Test; import java.text.DateFormat; @@ -33,8 +34,6 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import org.apache.atlas.server.common.util.DateTimeHelper; - public class DateTimeHelperTest { @Test public void testConstantsAndPattern() { diff --git a/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java b/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java index a8de350eeb..4e438e72f0 100644 --- a/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/util/ServletsTest.java @@ -20,14 +20,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.apache.atlas.AtlasClient; +import org.apache.atlas.server.common.util.Servlets; import org.testng.annotations.Test; import javax.ws.rs.core.Response; import static org.testng.Assert.assertNotNull; -import org.apache.atlas.server.common.util.Servlets; - @Test public class ServletsTest { public void testEmptyMessage() throws Exception { From f1ce7bd6c1928b231c8eb39fb3b1084af0bf9e26 Mon Sep 17 00:00:00 2001 From: Umesh Patil Date: Thu, 7 May 2026 21:20:53 +0530 Subject: [PATCH 22/22] Updating server-common to atlas-server-common. --- pom.xml | 12 ++++++------ rest-notification-webapp/pom.xml | 2 +- server-common/pom.xml | 2 +- .../common/service/HighAvailabilitySupport.java | 2 +- webapp/pom.xml | 2 +- .../apache/atlas/web/service/ServiceStateTest.java | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index a4d6accc78..3bb49cc59f 100644 --- a/pom.xml +++ b/pom.xml @@ -494,6 +494,12 @@ ${project.version} + + org.apache.atlas + atlas-server-common + ${project.version} + + org.apache.atlas atlas-webapp @@ -567,12 +573,6 @@ ${project.version} - - org.apache.atlas - server-common - ${project.version} - - org.apache.atlas sqoop-bridge diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml index 7885ca179a..8a308053a5 100644 --- a/rest-notification-webapp/pom.xml +++ b/rest-notification-webapp/pom.xml @@ -83,7 +83,7 @@ org.apache.atlas - server-common + atlas-server-common com.google.guava diff --git a/server-common/pom.xml b/server-common/pom.xml index 78758f6e7b..8307e9e802 100644 --- a/server-common/pom.xml +++ b/server-common/pom.xml @@ -25,7 +25,7 @@ 3.0.0-SNAPSHOT - server-common + atlas-server-common jar Apache Atlas Server Common Shared server-side infrastructure for webapp modules diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java index df046a0b82..b371c2fa69 100644 --- a/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailabilitySupport.java @@ -24,7 +24,7 @@ /** * Interface to abstract High Availability (HA) configuration retrieval. - * Enables shared services in 'server-common' to operate without direct + * Enables shared services in {@code atlas-server-common} to operate without direct * dependencies on application-specific configuration classes. */ public interface HighAvailabilitySupport { diff --git a/webapp/pom.xml b/webapp/pom.xml index 8929475f01..6caa8d7d9d 100755 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -215,7 +215,7 @@ org.apache.atlas - server-common + atlas-server-common diff --git a/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java b/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java index aaf1996902..2c87f31728 100644 --- a/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java +++ b/webapp/src/test/java/org/apache/atlas/web/service/ServiceStateTest.java @@ -194,7 +194,7 @@ public void testShouldDisallowSetMigrationIfHAIsDisabled() { @Test public void testAuditServerStatusWithException() throws Exception { if (!hasAuditServiceField()) { - throw new SkipException("ServiceState no longer exposes auditService field in server-common design."); + throw new SkipException("ServiceState no longer exposes auditService field in atlas-server-common design."); } ServiceState serviceState = newServiceState(true);