diff --git a/pom.xml b/pom.xml index 8d27e11ed6..3bb49cc59f 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,9 @@ notification plugin-classloader repository + rest-notification-webapp server-api + server-common test-tools tools/atlas-index-repair tools/classification-updater @@ -492,6 +494,12 @@ ${project.version} + + org.apache.atlas + atlas-server-common + ${project.version} + + org.apache.atlas atlas-webapp diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml new file mode 100644 index 0000000000..8a308053a5 --- /dev/null +++ b/rest-notification-webapp/pom.xml @@ -0,0 +1,232 @@ + + + + 4.0.0 + + org.apache.atlas + apache-atlas + 3.0.0-SNAPSHOT + + + rest-notification-webapp + war + + Rest Notification Webapp + + + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + + + ch.qos.logback + logback-classic + + + ch.qos.logback + logback-core + + + junit + junit + ${junit.version} + test + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-webapp + + + org.springframework + spring-web + + + org.springframework + spring-aop + + + org.aspectj + aspectjrt + 1.8.9 + + + org.aspectj + aspectjweaver + 1.8.9 + + + org.apache.atlas + atlas-intg + + + + org.apache.atlas + atlas-server-common + + + com.google.guava + guava + + + org.apache.atlas + atlas-notification + + + org.apache.atlas + atlas-client-v2 + + + org.apache.atlas + atlas-authorization + ${project.version} + + + 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-conf2.version} + + + org.apache.commons + commons-text + + + + + + org.apache.commons + commons-text + ${commons-text.version} + + + + commons-lang + commons-lang + 2.6 + + + + com.googlecode.json-simple + json-simple + + + + + org.kohsuke + libpam4j + 1.11 + + + + + com.sun.jersey + jersey-client + + + com.sun.jersey + jersey-core + + + + com.sun.jersey + jersey-server + + + + com.sun.jersey + jersey-servlet + + + + com.sun.jersey.contribs + jersey-multipart + + + + com.sun.jersey.contribs + jersey-spring + + + + com.sun.jersey + jersey-json + ${jersey.version} + + + + javax.servlet + javax.servlet-api + provided + + + + + + + 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/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/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..af279de5cf --- /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.server.common.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/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/util/CredentialProviderUtility.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/util/CredentialProviderUtility.java new file mode 100755 index 0000000000..d50368cfa5 --- /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.server.common.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/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..33d1fe3545 --- /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.server.common.util.Servlets; +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; +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..24e030aade --- /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.server.common.util.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/AtlasSecurityConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java new file mode 100644 index 0000000000..3158580af2 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.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.security; + +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; +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.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.server.common.security.AtlasAuthenticationProvider; +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; +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; + +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 { + 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); + } + + @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/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..a969617f1e --- /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.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; +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/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..06647d59e4 --- /dev/null +++ b/rest-notification-webapp/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,89 @@ + + + + + Atlas Rest Notification 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.server.common.setup.KerberosAwareListener + + + + AuditFilter + org.apache.atlas.server.common.filters.AuditFilter + + + + AuditFilter + /* + + + + HeaderFilter + org.apache.atlas.server.common.filters.AtlasHeaderFilter + + + + HeaderFilter + /api/atlas/admin/status + + + + diff --git a/server-common/pom.xml b/server-common/pom.xml new file mode 100644 index 0000000000..8307e9e802 --- /dev/null +++ b/server-common/pom.xml @@ -0,0 +1,182 @@ + + + + 4.0.0 + + + org.apache.atlas + apache-atlas + 3.0.0-SNAPSHOT + + + atlas-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.nimbusds + nimbus-jose-jwt + 10.0.2 + compile + + + org.bouncycastle + bcprov-jdk15on + + + + + 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-authorization + ${project.version} + + + org.apache.atlas + atlas-client-v1 + + + org.apache.atlas + atlas-common + + + org.apache.atlas + atlas-intg + + + org.apache.atlas + atlas-server-api + + + 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 + + + org.apache.httpcomponents + httpclient + + + org.apache.zookeeper + zookeeper + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-webapp + + + org.kohsuke + libpam4j + 1.11 + + + 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 + + + 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/webapp/src/main/java/org/apache/atlas/LocalServletRequest.java b/server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java similarity index 94% rename from webapp/src/main/java/org/apache/atlas/LocalServletRequest.java rename to server-common/src/main/java/org/apache/atlas/server/common/LocalServletRequest.java index be3bcb4ea6..3bf79ae025 100644 --- a/webapp/src/main/java/org/apache/atlas/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; +package org.apache.atlas.server.common; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; @@ -32,9 +32,9 @@ 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; @@ -193,22 +193,22 @@ public void login(String username, String password) throws ServletException { } @Override - public void logout() { + public void logout() throws ServletException { throw new IllegalStateException("Not supported"); } @Override - public Collection getParts() { + public Collection getParts() throws IOException, ServletException { throw new IllegalStateException("Not supported"); } @Override - public Part getPart(String name) { + public Part getPart(String name) throws IOException, ServletException { throw new IllegalStateException("Not supported"); } @Override - public T upgrade(Class handlerClass) { + public T upgrade(Class handlerClass) throws IOException, ServletException { throw new IllegalStateException("Not supported"); } @@ -228,7 +228,7 @@ public String getCharacterEncoding() { } @Override - public void setCharacterEncoding(String env) { + public void setCharacterEncoding(String env) throws UnsupportedEncodingException { throw new IllegalStateException("Not supported"); } @@ -248,7 +248,7 @@ public String getContentType() { } @Override - public ServletInputStream getInputStream() { + public ServletInputStream getInputStream() throws IOException { throw new IllegalStateException("Not supported"); } @@ -293,7 +293,7 @@ public int getServerPort() { } @Override - public BufferedReader getReader() { + public BufferedReader getReader() throws IOException { throw new IllegalStateException("Not supported"); } 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 89c768800a..efccdea165 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,19 +15,20 @@ * 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.web.security.AtlasAuthenticationException; +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; 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.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/web/filters/ActiveServerFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/ActiveServerFilter.java similarity index 84% 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..de800f4328 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,22 +43,26 @@ * 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); 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"}; - private final ActiveInstanceState activeInstanceState; - private final ServiceState serviceState; + "/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" + }; - @Inject - public ActiveServerFilter(ActiveInstanceState activeInstanceState, ServiceState serviceState) { + private final ActiveInstanceStateProvider activeInstanceState; + private final ServiceStateProvider serviceState; + + public ActiveServerFilter(ActiveInstanceStateProvider activeInstanceState, ServiceStateProvider serviceState) { this.activeInstanceState = activeInstanceState; this.serviceState = serviceState; } @@ -81,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()) { @@ -126,15 +127,17 @@ public void destroy() { } boolean isInstanceActive() { - return serviceState.getState() == ServiceState.ServiceStateValue.ACTIVE; + 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); @@ -143,9 +146,9 @@ private boolean isFilteredURI(ServletRequest servletRequest) { } return true; - } else { - return false; } + + return false; } private boolean isRootURI(ServletRequest servletRequest) { diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java similarity index 83% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPoint.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasAuthenticationEntryPoint.java index 7526c18022..62368c4729 100644 --- a/webapp/src/main/java/org/apache/atlas/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.web.filters; +package org.apache.atlas.server.common.filters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,16 +24,18 @@ 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 static final String LOGIN_PATH = "/login.jsp"; + private String loginPath = "/login.jsp"; @Inject public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { @@ -41,17 +43,18 @@ public AtlasAuthenticationEntryPoint(@Value("/login.jsp") String loginFormUrl) { } @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { - String ajaxRequestHeader = request.getHeader("X-Requested-With"); + 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 {}", LOGIN_PATH); - - response.sendRedirect(LOGIN_PATH); + LOG.debug("redirecting to login page loginPath" + loginPath); + response.sendRedirect(loginPath); } } } 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 96% 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 652af3a86e..56fb1e4ca3 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,14 +16,15 @@ * 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.web.util.Servlets; +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; import org.apache.commons.configuration.ConfigurationConverter; @@ -49,12 +50,11 @@ 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.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; @@ -87,14 +88,18 @@ 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. * todo: Subclass of {@link AuthenticationFilter}. */ -@Component public class AtlasAuthenticationFilter extends AuthenticationFilter { private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class); @@ -110,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; @@ -125,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 { @@ -258,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 { @@ -445,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<>(); @@ -455,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); @@ -776,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 98% 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 7dc12acc37..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; @@ -44,7 +43,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -@Component +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; + 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/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java similarity index 76% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasDelegatingAuthenticationEntryPoint.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasDelegatingAuthenticationEntryPoint.java index 09cb1b9977..1115c74206 100644 --- a/webapp/src/main/java/org/apache/atlas/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.web.filters; +package org.apache.atlas.server.common.filters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,34 +25,38 @@ 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"; public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { super(entryPoints); - - LOG.debug("AtlasDelegatingAuthenticationEntryPoint-AjaxAwareAuthenticationEntryPoint(): constructor"); + 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); + 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 (ajaxRequestHeader != null + && HeadersUtil.X_REQUESTED_WITH_VALUE.equalsIgnoreCase(ajaxRequestHeader)) { if (LOG.isDebugEnabled()) { - LOG.debug("commence() AJAX request. Authentication required. Returning {}. URL={}", HttpServletResponse.SC_UNAUTHORIZED, request.getRequestURI()); + 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()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, + authException.getMessage()); } } } diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasHeaderFilter.java similarity index 85% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasHeaderFilter.java index 7fe2c42b4f..b6fb170c70 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasHeaderFilter.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasHeaderFilter.java @@ -15,7 +15,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -24,27 +27,28 @@ 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 { + 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); } + + @Override + public void destroy() { + } } 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 96% 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 a5dca7aaee..cbd2e08b2f 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,8 @@ 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.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,10 +39,8 @@ 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; @@ -72,7 +71,10 @@ import java.util.List; import java.util.Map; -@Component("ssoAuthenticationFilter") +import org.apache.atlas.server.common.filters.AtlasResponseRequestWrapper; +import org.apache.atlas.server.common.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.SSOAuthenticationProperties; + public class AtlasKnoxSSOAuthenticationFilter implements Filter { private static final Logger LOG = LoggerFactory.getLogger(AtlasKnoxSSOAuthenticationFilter.class); @@ -90,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"; @@ -102,7 +104,7 @@ public class AtlasKnoxSSOAuthenticationFilter implements Filter { private JWSVerifier verifier; @Inject - public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProvider authenticationProvider) { + public AtlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge authenticationProvider) { this.authenticationProvider = authenticationProvider; try { @@ -119,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; @@ -219,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); @@ -300,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(); @@ -325,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("?")) { @@ -375,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/webapp/src/main/java/org/apache/atlas/web/filters/AtlasResponseRequestWrapper.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasResponseRequestWrapper.java similarity index 95% rename from webapp/src/main/java/org/apache/atlas/web/filters/AtlasResponseRequestWrapper.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasResponseRequestWrapper.java index f8b7b32c6f..2631eee0a6 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasResponseRequestWrapper.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/AtlasResponseRequestWrapper.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; 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 88% 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 12d34353ba..a154b84a2c 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.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; 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; @@ -128,12 +130,22 @@ public void destroy() { // do nothing } - boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { - try { - return AtlasRepositoryConfiguration.isExcludedFromAudit(config, requestHttpMethod, requestOperation); - } catch (AtlasException e) { - return false; + public boolean isOperationExcludedFromAudit(String requestHttpMethod, String requestOperation, Configuration config) { + 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/HeadersUtil.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/HeadersUtil.java similarity index 97% rename from webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/HeadersUtil.java index dbec3cdbfa..d5663a7f93 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/HeadersUtil.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/HeadersUtil.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import com.google.common.annotations.VisibleForTesting; import org.apache.atlas.ApplicationProperties; @@ -24,12 +24,14 @@ 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); @@ -56,7 +58,6 @@ public class HeadersUtil { private HeadersUtil() { // to block instantiation } - public static String getHeaderMap(String header) { return HEADER_MAP.get(header); } @@ -105,4 +106,4 @@ public static void initializeHttpResponseHeaders(Properties configuredHeaders) { initializeHttpResponseHeaders(configuredHeaders); } -} +} \ No newline at end of file diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java similarity index 77% rename from webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java rename to server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java index 60801aac64..e92e6f03c2 100644 --- a/webapp/src/main/java/org/apache/atlas/web/filters/NullServletContext.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/filters/NullServletContext.java @@ -2,9 +2,9 @@ * 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 - *

+ * + * 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. @@ -12,7 +12,7 @@ * limitations under the License. See accompanying LICENSE file. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import javax.servlet.Filter; import javax.servlet.FilterRegistration; @@ -20,217 +20,309 @@ 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 String getContextPath() { - return null; + + + + public void setSessionTrackingModes( + Set sessionTrackingModes) { } - public ServletContext getContext(String uripath) { + + 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; } - public int getMajorVersion() { + public int getSessionTimeout() { return 0; } - public int getMinorVersion() { - return 0; + public void setSessionTimeout(int i) { } - public int getEffectiveMajorVersion() { - return 0; + public String getRequestCharacterEncoding() { + return ""; } - public int getEffectiveMinorVersion() { - return 0; + public void setRequestCharacterEncoding(String s) { } - public String getMimeType(String file) { + public String getResponseCharacterEncoding() { + return ""; + } + + public void setResponseCharacterEncoding(String s) { + } + + + public SessionCookieConfig getSessionCookieConfig() { return null; } - public Set getResourcePaths(String path) { + + public Enumeration getServlets() { return null; } - public URL getResource(String path) { + + public Map getServletRegistrations() { return null; } - public InputStream getResourceAsStream(String path) { + + public ServletRegistration getServletRegistration(String servletName) { return null; } - public RequestDispatcher getRequestDispatcher(String path) { + + public Enumeration getServletNames() { return null; } - public RequestDispatcher getNamedDispatcher(String name) { + + public String getServletContextName() { return null; } - public Servlet getServlet(String name) { + + public Servlet getServlet(String name) throws ServletException { return null; } - public Enumeration getServlets() { + + public String getServerInfo() { return null; } - public Enumeration getServletNames() { + + public Set getResourcePaths(String path) { return null; } - public void log(String msg) { + + public InputStream getResourceAsStream(String path) { + return null; } - public void log(Exception exception, String msg) { + + public URL getResource(String path) throws MalformedURLException { + return null; } - public void log(String message, Throwable throwable) { + + public RequestDispatcher getRequestDispatcher(String path) { + return null; } + public String getRealPath(String path) { return null; } - public String getServerInfo() { + + public RequestDispatcher getNamedDispatcher(String name) { return null; } - public String getInitParameter(String name) { - return null; + + public int getMinorVersion() { + return 0; } - public Enumeration getInitParameterNames() { + + public String getMimeType(String file) { return null; } - public boolean setInitParameter(String name, String value) { - return false; + + public int getMajorVersion() { + return 0; } - public Object getAttribute(String name) { + + public JspConfigDescriptor getJspConfigDescriptor() { return null; } - public Enumeration getAttributeNames() { + + public Enumeration getInitParameterNames() { return null; } - public void setAttribute(String name, Object object) { - } - public void removeAttribute(String name) { + public String getInitParameter(String name) { + return null; } - public String getServletContextName() { + + public Map getFilterRegistrations() { return null; } - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, String className) { + + public FilterRegistration getFilterRegistration(String filterName) { return null; } - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Servlet servlet) { + + public Set getEffectiveSessionTrackingModes() { return null; } - public javax.servlet.ServletRegistration.Dynamic addServlet(String servletName, Class servletClass) { - return null; + + public int getEffectiveMinorVersion() { + return 0; + } + + + public int getEffectiveMajorVersion() { + return 0; } - public T createServlet(Class clazz) { + + public Set getDefaultSessionTrackingModes() { return null; } - public ServletRegistration getServletRegistration(String servletName) { + + public String getContextPath() { return null; } - public Map getServletRegistrations() { + + public ServletContext getContext(String uripath) { return null; } - public Dynamic addFilter(String filterName, String className) { + + public ClassLoader getClassLoader() { return null; } - public Dynamic addFilter(String filterName, Filter filter) { + + public Enumeration getAttributeNames() { return null; } - public Dynamic addFilter(String filterName, - Class filterClass) { + + public Object getAttribute(String name) { return null; } - public T createFilter(Class clazz) { + + public void declareRoles(String... roleNames) { + } + + + public T createServlet(Class clazz) + throws ServletException { return null; } - public FilterRegistration getFilterRegistration(String filterName) { + + public T createListener(Class clazz) + throws ServletException { return null; } - public Map getFilterRegistrations() { + + public T createFilter(Class clazz) + throws ServletException { return null; } - public SessionCookieConfig getSessionCookieConfig() { + + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, Class servletClass) { return null; } - public void setSessionTrackingModes(Set sessionTrackingModes) { + + public ServletRegistration.Dynamic addJspFile(String s, String s1) { + return null; } - public Set getDefaultSessionTrackingModes() { + + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, Servlet servlet) { return null; } - public Set getEffectiveSessionTrackingModes() { + + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, String className) { return null; } - public void addListener(String className) { + + public void addListener(Class listenerClass) { } + public void addListener(T t) { } - public void addListener(Class listenerClass) { - } - public T createListener(Class clazz) { - return null; + public void addListener(String className) { } - public JspConfigDescriptor getJspConfigDescriptor() { + + public Dynamic addFilter(String filterName, + Class filterClass) { return null; } - public ClassLoader getClassLoader() { + + public Dynamic addFilter(String filterName, Filter filter) { return null; } - public void declareRoles(String... roleNames) { - } - public String getVirtualServerName() { + 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 85% 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..26f92a2757 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,7 +16,7 @@ * 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.slf4j.Logger; @@ -59,7 +59,7 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { } } - 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)) { @@ -71,18 +71,18 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { } if (xForwardedHost.contains(",")) { - LOG.debug("xForwardedHost value is {} it contains multiple hosts, selecting the first host.", xForwardedHost); + 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 { @@ -90,7 +90,7 @@ public static String constructForwardableURL(HttpServletRequest httpRequest) { //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); } 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/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 97% 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..3e260ce904 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,7 +17,7 @@ * under the License. */ -package org.apache.atlas.web.filters; +package org.apache.atlas.server.common.filters; import java.security.interfaces.RSAPublicKey; 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/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/model/User.java b/server-common/src/main/java/org/apache/atlas/server/common/model/User.java similarity index 98% rename from webapp/src/main/java/org/apache/atlas/web/model/User.java rename to server-common/src/main/java/org/apache/atlas/server/common/model/User.java index c7bbdceb00..be664b62e1 100644 --- a/webapp/src/main/java/org/apache/atlas/web/model/User.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/model/User.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.model; +package org.apache.atlas.server.common.model; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; 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 59329a0643..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; @@ -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/AtlasAbstractAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAbstractAuthenticationProvider.java similarity index 98% 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..7c3014d4e4 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,8 +17,9 @@ * under the License. */ -package org.apache.atlas.web.security; +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/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 93% 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..452b254869 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,7 +36,7 @@ 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(); 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 new file mode 100644 index 0000000000..3878ad26f8 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java @@ -0,0 +1,164 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.atlas.server.common.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("atlasServerCommonAuthenticationProvider") +@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; + + 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."); + } + + @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; + } + + public void setSsoEnabled(boolean ssoEnabled) { + this.ssoEnabled = ssoEnabled; + } + + private Authentication getSSOAuthentication(Authentication authentication) throws AuthenticationException{ + return authentication; + } +} diff --git a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java similarity index 81% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java index 5d6834fd19..c73a09e130 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationSuccessHandler.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationSuccessHandler.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.AtlasConfiguration; import org.json.simple.JSONObject; @@ -27,18 +27,18 @@ 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 final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationSuccessHandler.class); - - public static final String LOCALLOGIN = "locallogin"; + private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); private int sessionTimeout = 3600; + public static final String LOCALLOGIN = "locallogin"; @PostConstruct public void setup() { @@ -46,22 +46,22 @@ public void setup() { } @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - LOG.debug("Login Success {}", authentication.getPrincipal()); + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { - JSONObject json = new JSONObject(); + 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) { request.getSession().setMaxInactiveInterval(sessionTimeout); } } - response.setContentType("application/json"); response.setStatus(HttpServletResponse.SC_OK); response.setCharacterEncoding("UTF-8"); 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 96% 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 096ca26d83..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,9 +14,9 @@ * 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.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/security/AtlasLdapAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasLdapAuthenticationProvider.java similarity index 98% 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 c401576006..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; @@ -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/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasPamAuthenticationProvider.java similarity index 95% 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 49970129c3..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; @@ -42,11 +42,14 @@ 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); - 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 98% 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 8ed9daef84..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; @@ -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/PamPrincipal.java b/server-common/src/main/java/org/apache/atlas/server/common/security/PamPrincipal.java similarity index 97% 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..9fb5759bf8 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; diff --git a/webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java b/server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java similarity index 95% rename from webapp/src/main/java/org/apache/atlas/web/security/UserAuthorityGranter.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/UserAuthorityGranter.java index defd54e413..b691692ed8 100644 --- a/webapp/src/main/java/org/apache/atlas/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.web.security; +package org.apache.atlas.server.common.security; import org.springframework.security.authentication.jaas.AuthorityGranter; @@ -26,6 +26,7 @@ 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/service/ActiveInstanceElectorService.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java similarity index 83% 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..4e78263bdf 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 @@ -16,14 +16,12 @@ * 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; @@ -35,6 +33,7 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; @@ -60,7 +59,8 @@ 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; @@ -74,15 +74,21 @@ public class ActiveInstanceElectorService implements Service, LeaderLatchListene * @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; } /** @@ -93,11 +99,16 @@ public class ActiveInstanceElectorService implements Service, LeaderLatchListene */ @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; @@ -105,7 +116,7 @@ public void start() throws AtlasException { cacheActiveStateChangeHandlers(); - serverId = AtlasServerIdSelector.selectServerId(configuration); + serverId = haSupport.selectServerId(configuration); joinElection(); } @@ -117,7 +128,7 @@ public void start() throws AtlasException { */ @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; @@ -140,22 +151,24 @@ public void stop() { @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(); } } @@ -182,7 +195,7 @@ 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); 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 76% 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..89486c8ebd 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,14 +15,12 @@ * 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; @@ -50,13 +48,14 @@ * 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 HighAvailabilitySupport haSupport; /** * Create a new instance of {@link ActiveInstanceState}. @@ -64,18 +63,10 @@ public class ActiveInstanceState { * @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; } /** @@ -87,12 +78,16 @@ 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(); + CuratorFramework client = curatorFactory.clientInstance(); - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); - String atlasServerAddress = HAConfiguration.getBoundAddressForId(configuration, serverId); + String atlasServerAddress = haSupport.getBoundAddressForId(configuration, serverId); List acls = new ArrayList<>(); @@ -128,14 +123,22 @@ public void update(String serverId) throws AtlasBaseException { * 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; + if (!haSupport.isHAEnabled(configuration)) { + return null; + } - try { - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); + CuratorFramework client = curatorFactory.clientInstance(); + String serverAddress = null; - byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); + if (client == null) { + return null; + } + + try { + HighAvailabilityProperties zookeeperProperties = haSupport.getZookeeperProperties(configuration); + byte[] bytes = client.getData().forPath(getZnodePath(zookeeperProperties)); serverAddress = new String(bytes, StandardCharsets.UTF_8); } catch (Exception e) { @@ -145,7 +148,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 85% 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..c7917a081d 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 @@ -16,13 +16,11 @@ * 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,10 +35,11 @@ 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; /** @@ -63,19 +62,18 @@ public class CuratorFactory { private final Configuration configuration; private CuratorFramework curatorFramework; + private final HighAvailabilitySupport haSupport; /** * 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)) { + if (haSupport.isHAEnabled(configuration)) { initializeCuratorFramework(); } } @@ -86,7 +84,9 @@ public CuratorFactory(Configuration configuration) { * After this call, no further calls to any curator objects should be done. */ public void close() { - curatorFramework.close(); + if (curatorFramework != null) { + curatorFramework.close(); + } } /** @@ -118,8 +118,8 @@ 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); @@ -129,9 +129,9 @@ protected void initializeCuratorFramework() { } @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()); @@ -160,7 +160,7 @@ private String getCurrentUser() { } } - private ACLProvider getAclProvider(HAConfiguration.ZookeeperProperties zookeeperProperties) { + private ACLProvider getAclProvider(HighAvailabilityProperties zookeeperProperties) { ACLProvider aclProvider = null; if (zookeeperProperties.hasAcl()) { @@ -169,7 +169,7 @@ private ACLProvider getAclProvider(HAConfiguration.ZookeeperProperties zookeeper 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 @@ -198,7 +198,7 @@ private String getIdForLogging(String scheme, String id) { return "unknown"; } - private CuratorFrameworkFactory.Builder getBuilder(HAConfiguration.ZookeeperProperties zookeeperProperties) { + private CuratorFrameworkFactory.Builder getBuilder(HighAvailabilityProperties zookeeperProperties) { return CuratorFrameworkFactory.builder() .connectString(zookeeperProperties.getConnectString()) .sessionTimeoutMs(zookeeperProperties.getSessionTimeout()) 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..9df6fb0ac9 --- 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; @@ -44,16 +39,16 @@ /** * This class embeds a Jetty server and a connector. */ + 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 +73,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 +122,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..b371c2fa69 --- /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 {@code atlas-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/webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java similarity index 58% rename from webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java rename to server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.java index b2e7dd705a..0c7a0ec470 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/ServiceState.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceState.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,26 +16,18 @@ * 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.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.atlas.server.common.filters.spi.ServiceStateProvider; 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.Inject; 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; @@ -47,26 +39,29 @@ */ @Singleton @Component -public class ServiceState { +public class ServiceState implements ServiceStateProvider { private static final Logger LOG = LoggerFactory.getLogger(ServiceState.class); - @Autowired - AtlasAuditService auditService; - - 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; - 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 = !HAConfiguration.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; } } @@ -75,75 +70,57 @@ 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); } + @Override public boolean isInstanceInTransition() { ServiceStateValue state = getState(); - - return state == ServiceStateValue.BECOMING_ACTIVE || state == ServiceStateValue.BECOMING_PASSIVE; + return state == ServiceStateValue.BECOMING_ACTIVE + || state == ServiceStateValue.BECOMING_PASSIVE; } public void setMigration() { LOG.warn("Instance in {}", state); - setState(ServiceStateValue.MIGRATING); } + @Override 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(); - } - } + @Override + public boolean isActive() { + return getState() == ServiceStateValue.ACTIVE; } - public enum ServiceStateValue { - ACTIVE, - PASSIVE, - BECOMING_ACTIVE, - BECOMING_PASSIVE, - MIGRATING + @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; } } 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 83% 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 e0ea2b2c02..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,10 +15,10 @@ * limitations under the License. */ -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.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/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 91% 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 5fb9848855..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,9 +15,9 @@ * 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.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/util/DateTimeHelper.java b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java old mode 100755 new mode 100644 similarity index 64% rename from webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java rename to server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java index dbdcd98e42..f2cfb8c184 --- a/webapp/src/main/java/org/apache/atlas/web/util/DateTimeHelper.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/util/DateTimeHelper.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.atlas.web.util; +package org.apache.atlas.server.common.util; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -28,17 +28,20 @@ * 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); + 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); - dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - - return dateFormat; - }); + 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() { } @@ -50,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/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 94% 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..3b4f8c93b2 --- 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; @@ -58,7 +58,7 @@ public final class Servlets { 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 = StandardCharsets.UTF_8; + private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); private static final String DO_AS = "doAs"; private Servlets() { @@ -149,14 +149,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) { @@ -226,7 +228,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..6caa8d7d9d 100755 --- a/webapp/pom.xml +++ b/webapp/pom.xml @@ -213,6 +213,11 @@ atlas-server-api + + org.apache.atlas + atlas-server-common + + org.apache.commons commons-collections4 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 32bf301667..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.web.filters.AuditFilter; -import org.apache.atlas.web.filters.AuditFilter.AuditLog; -import org.apache.atlas.web.service.ServiceState; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.collections4.map.PassiveExpiringMap; @@ -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/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/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/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/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 b092d48068..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.web.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.web.util.Servlets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; @@ -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/resources/DataSetLineageResource.java b/webapp/src/main/java/org/apache/atlas/web/resources/DataSetLineageResource.java index 0c14be1988..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.web.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..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.web.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..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.web.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..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.web.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..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.web.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..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.web.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..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.web.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..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.server.common.util.Servlets; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.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..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.server.common.util.DateTimeHelper; +import org.apache.atlas.server.common.util.Servlets; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.util.DateTimeHelper; -import org.apache.atlas.web.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..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.web.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..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.server.common.util.Servlets; import org.apache.atlas.utils.AtlasJson; -import org.apache.atlas.web.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..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.server.common.util.Servlets; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.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..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.web.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 3cff9fb8ee..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; 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 5058ebdf53..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 @@ -17,13 +17,15 @@ */ package org.apache.atlas.web.security; -import org.apache.atlas.web.filters.ActiveServerFilter; -import org.apache.atlas.web.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.web.filters.AtlasKnoxSSOAuthenticationFilter; -import org.apache.atlas.web.filters.HeadersUtil; +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; +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.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; @@ -80,7 +82,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/AtlasSecurityFilterBeansConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java new file mode 100644 index 0000000000..5bdb667864 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityFilterBeansConfig.java @@ -0,0 +1,71 @@ +/** + * 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.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() { + @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/AtlasSecurityStateProviderConfig.java b/webapp/src/main/java/org/apache/atlas/web/security/AtlasSecurityStateProviderConfig.java new file mode 100644 index 0000000000..d6cdb639be --- /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.server.common.service.ActiveInstanceState; +import org.apache.atlas.server.common.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/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/setup/SetupSteps.java b/webapp/src/main/java/org/apache/atlas/web/setup/SetupSteps.java index b7f5ec4a39..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.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.atlas.web.service.AtlasZookeeperSecurityProperties; -import org.apache.atlas.web.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/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java b/webapp/src/main/java/org/apache/atlas/web/util/AtlasJsonProvider.java index e675a36457..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; diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml index 3020690dca..a507b14b4a 100644 --- a/webapp/src/main/resources/spring-security.xml +++ b/webapp/src/main/resources/spring-security.xml @@ -54,23 +54,23 @@ - + - + - + + 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..f340d990ea 100755 --- a/webapp/src/main/webapp/WEB-INF/web.xml +++ b/webapp/src/main/webapp/WEB-INF/web.xml @@ -74,7 +74,7 @@ AuditFilter - org.apache.atlas.web.filters.AuditFilter + org.apache.atlas.server.common.filters.AuditFilter @@ -84,7 +84,7 @@ HeaderFilter - org.apache.atlas.web.filters.AtlasHeaderFilter + org.apache.atlas.server.common.filters.AtlasHeaderFilter @@ -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/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); 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..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.web.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 28693f767c..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.web.service.ServiceState; import org.apache.commons.configuration.Configuration; import org.apache.kafka.common.TopicPartition; import org.mockito.Mock; @@ -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/dao/UserDaoTest.java b/webapp/src/test/java/org/apache/atlas/web/dao/UserDaoTest.java index f424b34809..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.web.security.AtlasAuthenticationException; +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/filters/ActiveServerFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/ActiveServerFilterTest.java index 51d40a49d0..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 @@ -18,8 +18,10 @@ package org.apache.atlas.web.filters; -import org.apache.atlas.web.service.ActiveInstanceState; -import org.apache.atlas.web.service.ServiceState; +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.spi.ServiceStateProvider; +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; @@ -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/AtlasAuthenticationEntryPointTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationEntryPointTest.java index 8d2c747489..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,12 +18,14 @@ 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; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -52,7 +54,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"); @@ -65,7 +67,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/filters/AtlasAuthenticationFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasAuthenticationFilterTest.java index 9057ae5798..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 @@ -22,9 +22,12 @@ import org.apache.atlas.AtlasConfiguration; import org.apache.atlas.AtlasException; import org.apache.atlas.security.SecurityProperties; +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.atlas.web.security.AtlasAbstractAuthenticationProvider; -import org.apache.atlas.web.util.Servlets; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.MapConfiguration; import org.apache.commons.configuration.PropertiesConfiguration; 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..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.server.common.service.EmbeddedServer; import org.apache.atlas.web.security.BaseSecurityTest; -import org.apache.atlas.web.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/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/AtlasHeaderFilterTest.java b/webapp/src/test/java/org/apache/atlas/web/filters/AtlasHeaderFilterTest.java index 8e27e85711..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; 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..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 @@ -23,6 +23,9 @@ 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.SSOAuthenticationProperties; +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; @@ -117,6 +120,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); @@ -145,7 +167,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); @@ -170,7 +192,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); @@ -196,7 +218,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); @@ -236,7 +258,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=")); @@ -272,7 +294,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"); @@ -289,7 +311,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); @@ -308,7 +330,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()); @@ -322,7 +344,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); @@ -332,7 +354,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); @@ -348,7 +370,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); @@ -367,7 +389,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); @@ -387,7 +409,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); @@ -401,7 +423,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"); @@ -416,7 +438,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"); @@ -431,7 +453,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())); @@ -447,7 +469,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") @@ -467,7 +489,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", @@ -478,7 +500,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 @@ -489,7 +511,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 @@ -498,7 +520,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(); @@ -536,7 +558,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..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 @@ -19,6 +19,7 @@ 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 d3927f698e..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; 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..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; 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; 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..1d4766f250 --- /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(0L)); + } + + @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(0L)); + + 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(0L)); + } +} 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; 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..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.web.service.ServiceState; -import org.apache.atlas.web.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..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.web.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..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.server.common.util.Servlets; import org.apache.atlas.utils.AtlasPerfTracer; -import org.apache.atlas.web.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..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.web.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..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; 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/AtlasAuthenticationFailureHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationFailureHandlerTest.java index b351e0f440..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; @@ -28,6 +29,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; @@ -60,7 +62,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(); @@ -70,7 +72,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); @@ -92,7 +94,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); @@ -108,7 +110,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); @@ -125,7 +127,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); @@ -143,7 +145,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++) { @@ -165,7 +167,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); @@ -183,7 +185,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); @@ -209,7 +211,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); @@ -231,7 +233,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); @@ -253,7 +255,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); @@ -266,7 +268,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); @@ -295,7 +297,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); @@ -319,7 +321,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/AtlasAuthenticationProviderTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasAuthenticationProviderTest.java index d06b34e06c..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; 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..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; @@ -31,6 +32,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; @@ -74,7 +76,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(); @@ -104,7 +106,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"; @@ -137,7 +139,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"; @@ -169,7 +171,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"; @@ -195,7 +197,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); @@ -225,7 +227,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); @@ -239,7 +241,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); @@ -262,7 +264,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 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..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 @@ -20,6 +20,7 @@ package org.apache.atlas.web.security; 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/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 c63d61e89a..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 @@ -20,7 +20,11 @@ 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.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; @@ -504,7 +508,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/AtlasSecurityConfigTest.java b/webapp/src/test/java/org/apache/atlas/web/security/AtlasSecurityConfigTest.java index 46c5e2111c..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 @@ -19,12 +19,14 @@ package org.apache.atlas.web.security; -import org.apache.atlas.web.filters.ActiveServerFilter; -import org.apache.atlas.web.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.web.filters.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; +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.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; 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/FileAuthenticationTest.java b/webapp/src/test/java/org/apache/atlas/web/security/FileAuthenticationTest.java index b753bebedd..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; @@ -27,7 +28,8 @@ 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.core.env.ConfigurableEnvironment; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; @@ -75,7 +77,13 @@ public void setup() throws Exception { System.setProperty("atlas.conf", persistDir); - applicationContext = new ClassPathXmlApplicationContext("test-spring-security.xml"); + 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/PamLoginModuleTest.java b/webapp/src/test/java/org/apache/atlas/web/security/PamLoginModuleTest.java index 9a9c43fe59..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 @@ -19,6 +19,8 @@ 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; 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..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; 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..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.server.common.service.SecureEmbeddedServer; import org.apache.atlas.web.TestUtils; -import org.apache.atlas.web.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/security/TestSpringSecurityBridgeConfig.java b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java new file mode 100644 index 0000000000..94275f93fe --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java @@ -0,0 +1,62 @@ +/* + * 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.context.annotation.Profile; +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. + *

+ * 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 { + @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/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java b/webapp/src/test/java/org/apache/atlas/web/security/UserAuthorityGranterTest.java index 79c71a754a..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; 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/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..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 @@ -18,7 +18,12 @@ 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.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.api.CreateBuilder; @@ -48,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; @@ -84,7 +114,7 @@ public void testSharedPathIsCreatedIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -118,7 +148,7 @@ public void testSharedPathIsCreatedWithRightACLIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -141,7 +171,7 @@ public void testDataIsUpdatedWithAtlasServerAddress() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); activeInstanceState.update("id1"); @@ -158,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); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_SUPPORT_FOR_TESTS); String actualServerAddress = activeInstanceState.getActiveServerAddress(); assertEquals(actualServerAddress, SERVER_ADDRESS); @@ -174,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); + 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/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..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 @@ -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,188 @@ 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); } - @Test - public void shouldAddAuthorization() { - 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"); + private CuratorFactory buildCuratorFactory() { + when(haSupport.isHAEnabled(configuration)).thenReturn(true); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { + return new CuratorFactory(configuration, haSupport) { @Override protected void initializeCuratorFramework() { } }; + } - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); + 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() 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 = 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 curatorFactory = buildCuratorFactory(); + invokeEnhance(curatorFactory); - curatorFactory.enhanceBuilderWithSecurityParameters(zookeeperProperties, builder); - - 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..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 @@ -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; @@ -30,27 +31,16 @@ import java.io.IOException; import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; 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 +199,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..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 @@ -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 atlas-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 79e62148f9..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 @@ -18,10 +18,11 @@ 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.apache.atlas.server.common.service.UserService; 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; @@ -38,7 +39,7 @@ public class UserServiceTest { private UserDao userDao; @Mock - private User user; + private UserDetails user; private UserService userService; @@ -63,7 +64,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 +75,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 +86,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 +97,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 +116,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 +127,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 +138,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 +149,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 +160,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 +171,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 +208,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/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; 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..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.web.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 4fa04b60a5..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; 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..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,6 +20,7 @@ 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; diff --git a/webapp/src/test/resources/test-spring-security.xml b/webapp/src/test/resources/test-spring-security.xml index f77c20bd68..11b44a056f 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -52,16 +52,16 @@ - - + + - - + + - - + + @@ -71,24 +71,24 @@ - + - - + + - + + 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/test/webapp/WEB-INF/web.xml b/webapp/src/test/webapp/WEB-INF/web.xml index fd1d492fcd..e149a285ef 100755 --- a/webapp/src/test/webapp/WEB-INF/web.xml +++ b/webapp/src/test/webapp/WEB-INF/web.xml @@ -59,7 +59,7 @@ AuditFilter - org.apache.atlas.web.filters.AuditFilter + org.apache.atlas.server.common.filters.AuditFilter