diff --git a/addons/hbase-bridge/src/bin/import-hbase.sh b/addons/hbase-bridge/src/bin/import-hbase.sh index aaf92ddd087..ea45ee9aff2 100644 --- a/addons/hbase-bridge/src/bin/import-hbase.sh +++ b/addons/hbase-bridge/src/bin/import-hbase.sh @@ -141,7 +141,7 @@ then fi JAVA_PROPERTIES="$ATLAS_OPTS -Datlas.log.dir=$ATLAS_LOG_DIR -Datlas.log.file=import-hbase.log --Dlog4j.configuration=atlas-hbase-import-log4j.xml" +-Dlog4j.configurationFile=atlas-hbase-import-logback.xml" IMPORT_ARGS= JVM_ARGS= diff --git a/addons/hive-bridge/src/bin/import-hive.sh b/addons/hive-bridge/src/bin/import-hive.sh index c64f6d1f0e6..bc0569db7ae 100755 --- a/addons/hive-bridge/src/bin/import-hive.sh +++ b/addons/hive-bridge/src/bin/import-hive.sh @@ -131,7 +131,7 @@ then fi JAVA_PROPERTIES="$ATLAS_OPTS -Datlas.log.dir=$ATLAS_LOG_DIR -Datlas.log.file=import-hive.log --Dlog4j.configuration=atlas-hive-import-log4j.xml" +-Dlog4j.configurationFile=atlas-hive-import-logback.xml" IMPORT_ARGS= JVM_ARGS= diff --git a/addons/kafka-bridge/src/bin/import-kafka.sh b/addons/kafka-bridge/src/bin/import-kafka.sh index 9255a8151d8..bca361dcf1c 100644 --- a/addons/kafka-bridge/src/bin/import-kafka.sh +++ b/addons/kafka-bridge/src/bin/import-kafka.sh @@ -121,7 +121,7 @@ then fi JAVA_PROPERTIES="$ATLAS_OPTS -Datlas.log.dir=$ATLAS_LOG_DIR -Datlas.log.file=import-kafka.log --Dlog4j.configuration=atlas-kafka-import-log4j.xml" +-Dlog4j.configurationFile=atlas-kafka-import-logback.xml" shift while [[ ${1} =~ ^\-D ]]; do diff --git a/dev-support/atlas-docker/Dockerfile.atlas-rest b/dev-support/atlas-docker/Dockerfile.atlas-rest new file mode 100644 index 00000000000..1fc6fc1a933 --- /dev/null +++ b/dev-support/atlas-docker/Dockerfile.atlas-rest @@ -0,0 +1,40 @@ +# 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. + + +ARG TAG=latest + +FROM atlas-base:$TAG + +ARG ATLAS_VERSION + +COPY ./scripts/atlas-rest.sh ${ATLAS_SCRIPTS}/ +COPY ./dist/apache-atlas-${ATLAS_VERSION}-rest-server.tar.gz /home/atlas/dist/ + +ENV ATLAS_REST_HOME=/opt/rest-notification + + +RUN tar xfz /home/atlas/dist/apache-atlas-${ATLAS_VERSION}-rest-server.tar.gz --directory=/opt/ && \ + ln -s /opt/rest-notification-webapp-${ATLAS_VERSION} ${ATLAS_REST_HOME} && \ + rm -f /home/atlas/dist/apache-atlas-${ATLAS_VERSION}-rest-server.tar.gz && \ + mkdir -p /var/run/atlas/rest-notification /var/log/atlas/rest-notification && \ + rm -rf ${ATLAS_REST_HOME}/logs && \ + ln -s /var/log/atlas/rest-notification ${ATLAS_REST_HOME}/logs && \ + chown -R atlas:atlas ${ATLAS_REST_HOME}/ /var/run/atlas/rest-notification /var/log/atlas/rest-notification + +EXPOSE 41000 + +ENTRYPOINT [ "/home/atlas/scripts/atlas-rest.sh" ] diff --git a/dev-support/atlas-docker/docker-compose.atlas-rest.yml b/dev-support/atlas-docker/docker-compose.atlas-rest.yml new file mode 100644 index 00000000000..ec17ae62cd3 --- /dev/null +++ b/dev-support/atlas-docker/docker-compose.atlas-rest.yml @@ -0,0 +1,29 @@ +services: + atlas-rest: + build: + context: . + dockerfile: Dockerfile.atlas-rest + args: + - ATLAS_VERSION=${ATLAS_VERSION} + image: atlas-rest:latest + container_name: atlas-rest + hostname: atlas-rest.example.com + stdin_open: true + tty: true + networks: + - atlas + ports: + - "41000:41000" + depends_on: + atlas-kafka: + condition: service_started + atlas-zk: + condition: service_started + environment: + - ATLAS_VERSION + command: + - /home/atlas/scripts/atlas-rest.sh + +networks: + atlas: + name: atlasnw diff --git a/dev-support/atlas-docker/scripts/atlas-build.sh b/dev-support/atlas-docker/scripts/atlas-build.sh index dc1482c3f75..41738b1bf35 100755 --- a/dev-support/atlas-docker/scripts/atlas-build.sh +++ b/dev-support/atlas-docker/scripts/atlas-build.sh @@ -94,7 +94,9 @@ for f in \ "distro/target/apache-atlas-${ATLAS_VERSION}-server.tar.gz" \ "distro/target/apache-atlas-${ATLAS_VERSION}-hive-hook.tar.gz" \ "distro/target/apache-atlas-${ATLAS_VERSION}-hbase-hook.tar.gz" \ - "distro/target/apache-atlas-${ATLAS_VERSION}-kafka-hook.tar.gz" + "distro/target/apache-atlas-${ATLAS_VERSION}-kafka-hook.tar.gz" \ + "distro/target/apache-atlas-${ATLAS_VERSION}-rest-server.tar.gz" + do if [ -f "${f}" ]; then mv -f "${f}" /home/atlas/dist/ diff --git a/dev-support/atlas-docker/scripts/atlas-hbase-application.properties b/dev-support/atlas-docker/scripts/atlas-hbase-application.properties index 268e5c8c4c1..3750b6b5b34 100644 --- a/dev-support/atlas-docker/scripts/atlas-hbase-application.properties +++ b/dev-support/atlas-docker/scripts/atlas-hbase-application.properties @@ -21,3 +21,10 @@ atlas.rest.address=http://atlas.example.com:21000 atlas.kafka.zookeeper.connect=atlas-zk.example.com:2181 atlas.kafka.bootstrap.servers=atlas-kafka.example.com:9092 + +####### Atlas REST Notification ########### + +#atlas.hook.rest.notification.address=http://atlas-rest.example.com:41000/rest +#atlas.hook.rest.notification.enabled=true +#atlas.rest.basic.auth.username=admin +#atlas.rest.basic.auth.password=atlasR0cks! diff --git a/dev-support/atlas-docker/scripts/atlas-hive-application.properties b/dev-support/atlas-docker/scripts/atlas-hive-application.properties index 268e5c8c4c1..9da8d70b153 100644 --- a/dev-support/atlas-docker/scripts/atlas-hive-application.properties +++ b/dev-support/atlas-docker/scripts/atlas-hive-application.properties @@ -17,7 +17,15 @@ # -atlas.rest.address=http://atlas.example.com:21000 +#atlas.rest.address=http://atlas.example.com:21000 atlas.kafka.zookeeper.connect=atlas-zk.example.com:2181 atlas.kafka.bootstrap.servers=atlas-kafka.example.com:9092 + + +####### Atlas REST Notification ########### + +#atlas.hook.rest.notification.address=http://atlas-rest.example.com:41000/rest +#atlas.hook.rest.notification.enabled=true +#atlas.rest.basic.auth.username=admin +#atlas.rest.basic.auth.password=atlasR0cks! diff --git a/dev-support/atlas-docker/scripts/atlas-kafka-application.properties b/dev-support/atlas-docker/scripts/atlas-kafka-application.properties index 268e5c8c4c1..9da8d70b153 100644 --- a/dev-support/atlas-docker/scripts/atlas-kafka-application.properties +++ b/dev-support/atlas-docker/scripts/atlas-kafka-application.properties @@ -17,7 +17,15 @@ # -atlas.rest.address=http://atlas.example.com:21000 +#atlas.rest.address=http://atlas.example.com:21000 atlas.kafka.zookeeper.connect=atlas-zk.example.com:2181 atlas.kafka.bootstrap.servers=atlas-kafka.example.com:9092 + + +####### Atlas REST Notification ########### + +#atlas.hook.rest.notification.address=http://atlas-rest.example.com:41000/rest +#atlas.hook.rest.notification.enabled=true +#atlas.rest.basic.auth.username=admin +#atlas.rest.basic.auth.password=atlasR0cks! diff --git a/dev-support/atlas-docker/scripts/atlas-rest.sh b/dev-support/atlas-docker/scripts/atlas-rest.sh new file mode 100755 index 00000000000..dc4cad53563 --- /dev/null +++ b/dev-support/atlas-docker/scripts/atlas-rest.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +set -x + +if [ ! -e ${ATLAS_REST_HOME}/.setupDone ] +then + SETUP_ATLAS_REST=true +else + SETUP_ATLAS_REST=false +fi + +if [ "${SETUP_ATLAS_REST}" == "true" ] +then + encryptedPwd=$(${ATLAS_REST_HOME}/bin/rest_server_cputil.py -g -u admin -p atlasR0cks! -s) + echo "admin=ADMIN::${encryptedPwd}" > ${ATLAS_REST_HOME}/conf/users-credentials.properties + sed -i "s/atlas.notification.embedded=.*$/atlas.notification.embedded=false/" /opt/rest-notification/conf/atlas-application.properties + sed -i "s/atlas.kafka.zookeeper.connect=.*$/atlas.kafka.zookeeper.connect=atlas-zk.example.com:2181/" /opt/rest-notification/conf/atlas-application.properties + sed -i "s/atlas.kafka.bootstrap.servers=.*$/atlas.kafka.bootstrap.servers=atlas-kafka.example.com:9092/" /opt/rest-notification/conf/atlas-application.properties + + chown -R atlas:atlas ${ATLAS_REST_HOME}/ + + touch ${ATLAS_REST_HOME}/.setupDone +fi + + + +su -c "cd ${ATLAS_REST_HOME}/bin && ./rest_start.py" atlas +ATLAS_REST_PID=`ps -ef | grep -v grep | grep -i "org.apache.atlas.notification.rest.RestNotificationMain" | awk '{print $2}'` + +# prevent the container from exiting +tail --pid=${ATLAS_REST_PID} -f /dev/null diff --git a/distro/pom.xml b/distro/pom.xml index d7e95888949..ca347aae1ec 100644 --- a/distro/pom.xml +++ b/distro/pom.xml @@ -270,6 +270,7 @@ atlas.graph.storage.hbase.regions-per-server=1 src/main/assemblies/atlas-kafka-hook-package.xml src/main/assemblies/atlas-couchbase-hook-package.xml src/main/assemblies/atlas-server-package.xml + src/main/assemblies/rest-server-package.xml src/main/assemblies/standalone-package.xml src/main/assemblies/src-package.xml src/main/assemblies/atlas-repair-index-package.xml diff --git a/distro/src/bin/rest_config.py b/distro/src/bin/rest_config.py new file mode 100755 index 00000000000..a57374f751e --- /dev/null +++ b/distro/src/bin/rest_config.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python3 + +# +# 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. + +import getpass +import glob +import os +import re +import platform +import subprocess +import sys +import time +import errno +import socket +from re import split +from time import sleep + + +BIN = "bin" +LIB = "lib" +CONF = "conf" +LOG = "logs" +WEBAPP = "server" + os.sep + "webapp" + +DATA = "data" +REST_CONF = "REST_CONF" +REST_LOG = "REST_LOG_DIR" +REST_PID = "REST_PID_DIR" +REST_WEBAPP = "REST_EXPANDED_WEBAPP_DIR" +REST_SERVER_OPTS = "REST_SERVER_OPTS" +REST_OPTS = "REST_OPTS" +REST_SERVER_HEAP = "REST_SERVER_HEAP" +REST_DATA = "REST_DATA_DIR" +REST_HOME = "REST_HOME_DIR" +ATLAS_HOME = "ATLAS_HOME_DIR" +REST_WAR = "REST_WAR" + +ENV_KEYS = ["JAVA_HOME", REST_OPTS, REST_SERVER_OPTS, REST_SERVER_HEAP, REST_LOG, REST_PID, REST_CONF, + "RESTCPPATH", REST_DATA, REST_HOME, REST_WEBAPP, REST_WAR] + +IS_WINDOWS = platform.system() == "Windows" +ON_POSIX = 'posix' in sys.builtin_module_names +CONF_FILE="atlas-application.properties" + + +TOPICS_TO_CREATE="atlas.notification.topics" +REST_HTTP_PORT="atlas.rest.server.http.port" +REST_HTTPS_PORT="atlas.rest.server.https.port" +DEFAULT_REST_HTTP_PORT="41000" +DEFAULT_REST_HTTPS_PORT="41443" +REST_ENABLE_TLS="atlas.rest.notification.enableTLS" +REST_SERVER_BIND_ADDRESS="atlas.rest.server.bind.address" +DEFAULT_REST_SERVER_HOST="localhost" + +DEBUG=False + +def scriptDir(): + """ + get the script path + """ + return os.path.dirname(os.path.realpath(__file__)) + + +def restDir(): + home = os.path.dirname(scriptDir()) + return os.environ.get(REST_HOME, home) + +def atlasDir(): + home = os.path.dirname(scriptDir()) + return os.environ.get(ATLAS_HOME, home) + +def libDir(dir) : + return os.path.join(dir, LIB) + +def confDir(dir): + localconf = os.path.join(dir, CONF) + return os.environ.get(REST_CONF, localconf) + +def logDir(dir): + localLog = os.path.join(dir, LOG) + return os.environ.get(REST_LOG, localLog) + +def pidFile(dir): + localPid = os.path.join(dir, LOG) + return os.path.join(os.environ.get(REST_PID, localPid), 'rest.pid') + +def dataDir(dir): + data = os.path.join(dir, DATA) + return os.environ.get(REST_DATA, data) + +def webAppDir(dir): + webapp = os.path.join(dir, WEBAPP) + return os.environ.get(REST_WEBAPP, webapp) + + +def resolveRestWarPath(): + war_dir = os.path.join(restDir(), "server", "webapp") + env_war = os.environ.get(REST_WAR) + if env_war and os.path.isfile(env_war): + return env_war + for war_name in ("rest-notification.war", "rest-notification-webapp.war"): + candidate = os.path.join(war_dir, war_name) + if os.path.isfile(candidate): + return candidate + if os.path.isdir(war_dir): + matches = sorted( + p for p in glob.glob(os.path.join(war_dir, "rest-notification*.war")) + if os.path.isfile(p) + ) + if len(matches) == 1: + return matches[0] + if len(matches) > 1: + for preferred in ("rest-notification.war", "rest-notification-webapp.war"): + preferred_path = os.path.join(war_dir, preferred) + if preferred_path in matches: + return preferred_path + return matches[0] + raise EnvironmentError( + "REST notification WAR not found. Install under %s as rest-notification.war, " + "rest-notification-webapp.war, or rest-notification*.war, or set %s to the WAR path." + % (war_dir, REST_WAR) + ) + + +def expandWebApp(dir): + webappDir = webAppDir(dir) + webAppMetadataDir = os.path.join(webappDir, "rest-notification") + d = os.sep + if not os.path.exists(os.path.join(webAppMetadataDir, "WEB-INF")): + try: + os.makedirs(webAppMetadataDir) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + pass + restWarPath = resolveRestWarPath() + if isCygwin(): + restWarPath = convertCygwinPath(restWarPath) + os.chdir(webAppMetadataDir) + jar(restWarPath) + + +def dirMustExist(dirname): + if not os.path.exists(dirname): + os.mkdir(dirname) + return dirname + +def executeEnvSh(confDir): + envscript = '%s/rest-env.sh' % confDir + if not IS_WINDOWS and os.path.exists(envscript): + envCmd = 'source %s && env' % envscript + command = ['bash', '-c', envCmd] + + proc = subprocess.Popen(command, stdout = subprocess.PIPE) + + for line in proc.stdout: + (key, _, value) = line.decode('utf8').strip().partition("=") + if key in ENV_KEYS: + os.environ[key] = value + + proc.communicate() + + + +def java(classname, args, classpath, jvm_opts_list, logdir=None): + java_home = os.environ.get("JAVA_HOME", None) + if java_home: + prg = os.path.join(java_home, "bin", "java") + else: + prg = which("java") + + if prg is None: + raise EnvironmentError('The java binary could not be found in your path or JAVA_HOME') + + commandline = [prg] + commandline.extend(jvm_opts_list) + commandline.append("-classpath") + commandline.append(classpath) + commandline.append(classname) + commandline.extend(args) + return runProcess(commandline, logdir) + + +def jar(path): + java_home = os.environ.get("JAVA_HOME", None) + if java_home: + prg = os.path.join(java_home, "bin", "jar") + else: + prg = which("jar") + + if prg is None: + raise EnvironmentError('The jar binary could not be found in your path or JAVA_HOME') + + commandline = [prg] + commandline.append("-xf") + commandline.append(path) + process = runProcess(commandline) + process.wait() + +def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + +def which(program): + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + path = path.strip('"') + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + + return None + + + +def runProcess(commandline, logdir=None, shell=False, wait=False): + """ + Run a process + :param commandline: command line + :return:the return code + """ + global finished + debug ("Executing : %s" % str(commandline)) + timestr = time.strftime("rest.%Y%m%d-%H%M%S") + stdoutFile = None + stderrFile = None + if logdir: + stdoutFile = open(os.path.join(logdir, timestr + ".out"), "w") + stderrFile = open(os.path.join(logdir,timestr + ".err"), "w") + + p = subprocess.Popen(commandline, stdout=stdoutFile, stderr=stderrFile, shell=shell) + + if wait: + p.communicate() + + return p + + +def print_output(name, src, toStdErr): + """ + Relay the output stream to stdout line by line + :param name: + :param src: source stream + :param toStdErr: flag set if stderr is to be the dest + :return: + """ + + global needPassword + debug ("starting printer for %s" % name ) + line = "" + while not finished: + (line, done) = read(src, line) + if done: + out(toStdErr, line + "\n") + flush(toStdErr) + if line.find("Enter password for") >= 0: + needPassword = True + line = "" + out(toStdErr, line) + # closedown: read remainder of stream + c = src.read(1) + while c!="" : + c = c.decode('utf-8') + out(toStdErr, c) + if c == "\n": + flush(toStdErr) + c = src.read(1) + flush(toStdErr) + src.close() + + +def read_input(name, exe): + """ + Read input from stdin and send to process + :param name: + :param process: process to send input to + :return: + """ + global needPassword + debug ("starting reader for %s" % name ) + while not finished: + if needPassword: + needPassword = False + if sys.stdin.isatty(): + cred = getpass.getpass() + else: + cred = sys.stdin.readline().rstrip() + exe.stdin.write(cred + "\n") + +def debug(text): + if DEBUG: print('[DEBUG] ' + text) + + +def error(text): + print('[ERROR] ' + text) + sys.stdout.flush() + +def info(text): + print(text) + sys.stdout.flush() + +def out(toStdErr, text) : + """ + Write to one of the system output channels. + This action does not add newlines. If you want that: write them yourself + :param toStdErr: flag set if stderr is to be the dest + :param text: text to write. + :return: + """ + if toStdErr: + sys.stderr.write(text) + else: + sys.stdout.write(text) + +def flush(toStdErr) : + """ + Flush the output stream + :param toStdErr: flag set if stderr is to be the dest + :return: + """ + if toStdErr: + sys.stderr.flush() + else: + sys.stdout.flush() + +def read(pipe, line): + """ + read a char, append to the listing if there is a char that is not \n + :param pipe: pipe to read from + :param line: line being built up + :return: (the potentially updated line, flag indicating newline reached) + """ + + c = pipe.read(1) + if c != "": + o = c.decode('utf-8') + if o != '\n': + line += o + return line, False + else: + return line, True + else: + return line, False + + +def writePid(rest_pid_file, process): + f = open(rest_pid_file, 'w') + f.write(str(process.pid)) + f.close() + + + +def exist_pid(pid): + if ON_POSIX: + #check if process id exist in the current process table + #See man 2 kill - Linux man page for info about the kill(pid,0) system function + try: + os.kill(pid, 0) + except OSError as e : + return e.errno == errno.EPERM + else: + return True + + elif IS_WINDOWS: + #The os.kill approach does not work on Windows with python 2.7 + #the output from tasklist command is searched for the process id + pidStr = str(pid) + command='tasklist /fi "pid eq %s"' % pidStr + sub_process=subprocess.Popen(command, stdout = subprocess.PIPE, shell=False) + sub_process.communicate() + output = subprocess.check_output(command) + output=split(" *",output) + for line in output: + if pidStr in line: + return True + return False + #os other than nt or posix - not supported - need to delete the file to restart server if pid no longer exist + return True + +def wait_for_shutdown(pid, msg, wait): + count = 0 + sys.stdout.write(msg) + while exist_pid(pid): + sys.stdout.write('.') + sys.stdout.flush() + sleep(1) + if count > wait: + break + count = count + 1 + + sys.stdout.write('\n') + + +def get_topics_to_create(confdir): + confdir = os.path.join(confdir, CONF_FILE) + topic_list = getConfig(confdir, TOPICS_TO_CREATE) + if topic_list is not None: + topics = topic_list.split(",") + else: + topics = [getConfigWithDefault("atlas.notification.hook.topic.name", "ATLAS_HOOK"), getConfigWithDefault("atlas.notification.entities.topic.name", "ATLAS_ENTITIES")] + return topics + + + +def get_rest_url_port(confdir): + port = None + if '-port' in sys.argv: + port = sys.argv[sys.argv.index('-port')+1] + + if port is None: + confdir = os.path.join(confdir, CONF_FILE) + enable_tls = getConfig(confdir, REST_ENABLE_TLS) + if enable_tls is not None and enable_tls.lower() == 'true': + port = getConfigWithDefault(confdir, REST_HTTPS_PORT, DEFAULT_REST_HTTPS_PORT) + else: + port = getConfigWithDefault(confdir, REST_HTTP_PORT, DEFAULT_REST_HTTP_PORT) + + print("Starting REST server on port: %s" % port) + return port + +def get_rest_url_host(confdir): + confdir = os.path.join(confdir, CONF_FILE) + host = getConfigWithDefault(confdir, REST_SERVER_BIND_ADDRESS, DEFAULT_REST_SERVER_HOST) + if (host == '0.0.0.0'): + host = DEFAULT_REST_SERVER_HOST + print("\nStarting Rest server on host: %s" % host) + return host + +def wait_for_startup(confdir, wait): + count = 0 + host = get_rest_url_host(confdir) + port = get_rest_url_port(confdir) + while True: + try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(1) + s.connect((host, int(port))) + s.close() + break + except Exception as e: + # Wait for 1 sec before next ping + sys.stdout.write('.') + sys.stdout.flush() + sleep(1) + + if count > wait: + s.close() + break + + count = count + 1 + + sys.stdout.write('\n') + + +def server_already_running(pid): + print("Rest server is already running under process %s" % pid) + sys.exit() + +def server_pid_not_running(pid): + print("The Server is no longer running with pid %s" %pid) + + +def grep(file, value): + for line in open(file).readlines(): + if re.match(value, line): + return line + return None + +def getConfig(file, key): + key = key + "\s*=" + for line in open(file).readlines(): + if re.match(key, line): + return line.split('=')[1].strip() + return None + +def getConfigWithDefault(file, key, defaultValue): + value = getConfig(file, key) + if value is None: + value = defaultValue + return value + +def isCygwin(): + return platform.system().startswith("CYGWIN") + +def isCygwin(): + return platform.system().startswith("CYGWIN") + +# Convert the specified cygwin-style pathname to Windows format, +# using the cygpath utility. By default, path is assumed +# to be a file system pathname. If isClasspath is True, +# then path is treated as a Java classpath string. +def convertCygwinPath(path, isClasspath=False): + if (isClasspath): + cygpathArgs = ["cygpath", "-w", "-p", path] + else: + cygpathArgs = ["cygpath", "-w", path] + windowsPath = subprocess.Popen(cygpathArgs, stdout=subprocess.PIPE).communicate()[0] + windowsPath = windowsPath.strip() + return windowsPath + + + diff --git a/distro/src/bin/rest_server_cputil.py b/distro/src/bin/rest_server_cputil.py new file mode 100644 index 00000000000..28cfd314dce --- /dev/null +++ b/distro/src/bin/rest_server_cputil.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +# +# 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. +import os +import sys + +import rest_config as mc + +DEFAULT_JVM_OPTS="-Xmx1024m" + +def main(): + + rest_home = mc.restDir() + confdir = mc.dirMustExist(mc.confDir(rest_home)) + logdir = mc.dirMustExist(mc.logDir(rest_home)) + mc.executeEnvSh(confdir) + + jvm_opts_list = [] + + default_jvm_opts = DEFAULT_JVM_OPTS + rest_jvm_opts = os.environ.get(mc.REST_OPTS, default_jvm_opts) + jvm_opts_list.extend(rest_jvm_opts.split()) + + #expand web app dir + web_app_dir = mc.webAppDir(rest_home) + mc.expandWebApp(rest_home) + + p = os.pathsep + rest_classpath = confdir + p \ + + os.path.join(web_app_dir, "rest-notification", "WEB-INF", "classes" ) + p \ + + os.path.join(web_app_dir, "rest-notification", "WEB-INF", "lib", "*" ) + p \ + + os.path.join(rest_home, "libext", "*") + + process = mc.java("org.apache.atlas.notification.rest.util.CredentialProviderUtility", sys.argv[1:], rest_classpath, jvm_opts_list) + process.wait() + +if __name__ == '__main__': + try: + returncode = main() + except Exception as e: + print("Exception: %s " % str(e)) + returncode = -1 + + sys.exit(returncode) diff --git a/distro/src/bin/rest_start.py b/distro/src/bin/rest_start.py new file mode 100644 index 00000000000..5dcf50af253 --- /dev/null +++ b/distro/src/bin/rest_start.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 + +# +# 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. + +import os +import sys +import traceback + +import rest_config as mc + + +REST_LOG_OPTS="-Drest.log.dir=%s -Drest.log.file=%s.log" +REST_COMMAND_OPTS="-Drest.home=%s" +REST_CONFIG_OPTS="-Drest.conf=%s" +DEFAULT_JVM_HEAP_OPTS="-Xmx1024m" +DEFAULT_JVM_OPTS="-Dlogback.configurationFile=rest-logback.xml -Djava.security.auth.login.config=conf/atlas_jaas.conf -Djava.net.preferIPv4Stack=true -Djdk.util.zip.disableZip64ExtraFieldValidation=true -server" + +ATLAS_COMMAND_OPTS="-Datlas.home=%s" + + + +def main(): + + is_setup = (len(sys.argv)>1) and sys.argv[1] is not None and sys.argv[1] == '-setup' + + rest_home = mc.restDir() + atlas_home = mc.atlasDir() + confdir = mc.dirMustExist(mc.confDir(rest_home)) + mc.executeEnvSh(confdir) + logdir = mc.dirMustExist(mc.logDir(rest_home)) + mc.dirMustExist(mc.dataDir(rest_home)) + if mc.isCygwin(): + # Pathnames that are passed to JVM must be converted to Windows format. + jvm_rest_home = mc.convertCygwinPath(rest_home) + jvm_confdir = mc.convertCygwinPath(confdir) + jvm_logdir = mc.convertCygwinPath(logdir) + jvm_atlas_home = mc.convertCygwinPath(atlas_home) + else: + jvm_rest_home = rest_home + jvm_confdir = confdir + jvm_logdir = logdir + jvm_atlas_home = atlas_home + + #create sys property for conf dirs + if not is_setup: + jvm_opts_list = (REST_LOG_OPTS % (jvm_logdir, "application")).split() + else: + jvm_opts_list = (REST_LOG_OPTS % (jvm_logdir, "rest_setup")).split() + + cmd_opts = (REST_COMMAND_OPTS % jvm_rest_home) + jvm_opts_list.extend(cmd_opts.split()) + + cmd_opts2 = (ATLAS_COMMAND_OPTS % jvm_atlas_home) + jvm_opts_list.extend(cmd_opts2.split()) + + config_opts = (REST_CONFIG_OPTS % jvm_confdir) + jvm_opts_list.extend(config_opts.split()) + + rest_server_heap_opts = os.environ.get(mc.REST_SERVER_HEAP, DEFAULT_JVM_HEAP_OPTS) + jvm_opts_list.extend(rest_server_heap_opts.split()) + + rest_server_jvm_opts = os.environ.get(mc.REST_SERVER_OPTS) + if rest_server_jvm_opts: + jvm_opts_list.extend(rest_server_jvm_opts.split()) + + rest_jvm_opts = os.environ.get(mc.REST_OPTS, DEFAULT_JVM_OPTS) + jvm_opts_list.extend(rest_jvm_opts.split()) + + #expand web app dir + web_app_dir = mc.webAppDir(rest_home) + mc.expandWebApp(rest_home) + + p = os.pathsep + rest_classpath = confdir + p \ + + os.path.join(web_app_dir,"rest-notification", "WEB-INF", "classes" ) + p \ + + os.path.join(web_app_dir, "rest-notification","WEB-INF", "lib", "*" ) + p \ + + os.path.join(rest_home, "libext", "*") + + + + if mc.isCygwin(): + rest_classpath = mc.convertCygwinPath(rest_classpath, True) + + rest_pid_file = mc.pidFile(rest_home) + + if os.path.isfile(rest_pid_file): + #Check if process listed in rest.pid file is still running + pf = open(rest_pid_file, 'r') + pid = pf.read().strip() + pf.close() + if pid != "": + if mc.exist_pid((int)(pid)): + if is_setup: + print("Cannot run setup when server is running.") + mc.server_already_running(pid) + else: + mc.server_pid_not_running(pid) + + + web_app_path = os.path.join(web_app_dir, "rest-notification") + + if (mc.isCygwin()): + web_app_path = mc.convertCygwinPath(web_app_path) + if not is_setup: + start_rest_server(rest_classpath, rest_pid_file, jvm_logdir, jvm_opts_list, web_app_path) + mc.wait_for_startup(confdir, 300) + print("Rest Server started!!!\n") + """ else: + process = mc.java("org.apache.atlas.web.setup.AtlasSetup", [], atlas_classpath, jvm_opts_list, jvm_logdir) + return process.wait() """ + + +def start_rest_server(rest_classpath, rest_pid_file, jvm_logdir, jvm_opts_list, web_app_path): + args = ["-app", web_app_path] + args.extend(sys.argv[1:]) + + process = mc.java("org.apache.atlas.notification.rest.RestNotificationMain", args, rest_classpath, jvm_opts_list, jvm_logdir) + mc.writePid(rest_pid_file, process) + +if __name__ == '__main__': + try: + returncode = main() + except Exception as e: + print("Exception: %s " % str(e)) + print(traceback.format_exc()) + returncode = -1 + + sys.exit(returncode) diff --git a/distro/src/bin/rest_stop.py b/distro/src/bin/rest_stop.py new file mode 100644 index 00000000000..0f2ad7af19d --- /dev/null +++ b/distro/src/bin/rest_stop.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +# +# 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. + + +# Signal handling is OS-specific because there is no SIGKILL on Windows. + +import os + +if os.name == "nt": + # Attempting to import SIGKILL on Windows would cause script to fail. + from signal import SIGTERM +else: + from signal import SIGTERM, SIGKILL + +import sys +import traceback +import time +import rest_config as mc + +def main(): + + rest_home = mc.restDir() + confdir = mc.dirMustExist(mc.confDir(rest_home)) + mc.executeEnvSh(confdir) + mc.dirMustExist(mc.logDir(rest_home)) + + rest_pid_file = mc.pidFile(rest_home) + + try: + pf = open(rest_pid_file, 'r') + pid = int(pf.read().strip()) + pf.close() + except: + pid = None + if not pid: + sys.stderr.write("No process ID file found. Server not running?\n") + return + + if not mc.exist_pid(pid): + sys.stderr.write("Server no longer running with pid %s\nImproper shutdown?\npid file deleted.\n" %pid) + os.remove(rest_pid_file) + return + + os.kill(pid, SIGTERM) + + mc.wait_for_shutdown(pid, "stopping rest", 30) + if not mc.exist_pid(pid): + print("Rest Server stopped!!!\n") + + # assuming kill worked since process check on windows is more involved... + if os.path.exists(rest_pid_file): + os.remove(rest_pid_file) + + + if mc.exist_pid(pid): + #after 30 seconds kill it + time.sleep(30) + try: + + if os.name == "nt": + # If running on Windows then timeout termination uses SIGTERM instead of SIGKILL. + sys.stderr.write("did not stop gracefully after 30 seconds: killing process using SIGTERM\n") + os.kill(pid, SIGTERM) + else: + sys.stderr.write("did not stop gracefully after 30 seconds: killing process using SIGKILL\n") + os.kill(pid, SIGKILL) + + except: + pass + +if __name__ == '__main__': + try: + returncode = main() + except Exception as e: + print("Exception: %s " % str(e)) + print(traceback.format_exc()) + returncode = -1 + + sys.exit(returncode) diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties index b5734d7a8de..f2de1a7997e 100755 --- a/distro/src/conf/atlas-application.properties +++ b/distro/src/conf/atlas-application.properties @@ -267,6 +267,11 @@ atlas.metric.query.cache.ttlInSecs=900 #Set to false to disable gremlin search. atlas.search.gremlin.enable=false +########## Atlas REST Server ########### + +atlas.rest.server.https.port=41443 +atlas.rest.server.http.port=41000 +atlas.rest.notification.enableTLS=false ########## Add http headers ########### diff --git a/distro/src/conf/rest-env.sh b/distro/src/conf/rest-env.sh new file mode 100644 index 00000000000..cce9eec4972 --- /dev/null +++ b/distro/src/conf/rest-env.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env bash +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# The java implementation to use. If JAVA_HOME is not found we expect java and jar to be in path +#export JAVA_HOME= + +# any additional java opts you want to set. This will apply to both client and server operations +#export ATLAS_OPTS= + +# any additional java opts that you want to set for client only +#export ATLAS_CLIENT_OPTS= + +# java heap size we want to set for the client. Default is 1024MB +#export ATLAS_CLIENT_HEAP= + +# any additional opts you want to set for atlas service. +#export ATLAS_SERVER_OPTS= + +# indicative values for large number of metadata entities (equal or more than 10,000s) +#export ATLAS_SERVER_OPTS="-server -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+PrintTenuringDistribution -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=dumps/atlas_server.hprof -Xloggc:logs/gc-worker.log -verbose:gc -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=1m -XX:+PrintGCDetails -XX:+PrintHeapAtGC -XX:+PrintGCTimeStamps" + +# java heap size we want to set for the atlas server. Default is 1024MB +#export ATLAS_SERVER_HEAP= + +# indicative values for large number of metadata entities (equal or more than 10,000s) for JDK 8 +#export ATLAS_SERVER_HEAP="-Xms15360m -Xmx15360m -XX:MaxNewSize=5120m -XX:MetaspaceSize=100M -XX:MaxMetaspaceSize=512m" + +# What is is considered as atlas home dir. Default is the base locaion of the installed software +#export ATLAS_HOME_DIR= + +# Where log files are stored. Defatult is logs directory under the base install location +#export ATLAS_LOG_DIR= + +# Where pid files are stored. Defatult is logs directory under the base install location +#export ATLAS_PID_DIR= + +# where the atlas titan db data is stored. Defatult is logs/data directory under the base install location +#export ATLAS_DATA_DIR= + +# Where do you want to expand the war file. By Default it is in /server/webapp dir under the base install dir. +#export ATLAS_EXPANDED_WEBAPP_DIR= + +# indicates whether or not a local instance of HBase should be started for Atlas +#export MANAGE_LOCAL_HBASE=${hbase.embedded} + +# indicates whether or not a local instance of Solr should be started for Atlas +#export MANAGE_LOCAL_SOLR=${solr.embedded} + +# indicates whether or not cassandra is the embedded backend for Atlas +#export MANAGE_EMBEDDED_CASSANDRA=${cassandra.embedded} + +# indicates whether or not a local instance of Elasticsearch should be started for Atlas +#export MANAGE_LOCAL_ELASTICSEARCH=${elasticsearch.managed} +#export ATLAS_HOME_DIR=/rest-notification-webapp/ \ No newline at end of file diff --git a/distro/src/conf/rest-logback.xml b/distro/src/conf/rest-logback.xml new file mode 100644 index 00000000000..1b1e7695cc6 --- /dev/null +++ b/distro/src/conf/rest-logback.xml @@ -0,0 +1,84 @@ + + + + + + + + %date [%thread] %level{5} [%file:%line] %msg%n + + + INFO + + + + + ${rest.log.dir}/${rest.log.file} + true + + %date [%thread] %level{5} [%file:%line] %msg%n + + + ${rest.log.dir}/${rest.log.file}-%d + 20 + true + + + + + ${rest.log.dir}/audit.log + true + + %date [%thread] %level{5} [%file:%line] %msg%n + + + ${rest.log.dir}/audit-%d.log + 20 + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/distro/src/main/assemblies/rest-server-package.xml b/distro/src/main/assemblies/rest-server-package.xml new file mode 100644 index 00000000000..66da2f78537 --- /dev/null +++ b/distro/src/main/assemblies/rest-server-package.xml @@ -0,0 +1,105 @@ + + + + + tar.gz + + rest-server + rest-notification-webapp-${project.version} + + + + + ../logs + logs + 0777 + + * + **/** + + + + + ../ + / + + DISCLAIMER.txt + NOTICE + LICENSE + CHANGES.txt + README + + + + + + + + ../rest-notification-webapp/target/rest-notification-webapp-${project.version}.war + server/webapp + rest-notification.war + + + + target/conf/atlas-application.properties + conf + + + target/conf/users-credentials.properties + conf + + + + target/conf/rest-env.sh + conf + + + + target/conf/rest-logback.xml + conf + + + + + target/bin/rest_start.py + bin + 0755 + + + + target/bin/rest_stop.py + bin + 0755 + + + + target/bin/rest_config.py + bin + 0755 + + + + target/bin/rest_server_cputil.py + bin + 0755 + + + diff --git a/distro/src/main/assemblies/standalone-package.xml b/distro/src/main/assemblies/standalone-package.xml index 56b338232eb..97ac9823503 100755 --- a/distro/src/main/assemblies/standalone-package.xml +++ b/distro/src/main/assemblies/standalone-package.xml @@ -225,5 +225,48 @@ server/webapp atlas.war + + + target/bin/rest_start.py + rest-notification-webapp/bin + 0755 + + + + target/bin/rest_stop.py + rest-notification-webapp/bin + 0755 + + + + target/bin/rest_config.py + rest-notification-webapp/bin + 0755 + + + + ../rest-notification-webapp/target/rest-notification-webapp-${project.version}.war + rest-notification-webapp/server/webapp + rest-notification-webapp.war + + + target/conf/atlas-application.properties + rest-notification-webapp/conf + + + target/conf/users-credentials.properties + rest-notification-webapp/conf + + + + target/conf/rest-env.sh + rest-notification-webapp/conf + + + + target/conf/rest-logback.xml + rest-notification-webapp/conf + + diff --git a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java index 51b8963c366..5286f30d63b 100644 --- a/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java +++ b/intg/src/main/java/org/apache/atlas/AtlasConfiguration.java @@ -44,6 +44,7 @@ public enum AtlasConfiguration { NOTIFICATION_HOOK_CONSUMER_BUFFERING_BATCH_SIZE("atlas.notification.consumer.message.buffering.batch.size", 100), NOTIFICATION_HOOK_REST_ENABLED("atlas.hook.rest.notification.enabled", false), + NOTIFICATION_HOOK_REST_ADDRESS("atlas.hook.rest.notification.address", ""), NOTIFICATION_HOOK_CONSUMER_TOPIC_NAMES("atlas.notification.hook.consumer.topic.names", "ATLAS_HOOK"), // a comma separated list of topic names NOTIFICATION_ENTITIES_CONSUMER_TOPIC_NAMES("atlas.notification.entities.consumer.topic.names", "ATLAS_ENTITIES"), // a comma separated list of topic names diff --git a/notification/src/main/java/org/apache/atlas/notification/rest/RestNotification.java b/notification/src/main/java/org/apache/atlas/notification/rest/RestNotification.java index cfbae5f26b2..8d4b642a23e 100644 --- a/notification/src/main/java/org/apache/atlas/notification/rest/RestNotification.java +++ b/notification/src/main/java/org/apache/atlas/notification/rest/RestNotification.java @@ -20,6 +20,7 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.atlas.AtlasClientV2; import org.apache.atlas.AtlasConfiguration; +import org.apache.atlas.AtlasConstants; import org.apache.atlas.AtlasErrorCode; import org.apache.atlas.AtlasException; import org.apache.atlas.AtlasServiceException; @@ -29,6 +30,7 @@ import org.apache.atlas.notification.NotificationException; import org.apache.atlas.utils.AuthenticationUtil; import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -45,7 +47,7 @@ public class RestNotification extends AbstractNotification { private static final Logger LOG = LoggerFactory.getLogger(RestNotification.class); private static final int BATCH_MAX_LENGTH_BYTES = AtlasConfiguration.NOTIFICATION_REST_BODY_MAX_LENGTH_BYTES.getInt(); - private static final String ATLAS_ENDPOINT = "atlas.rest.address"; + private static final String ATLAS_HOOK_REST_NOTIFICATION_ENDPOINT = "atlas.hook.rest.notification.address"; private static final String BASIC_AUTH_USERNAME = "atlas.rest.basic.auth.username"; private static final String BASIC_AUTH_PASSWORD = "atlas.rest.basic.auth.password"; private static final String DEFAULT_ATLAS_URL = "http://localhost:31000/"; @@ -110,9 +112,13 @@ private AtlasClientV2 setupAtlasClientV2(Configuration configuration) throws Atl } try { - String[] atlasEndPoint = configuration.getStringArray(ATLAS_ENDPOINT); + String[] atlasEndPoint = configuration.getStringArray(ATLAS_HOOK_REST_NOTIFICATION_ENDPOINT); - if (atlasEndPoint == null || atlasEndPoint.length == 0) { + if (isEndpointNotSpecified(atlasEndPoint)) { + atlasEndPoint = configuration.getStringArray(AtlasConstants.ATLAS_REST_ADDRESS_KEY); + } + + if (isEndpointNotSpecified(atlasEndPoint)) { atlasEndPoint = new String[] {DEFAULT_ATLAS_URL}; } @@ -133,6 +139,18 @@ private AtlasClientV2 setupAtlasClientV2(Configuration configuration) throws Atl return atlasClientV2; } + private boolean isEndpointNotSpecified(String[] endpointUrls) { + if (endpointUrls == null || endpointUrls.length == 0) { + return true; + } + for (String url : endpointUrls) { + if (StringUtils.isNotBlank(url)) { + return false; + } + } + return true; + } + private List> getBatches(List messages) { List> batches = new ArrayList<>(); List batch = new ArrayList<>(); diff --git a/notification/src/test/java/org/apache/atlas/notification/RestNotificationTest.java b/notification/src/test/java/org/apache/atlas/notification/RestNotificationTest.java index 43d32a9f161..4f35acd9e91 100644 --- a/notification/src/test/java/org/apache/atlas/notification/RestNotificationTest.java +++ b/notification/src/test/java/org/apache/atlas/notification/RestNotificationTest.java @@ -27,6 +27,7 @@ import org.apache.atlas.AtlasErrorCode; import org.apache.atlas.kafka.NotificationProvider; import org.apache.atlas.notification.rest.RestNotification; +import org.apache.commons.configuration.BaseConfiguration; import org.apache.commons.configuration.Configuration; import org.mockito.Matchers; import org.mockito.Mock; @@ -37,6 +38,7 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; @@ -131,4 +133,63 @@ private WebResource.Builder getBuilder(WebResource resourceObject) { return resourceBuilderMock; } + + @Test + public void testRestNotificationEndpointPrefersHookRestAddress() throws Exception { + Configuration localConf = new BaseConfiguration(); + localConf.setProperty("atlas.hook.rest.notification.address", "http://atlas-rest.example.com:41000/rest"); + localConf.setProperty("atlas.rest.address", "http://atlas-main.example.com:21000"); + localConf.setProperty("atlas.rest.basic.auth.username", "admin"); + localConf.setProperty("atlas.rest.basic.auth.password", "admin123"); + + RestNotification restNotification = new RestNotification(localConf); + + String[] configuredEndpoints = getConfiguredBaseUrls(restNotification.atlasClientV2); + + assertEquals(configuredEndpoints.length, 1); + assertEquals(configuredEndpoints[0], "http://atlas-rest.example.com:41000/rest"); + } + + @Test + public void testRestNotificationEndpointFallsBackToAtlasRestAddress() throws Exception { + Configuration localConf = new BaseConfiguration(); + localConf.setProperty("atlas.rest.address", "http://atlas-main.example.com:21000"); + localConf.setProperty("atlas.rest.basic.auth.username", "admin"); + localConf.setProperty("atlas.rest.basic.auth.password", "admin123"); + + RestNotification restNotification = new RestNotification(localConf); + + String[] configuredEndpoints = getConfiguredBaseUrls(restNotification.atlasClientV2); + + assertEquals(configuredEndpoints.length, 1); + assertEquals(configuredEndpoints[0], "http://atlas-main.example.com:21000"); + } + + @Test + public void testRestNotificationFallsBackToAtlasRestWhenNotificationAddressesAllBlank() throws Exception { + Configuration localConf = new BaseConfiguration(); + localConf.addProperty("atlas.hook.rest.notification.address", ""); + localConf.addProperty("atlas.hook.rest.notification.address", " "); + localConf.setProperty("atlas.rest.address", "http://atlas-main.example.com:21000"); + localConf.setProperty("atlas.rest.basic.auth.username", "admin"); + localConf.setProperty("atlas.rest.basic.auth.password", "admin123"); + + RestNotification restNotification = new RestNotification(localConf); + + String[] configuredEndpoints = getConfiguredBaseUrls(restNotification.atlasClientV2); + + assertEquals(configuredEndpoints.length, 1); + assertEquals(configuredEndpoints[0], "http://atlas-main.example.com:21000"); + } + + private String[] getConfiguredBaseUrls(AtlasClientV2 atlasClientV2) throws Exception { + Field clientContextField = AtlasBaseClient.class.getDeclaredField("atlasClientContext"); + clientContextField.setAccessible(true); + Object clientContext = clientContextField.get(atlasClientV2); + + Field baseUrlsField = clientContext.getClass().getDeclaredField("baseUrls"); + baseUrlsField.setAccessible(true); + + return (String[]) baseUrlsField.get(clientContext); + } } diff --git a/pom.xml b/pom.xml index 8d27e11ed6a..3bb49cc59f0 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/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java index 4776912cd13..93162d42581 100644 --- a/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java +++ b/repository/src/main/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStore.java @@ -777,7 +777,11 @@ public AtlasBaseTypeDef getByGuid(String guid) throws AtlasBaseException { @Override @GraphTransaction - public void deleteTypeByName(String typeName) throws AtlasBaseException { + public void deleteTypeByName(String typeName, boolean forceDelete) throws AtlasBaseException { + if (LOG.isDebugEnabled()) { + LOG.debug("==> deleteTypeByName({}, forceDelete={})", typeName, forceDelete); + } + AtlasType atlasType = typeRegistry.getType(typeName); if (atlasType == null) { diff --git a/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java b/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java index 792f714ad3f..6c38f27fde3 100644 --- a/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java +++ b/repository/src/main/java/org/apache/atlas/store/AtlasTypeDefStore.java @@ -109,7 +109,7 @@ AtlasClassificationDef updateClassificationDefByGuid(String guid, AtlasClassific AtlasBaseTypeDef getByGuid(String guid) throws AtlasBaseException; - void deleteTypeByName(String typeName) throws AtlasBaseException; + void deleteTypeByName(String typeName, boolean forceDelete) throws AtlasBaseException; void notifyLoadCompletion(); } diff --git a/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java b/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java index f84d1f4610e..90603af0341 100644 --- a/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java +++ b/repository/src/test/java/org/apache/atlas/repository/store/graph/AtlasTypeDefGraphStoreTest.java @@ -369,10 +369,10 @@ public void deleteTypeByName() throws IOException { AtlasTypesDef typesDef = TestResourceFileUtils.readObjectFromJson(".", hivedbV2Json, AtlasTypesDef.class); typeDefStore.createTypesDef(typesDef); - typeDefStore.deleteTypeByName(hiveDB2); - typeDefStore.deleteTypeByName(relationshipDefName); - typeDefStore.deleteTypeByName(hostEntityDef); - typeDefStore.deleteTypeByName(clusterEntityDef); + typeDefStore.deleteTypeByName(hiveDB2, false); + typeDefStore.deleteTypeByName(relationshipDefName, false); + typeDefStore.deleteTypeByName(hostEntityDef, false); + typeDefStore.deleteTypeByName(clusterEntityDef, false); } catch (AtlasBaseException e) { fail("Deletion should've succeeded"); } diff --git a/rest-notification-webapp/pom.xml b/rest-notification-webapp/pom.xml new file mode 100644 index 00000000000..d2f73e4cd04 --- /dev/null +++ b/rest-notification-webapp/pom.xml @@ -0,0 +1,231 @@ + + + + 4.0.0 + + org.apache.atlas + apache-atlas + 3.0.0-SNAPSHOT + + org.apache.atlas + 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 + + + + + + + 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 00000000000..0afad377526 --- /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 00000000000..4d5566213b9 --- /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 00000000000..18cfa7bd7dc --- /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 00000000000..af279de5cfd --- /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 00000000000..22d266e4111 --- /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/RestNotificationHighAvailabilityImpl.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilityImpl.java new file mode 100644 index 00000000000..f650c9cdaa5 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/ha/RestNotificationHighAvailabilityImpl.java @@ -0,0 +1,63 @@ +/** + * 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.HighAvailability; +import org.apache.commons.configuration.Configuration; +import org.springframework.stereotype.Component; + +/** + * Rest-Notification specific implementation of HighAvailability. + * It adapts RestHAConfiguration (not standard HAConfiguration) into + * the common contract. + */ +@Component +public class RestNotificationHighAvailabilityImpl implements HighAvailability { + @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 00000000000..d50368cfa59 --- /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 00000000000..33d1fe3545c --- /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 00000000000..24e030aade5 --- /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 00000000000..542b9fb7e38 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/security/AtlasSecurityConfig.java @@ -0,0 +1,93 @@ +/** + * 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.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; +import org.apache.commons.configuration.Configuration; +import org.springframework.beans.factory.ObjectProvider; +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 javax.inject.Inject; + +/** + * Spring Security configuration for the Atlas rest-notification-webapp. + * + * Why this class was created + * Before this commit, {@code server-common/AtlasSecurityConfig} was a concrete + * {@code @EnableWebSecurity} class. It also injected + * {@code @Qualifier("staleTransactionCleanupFilter")} — a bean that only exists + * in the webapp Spring context. When rest-notification-webapp started, Spring + * threw {@code NoSuchBeanDefinitionException} because no such bean was registered, + * preventing the Kerberos authentication filter chain from initializing. + * + * This class was created to be the SINGLE {@code @EnableWebSecurity} authority + * for rest-notification-webapp — completely separate from webapp's security config. + * + * What is intentionally ABSENT here (compared to webapp) + * NO {@code staleTransactionCleanupFilter} — rest-notification makes no direct + * JanusGraph writes and has no such bean in its context + * NO form login ({@code addWebUiFormLogin}) — rest-notification is a headless + * REST server with no browser UI, login page, or interactive sessions. + */ +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class AtlasSecurityConfig extends org.apache.atlas.server.common.security.AtlasSecurityConfig { + @Inject + public AtlasSecurityConfig(ObjectProvider ssoAuthenticationFilterProvider, + ObjectProvider csrfPreventionFilterProvider, + ObjectProvider atlasAuthenticationFilterProvider, + AtlasAuthenticationProvider authenticationProvider, + AtlasAuthenticationSuccessHandler successHandler, + AtlasAuthenticationFailureHandler failureHandler, + org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint, + Configuration configuration, + ObjectProvider activeServerFilterProvider) { + super(ssoAuthenticationFilterProvider, csrfPreventionFilterProvider, atlasAuthenticationFilterProvider, + authenticationProvider, successHandler, failureHandler, atlasAuthenticationEntryPoint, configuration, + activeServerFilterProvider); + } + + @Override + public void configure(WebSecurity web) { + web.ignoring() + .antMatchers("/login.jsp", + "/css/**", + "/n/css/**", + "/img/**", + "/n/img/**", + "/libs/**", + "/n/libs/**", + "/js/**", + "/n/js/**", + "/ieerror.html", + "/migration-status.html", + "/rest/api/atlas/admin/status"); + } + + @Override + protected void configure(HttpSecurity httpSecurity) throws Exception { + configureCommonHttpSecurity(httpSecurity); + addHaAndMigrationGuards(httpSecurity); + addCommonAuthFilters(httpSecurity); + } +} diff --git a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/services/AtlasServiceStateProviderConfig.java b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/services/AtlasServiceStateProviderConfig.java new file mode 100644 index 00000000000..9b489bb3c81 --- /dev/null +++ b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/services/AtlasServiceStateProviderConfig.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 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.services; + +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; + +/** + * Local adapter for Rest-notification module. + * Bridges local ActiveInstanceState and ServiceState to shared Security SPIs. + */ +@Configuration +public class AtlasServiceStateProviderConfig { + @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 00000000000..2404f8fb939 --- /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 00000000000..2ecd8d1199f --- /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 00000000000..06647d59e4c --- /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 00000000000..cca55e44f20 --- /dev/null +++ b/server-common/pom.xml @@ -0,0 +1,195 @@ + + + + 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.keycloak + keycloak-spring-security-adapter + ${keycloak.version} + + + com.fasterxml.jackson.core + jackson-core + + + + + 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 be3bcb4ea6e..3bf79ae0251 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 89c768800ae..efccdea1657 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 04acbfc8f99..de800f43283 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 7526c18022a..62368c47292 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 652af3a86ec..56fb1e4ca39 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 7dc12acc37e..143164df6da 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 86% 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 09cb1b99774..80db7ee62b5 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,14 +25,14 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; import java.util.LinkedHashMap; public class AtlasDelegatingAuthenticationEntryPoint extends DelegatingAuthenticationEntryPoint { + + public static final String SESSION_TIMEOUT = "Session Timeout"; private static final Logger LOG = LoggerFactory.getLogger(AtlasDelegatingAuthenticationEntryPoint.class); - public static final String SESSION_TIMEOUT = "Session Timeout"; public AtlasDelegatingAuthenticationEntryPoint(LinkedHashMap entryPoints) { super(entryPoints); @@ -45,11 +45,12 @@ public void commence(HttpServletRequest request, HttpServletResponse response, A 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()); 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 97% 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 7fe2c42b4fe..38bcf5a14c1 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,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.Filter; import javax.servlet.FilterChain; 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 a5dca7aaeea..cbd2e08b2f8 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 f8b7b32c6f5..2631eee0a67 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 12d34353ba9..a154b84a2c9 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 dbec3cdbfab..d5663a7f93e 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 60801aac64e..e92e6f03c26 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 96% 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 d2f335e2c90..3f0259334e9 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; @@ -71,7 +71,7 @@ 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(); } 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 d15f075625b..7ffe059b3ce 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 890bf25433f..3e260ce9040 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 00000000000..1edbba55468 --- /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 00000000000..6ef3361cbfd --- /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 00000000000..ab7d6129f99 --- /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 e8a198e3399..066da4329f2 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 c7bbdceb00a..be664b62e1f 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 59329a0643d..3c57b8dd73d 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 cc2d1587c1b..7c3014d4e49 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 cda9fad6229..34be0fb46fa 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 514ccbc4db4..452b254869a 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/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java similarity index 81% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java index 3cff9fb8eeb..f78be18192f 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasAuthenticationProvider.java @@ -15,7 +15,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; import org.apache.commons.configuration.Configuration; @@ -32,48 +32,56 @@ @Component @Scope("prototype") public class AtlasAuthenticationProvider extends AtlasAbstractAuthenticationProvider { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationProvider.class); + private static final Logger LOG = LoggerFactory + .getLogger(AtlasAuthenticationProvider.class); - 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 fileAuthenticationMethodEnabled = true; + private boolean pamAuthenticationEnabled = false; + private boolean keycloakAuthenticationEnabled; + private String ldapType = "NONE"; + public static final String FILE_AUTH_METHOD = "atlas.authentication.method.file"; + public static final String LDAP_AUTH_METHOD = "atlas.authentication.method.ldap"; + public static final String LDAP_TYPE = "atlas.authentication.method.ldap.type"; + public static final String PAM_AUTH_METHOD = "atlas.authentication.method.pam"; public static final String KEYCLOAK_AUTH_METHOD = "atlas.authentication.method.keycloak"; + private boolean ssoEnabled; + final AtlasLdapAuthenticationProvider ldapAuthenticationProvider; + final AtlasFileAuthenticationProvider fileAuthenticationProvider; + final AtlasADAuthenticationProvider adAuthenticationProvider; + final AtlasPamAuthenticationProvider pamAuthenticationProvider; - final AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider; - private boolean fileAuthenticationMethodEnabled = true; - private boolean pamAuthenticationEnabled; - private boolean keycloakAuthenticationEnabled; - private String ldapType = "NONE"; - private boolean ssoEnabled; + final AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider; @Inject public AtlasAuthenticationProvider(AtlasLdapAuthenticationProvider ldapAuthenticationProvider, - AtlasFileAuthenticationProvider fileAuthenticationProvider, AtlasADAuthenticationProvider adAuthenticationProvider, - AtlasPamAuthenticationProvider pamAuthenticationProvider, AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider) { - this.ldapAuthenticationProvider = ldapAuthenticationProvider; - this.fileAuthenticationProvider = fileAuthenticationProvider; - this.adAuthenticationProvider = adAuthenticationProvider; - this.pamAuthenticationProvider = pamAuthenticationProvider; + AtlasFileAuthenticationProvider fileAuthenticationProvider, + AtlasADAuthenticationProvider adAuthenticationProvider, + AtlasPamAuthenticationProvider pamAuthenticationProvider, AtlasKeycloakAuthenticationProvider atlasKeycloakAuthenticationProvider) { + this.ldapAuthenticationProvider = ldapAuthenticationProvider; + this.fileAuthenticationProvider = fileAuthenticationProvider; + this.adAuthenticationProvider = adAuthenticationProvider; + this.pamAuthenticationProvider = pamAuthenticationProvider; this.atlasKeycloakAuthenticationProvider = atlasKeycloakAuthenticationProvider; } @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - if (ssoEnabled) { - if (authentication != null) { - authentication = getSSOAuthentication(authentication); + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { - if (authentication != null && authentication.isAuthenticated()) { + if(ssoEnabled){ + if (authentication != null){ + authentication = getSSOAuthentication(authentication); + if(authentication!=null && authentication.isAuthenticated()){ return authentication; } } } else { + if (ldapType.equalsIgnoreCase("LDAP")) { try { authentication = ldapAuthenticationProvider.authenticate(authentication); @@ -114,7 +122,6 @@ public Authentication authenticate(Authentication authentication) throws Authent } LOG.error("Authentication failed."); - throw new AtlasAuthenticationException("Authentication failed."); } 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 94% 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 5d6834fd199..755f0ddb4f3 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; @@ -34,7 +34,7 @@ @Component public class AtlasAuthenticationSuccessHandler implements AuthenticationSuccessHandler { - private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationSuccessHandler.class); + private static Logger LOG = LoggerFactory.getLogger(AuthenticationSuccessHandler.class); public static final String LOCALLOGIN = "locallogin"; 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 096ca26d837..bda282009a8 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/AtlasKeycloakAuthenticationProvider.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasKeycloakAuthenticationProvider.java similarity index 94% rename from webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java rename to server-common/src/main/java/org/apache/atlas/server/common/security/AtlasKeycloakAuthenticationProvider.java index 9839a219515..78893a0981e 100644 --- a/webapp/src/main/java/org/apache/atlas/web/security/AtlasKeycloakAuthenticationProvider.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasKeycloakAuthenticationProvider.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.atlas.web.security; +package org.apache.atlas.server.common.security; import org.apache.atlas.ApplicationProperties; import org.apache.commons.configuration.Configuration; @@ -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/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 c401576006d..057f764fedf 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 49970129c3c..fac38d30cc9 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/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasSecurityConfig.java b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasSecurityConfig.java new file mode 100644 index 00000000000..bcab25f47ad --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/security/AtlasSecurityConfig.java @@ -0,0 +1,236 @@ +/** + * 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.server.common.filters.ActiveServerFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationFilter; +import org.apache.atlas.server.common.filters.AtlasAuthenticationEntryPoint; +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.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.context.annotation.Bean; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +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 java.util.List; + +import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; + +/** + * Abstract base class for Atlas Spring Security configuration, shared between + * {@code webapp} and {@code rest-notification-webapp}. + * + * Why this class is abstract + * This class was previously a concrete {@code @EnableWebSecurity} class. + * Having {@code @EnableWebSecurity} on a class in {@code server-common} caused + * Spring to find TWO competing security configurations on the classpath when + * either webapp started — this concrete base class AND the module-specific + * subclass (if it existed) — causing a bean registration conflict that prevented + * the Kerberos filter chain from assembling correctly. + * + * Making this class {@code abstract} ensures Spring Security NEVER tries to + * instantiate it directly. Only the concrete subclasses in each module carry + * the {@code @EnableWebSecurity} annotation and are registered as the security + * configuration bean. + */ +public abstract class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { + private static final Logger LOG = LoggerFactory.getLogger(AtlasSecurityConfig.class); + + protected final AtlasAuthenticationProvider authenticationProvider; + protected final AtlasAuthenticationSuccessHandler successHandler; + protected final AtlasAuthenticationFailureHandler failureHandler; + protected final ObjectProvider ssoAuthenticationFilterProvider; + protected final ObjectProvider atlasAuthenticationFilterProvider; + protected final ObjectProvider csrfPreventionFilterProvider; + protected final AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint; + protected final Configuration configuration; + protected final ObjectProvider activeServerFilterProvider; + + @Inject + public AtlasSecurityConfig(ObjectProvider ssoAuthenticationFilterProvider, + ObjectProvider csrfPreventionFilterProvider, + ObjectProvider atlasAuthenticationFilterProvider, + AtlasAuthenticationProvider authenticationProvider, + AtlasAuthenticationSuccessHandler successHandler, + AtlasAuthenticationFailureHandler failureHandler, + AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint, + Configuration configuration, + ObjectProvider activeServerFilterProvider) { + this.ssoAuthenticationFilterProvider = ssoAuthenticationFilterProvider; + this.csrfPreventionFilterProvider = csrfPreventionFilterProvider; + this.atlasAuthenticationFilterProvider = atlasAuthenticationFilterProvider; + this.authenticationProvider = authenticationProvider; + this.successHandler = successHandler; + this.failureHandler = failureHandler; + this.atlasAuthenticationEntryPoint = atlasAuthenticationEntryPoint; + this.configuration = configuration; + this.activeServerFilterProvider = activeServerFilterProvider; + } + + public AuthenticationEntryPoint getAuthenticationEntryPoint() throws Exception { + LinkedHashMap entryPointMap = new LinkedHashMap<>(); + entryPointMap.put(new RequestHeaderRequestMatcher(HeadersUtil.USER_AGENT_KEY, HeadersUtil.USER_AGENT_VALUE), atlasAuthenticationEntryPoint); + return new AtlasDelegatingAuthenticationEntryPoint(entryPointMap); + } + + public DelegatingAuthenticationEntryPoint getDelegatingAuthenticationEntryPoint() throws Exception { + 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); + } + + protected void configureCommonHttpSecurity(HttpSecurity httpSecurity) throws Exception { + 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(); + + httpSecurity + .httpBasic() + .authenticationEntryPoint(getDelegatingAuthenticationEntryPoint()); + } + + protected void addWebUiFormLogin(HttpSecurity httpSecurity) throws Exception { + httpSecurity + .formLogin() + .loginPage("/login.jsp") + .loginProcessingUrl("/j_spring_security_check") + .successHandler(successHandler) + .failureHandler(failureHandler) + .usernameParameter("j_username") + .passwordParameter("j_password") + .and() + .logout() + .logoutSuccessUrl("/login.jsp") + .deleteCookies("ATLASSESSIONID") + .logoutUrl("/logout.html"); + } + + protected void addHaAndMigrationGuards(HttpSecurity httpSecurity) throws Exception { + boolean configMigrationEnabled = !StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME)); + if (configuration.getBoolean("atlas.server.ha.enabled", false) || configMigrationEnabled) { + if (configMigrationEnabled) { + LOG.info("Atlas is in Migration Mode, enabling ActiveServerFilter"); + } else { + LOG.info("Atlas is in HA Mode, enabling ActiveServerFilter"); + } + ActiveServerFilter activeServerFilter = activeServerFilterProvider.getIfAvailable(); + if (activeServerFilter != null) { + httpSecurity.addFilterAfter(activeServerFilter, BasicAuthenticationFilter.class); + } + } + } + + protected void addCommonAuthFilters(HttpSecurity httpSecurity) throws Exception { + AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFilter = ssoAuthenticationFilterProvider.getIfAvailable(); + if (ssoAuthenticationFilter != null) { + httpSecurity.addFilterBefore(ssoAuthenticationFilter, BasicAuthenticationFilter.class); + } + + AtlasAuthenticationFilter atlasAuthenticationFilter = atlasAuthenticationFilterProvider.getIfAvailable(); + if (atlasAuthenticationFilter != null) { + httpSecurity.addFilterAfter(atlasAuthenticationFilter, SecurityContextHolderAwareRequestFilter.class); + } + + AtlasCSRFPreventionFilter csrfPreventionFilter = csrfPreventionFilterProvider.getIfAvailable(); + if (csrfPreventionFilter != null && atlasAuthenticationFilter != null) { + httpSecurity.addFilterAfter(csrfPreventionFilter, AtlasAuthenticationFilter.class); + } + } + + @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); + } + }; + } + + @Bean + public ActiveServerFilter activeServerFilter(ActiveInstanceStateProvider activeInstanceStateProvider, + ServiceStateProvider serviceStateProvider) { + return new ActiveServerFilter(activeInstanceStateProvider, serviceStateProvider); + } + + @Bean + public AtlasAuthenticationFilter atlasAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasKnoxSSOAuthenticationFilter atlasKnoxSSOAuthenticationFilter(AtlasAuthenticationProviderBridge atlasAuthenticationProviderBridge) { + return new AtlasKnoxSSOAuthenticationFilter(atlasAuthenticationProviderBridge); + } + + @Bean + public AtlasCSRFPreventionFilter atlasCSRFPreventionFilter() { + return new AtlasCSRFPreventionFilter(); + } +} 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 8ed9daef84d..f4b8640effe 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 082c1c1bb7e..9fb5759bf85 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 defd54e4135..b691692ed86 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 80% 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 ac9ecd5c9c4..c7b222c34c0 100644 --- a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceElectorService.java +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceElectorService.java @@ -15,15 +15,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.atlas.web.service; +package org.apache.atlas.server.common.service; import org.apache.atlas.AtlasException; -import org.apache.atlas.ha.AtlasServerIdSelector; -import org.apache.atlas.ha.HAConfiguration; +import org.apache.atlas.RequestContext; import org.apache.atlas.listener.ActiveStateChangeHandler; import org.apache.atlas.service.Service; -import org.apache.atlas.util.AtlasMetricsUtil; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.recipes.leader.LeaderLatch; import org.apache.curator.framework.recipes.leader.LeaderLatchListener; @@ -35,13 +32,14 @@ import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; /** * A service that implements leader election to determine whether this Atlas server is Active. - * + *

* The service implements leader election through Curator's * {@link LeaderLatch} recipe. The service also implements {@link LeaderLatchListener} to get * notified of changes to leadership state. Upon becoming leader, this instance is treated as the @@ -49,7 +47,6 @@ * on being removed from leadership, this instance is treated as a passive instance and calls * {@link ActiveStateChangeHandler}s to deactivate them. */ - @Component // // This should be called the last, leaving it without the @Order(Integer.MAX_VALUE) will make it get @@ -60,7 +57,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 HighAvailability highAvailability; + private final Set serviceStateChangeHandlers; private final Set activeStateChangeHandlerProviders; private final List activeStateChangeHandlers; private final CuratorFactory curatorFactory; @@ -69,57 +67,67 @@ public class ActiveInstanceElectorService implements Service, LeaderLatchListene /** * Create a new instance of {@link ActiveInstanceElectorService} + * * @param activeStateChangeHandlerProviders The list of registered {@link ActiveStateChangeHandler}s that - * must be called back on state changes. + * must be called back on state changes. * @throws AtlasException */ @Inject - ActiveInstanceElectorService(Configuration configuration, Set activeStateChangeHandlerProviders, - CuratorFactory curatorFactory, ActiveInstanceState activeInstanceState, ServiceState serviceState, AtlasMetricsUtil metricsUtil) { + public ActiveInstanceElectorService(Configuration configuration, + Set activeStateChangeHandlerProviders, + Set serviceStateChangeHandlers, + CuratorFactory curatorFactory, + ActiveInstanceState activeInstanceState, + ServiceState serviceState, + HighAvailability highAvailability) { this.configuration = configuration; this.activeStateChangeHandlerProviders = activeStateChangeHandlerProviders; + this.serviceStateChangeHandlers = serviceStateChangeHandlers != null + ? serviceStateChangeHandlers : Collections.emptySet(); this.activeStateChangeHandlers = new ArrayList<>(); this.curatorFactory = curatorFactory; this.activeInstanceState = activeInstanceState; this.serviceState = serviceState; - this.metricsUtil = metricsUtil; + this.highAvailability = highAvailability; } /** * Join leader election on starting up. - * + *

* If Atlas High Availability configuration is disabled, this operation is a no-op. + * * @throws AtlasException */ @Override public void start() throws AtlasException { - metricsUtil.onServerStart(); + boolean haEnabled = highAvailability.isHAEnabled(configuration); - if (!HAConfiguration.isHAEnabled(configuration)) { - metricsUtil.onServerActivation(); + serviceStateChangeHandlers.forEach(hook -> { + hook.onServerStart(); + if (!haEnabled) { + hook.onServerActivation(); + } + }); + if (!haEnabled) { LOG.info("HA is not enabled, no need to start leader election service"); - return; } cacheActiveStateChangeHandlers(); - - serverId = AtlasServerIdSelector.selectServerId(configuration); - + serverId = highAvailability.selectServerId(configuration); joinElection(); } /** * Leave leader election process and clean up resources on shutting down. - * + *

* If Atlas High Availability configuration is disabled, this operation is a no-op. */ @Override public void stop() { - if (!HAConfiguration.isHAEnabled(configuration)) { + if (!highAvailability.isHAEnabled(configuration)) { LOG.info("HA is not enabled, no need to stop leader election service"); - return; } @@ -133,29 +141,29 @@ public void stop() { /** * Call all registered {@link ActiveStateChangeHandler}s on being elected active. - * + *

* In addition, shared state information about this instance becoming active is updated * using {@link ActiveInstanceState}. */ @Override public void isLeader() { LOG.warn("Server instance with server id {} is elected as leader", serverId); - serviceState.becomingActive(); - try { for (ActiveStateChangeHandler handler : activeStateChangeHandlers) { handler.instanceIsActive(); } - activeInstanceState.update(serverId); serviceState.setActive(); - metricsUtil.onServerActivation(); + for (ServiceStateChangeHandler serviceStateChangeHandler : serviceStateChangeHandlers) { + serviceStateChangeHandler.onServerActivation(); + } } catch (Exception e) { LOG.error("Got exception while activating", e); - notLeader(); rejoinElection(); + } finally { + RequestContext.clear(); } } @@ -165,9 +173,7 @@ public void isLeader() { @Override public void notLeader() { LOG.warn("Server instance with server id {} is removed as leader", serverId); - serviceState.becomingPassive(); - for (int idx = activeStateChangeHandlers.size() - 1; idx >= 0; idx--) { try { activeStateChangeHandlers.get(idx).instanceIsPassive(); @@ -175,22 +181,17 @@ public void notLeader() { LOG.error("Error while reacting to passive state.", e); } } - serviceState.setPassive(); } private void joinElection() { LOG.info("Starting leader election for {}", serverId); - String zkRoot = HAConfiguration.getZookeeperProperties(configuration).getZkRoot(); - + String zkRoot = highAvailability.getZookeeperProperties(configuration).getZkRoot(); leaderLatch = curatorFactory.leaderLatchInstance(serverId, zkRoot); - leaderLatch.addListener(this); - try { leaderLatch.start(); - LOG.info("Leader latch started for {}.", serverId); } catch (Exception e) { LOG.info("Exception while starting leader latch for {}.", serverId, e); @@ -212,7 +213,6 @@ private void cacheActiveStateChangeHandlers() { private void rejoinElection() { try { leaderLatch.close(); - joinElection(); } catch (IOException e) { LOG.error("Error rejoining election", e); diff --git a/webapp/src/main/java/org/apache/atlas/web/service/ActiveInstanceState.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ActiveInstanceState.java similarity index 75% 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 8e79dbe6488..e0d7e6849d5 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 HighAvailability highAvailability; /** * 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) { - this.configuration = configuration; - this.curatorFactory = curatorFactory; + public ActiveInstanceState(Configuration configuration, CuratorFactory curatorFactory, HighAvailability highAvailability) { + this.configuration = configuration; + this.curatorFactory = curatorFactory; + this.highAvailability = highAvailability; } /** @@ -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 (!highAvailability.isHAEnabled(configuration)) { + return; + } + try { - CuratorFramework client = curatorFactory.clientInstance(); + CuratorFramework client = curatorFactory.clientInstance(); - HAConfiguration.ZookeeperProperties zookeeperProperties = HAConfiguration.getZookeeperProperties(configuration); + HighAvailabilityProperties zookeeperProperties = highAvailability.getZookeeperProperties(configuration); - String atlasServerAddress = HAConfiguration.getBoundAddressForId(configuration, serverId); + String atlasServerAddress = highAvailability.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 (!highAvailability.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 = highAvailability.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 fc77ee3f8b4..884217c5a7e 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 84% 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 7a390f4985f..2fa7d6b4144 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 HighAvailability highAvailability; /** * Initializes the {@link CuratorFramework} that is used for all interaction with Zookeeper. * @throws AtlasException */ - public CuratorFactory() throws AtlasException { - this(ApplicationProperties.get()); - } - - public CuratorFactory(Configuration configuration) { - this.configuration = configuration; + @Inject + public CuratorFactory(Configuration configuration, HighAvailability highAvailability) { + this.configuration = configuration; + this.highAvailability = highAvailability; - if (configuration.containsKey(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY) && configuration.getBoolean(HAConfiguration.ATLAS_SERVER_HA_ENABLED_KEY, true)) { + if (highAvailability.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 = highAvailability.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 37165c44b5f..9df6fb0ac94 --- 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/HighAvailability.java b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailability.java new file mode 100644 index 00000000000..f4996e7be77 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/HighAvailability.java @@ -0,0 +1,50 @@ +/** + * 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.commons.configuration.Configuration; + +/** + * Interface to abstract High Availability (HA) configuration retrieval. + * Enables shared services in 'server-common' to operate without direct + * dependencies on application-specific configuration classes. + */ +public interface HighAvailability { + + /** + * 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); +} 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 00000000000..f6caf4f85a3 --- /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/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 c898861d57a..ac81bcf8cad --- 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/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 57% 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 b2e7dd705a1..6ccc1b10f0b 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 HighAvailability highAvailability; - public ServiceState() throws AtlasException { - this(ApplicationProperties.get()); - } - - public ServiceState(Configuration configuration) { - this.configuration = configuration; + @Inject + public ServiceState(Configuration configuration, HighAvailability highAvailability) { + this.configuration = configuration; + this.highAvailability = highAvailability; - state = !HAConfiguration.isHAEnabled(configuration) ? ServiceStateValue.ACTIVE : ServiceStateValue.PASSIVE; + state = !highAvailability.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(highAvailability.isHAEnabled(configuration), + "Cannot change state as requested, as HA is not enabled for this instance."); + + state = newState; } } diff --git a/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceStateChangeHandler.java b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceStateChangeHandler.java new file mode 100644 index 00000000000..31f065321c9 --- /dev/null +++ b/server-common/src/main/java/org/apache/atlas/server/common/service/ServiceStateChangeHandler.java @@ -0,0 +1,38 @@ +/** + * 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; + +/** + * A hook interface for reacting to server lifecycle events. + * Used to decouple core service logic. + */ +public interface ServiceStateChangeHandler { + + /** + * Invoked when the server process begins its initialization. + */ + default void onServerStart() { + } + + /** + * Invoked when the server instance is elected as the ACTIVE instance + * in an HA cluster or starts up in non-HA mode. + */ + default void onServerActivation() { + } +} 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 e0ea2b2c02d..ab1eb820b3d 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 5fb98488550..c89a7803c54 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 dbdcd98e423..f2cfb8c1844 --- 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 d3ccc6cd482..3b4f8c93b2a --- 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 9f5c5bb4e6f..12b8b2c0580 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 @@ -358,18 +363,6 @@ javax.el - - - org.keycloak - keycloak-spring-security-adapter - ${keycloak.version} - - - com.fasterxml.jackson.core - jackson-core - - - org.kohsuke diff --git a/webapp/src/main/java/org/apache/atlas/Atlas.java b/webapp/src/main/java/org/apache/atlas/Atlas.java index 07f4c30fab1..d7f94624c7e 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 32bf3016673..5fcff85dcaf 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 182c26dbb97..050e3fca3b7 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/HighAvailabilityImpl.java b/webapp/src/main/java/org/apache/atlas/web/ha/HighAvailabilityImpl.java new file mode 100644 index 00000000000..1b63e71f7a0 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/ha/HighAvailabilityImpl.java @@ -0,0 +1,63 @@ +/** + * 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.HighAvailability; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.commons.configuration.Configuration; +import org.springframework.stereotype.Component; + +/** + * WebApp-specific implementation of HighAvailability. + * This class adapts the legacy HAConfiguration and AtlasServerIdSelector + * into the common contract required by the shared server engine. + */ +@Component +public class HighAvailabilityImpl implements HighAvailability { + @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/ServiceStateChangeAuditHandler.java b/webapp/src/main/java/org/apache/atlas/web/metrics/ServiceStateChangeAuditHandler.java new file mode 100644 index 00000000000..b944c107ecd --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/metrics/ServiceStateChangeAuditHandler.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.ServiceStateChangeHandler; +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 ServiceStateChangeMetricHandler} as a {@link ServiceStateChangeHandler} + * 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 ServiceStateChangeAuditHandler implements ServiceStateChangeHandler { + private static final Logger LOG = LoggerFactory.getLogger(ServiceStateChangeAuditHandler.class); + + private static final String ADMIN_AUDIT_USER = "atlas"; + + private final AtlasAuditService auditService; + + @Inject + public ServiceStateChangeAuditHandler(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/ServiceStateChangeMetricHandler.java b/webapp/src/main/java/org/apache/atlas/web/metrics/ServiceStateChangeMetricHandler.java new file mode 100644 index 00000000000..0d037110ae7 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/metrics/ServiceStateChangeMetricHandler.java @@ -0,0 +1,49 @@ +/** + * 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.ServiceStateChangeHandler; +import org.apache.atlas.util.AtlasMetricsUtil; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +/** + * WebApp implementation of the ServiceStateChangeHandler. + * This class bridges the common server lifecycle events to the + * WebApp-specific AtlasMetricsUtil. + */ +@Component +public class ServiceStateChangeMetricHandler implements ServiceStateChangeHandler { + private final AtlasMetricsUtil metricsUtil; + + @Inject + public ServiceStateChangeMetricHandler(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 b092d480684..9df94d96880 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 0c14be19883..469c161a791 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 7d6e829a422..ccf66c59505 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 61ceca3e529..6dca03e32d5 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 280e13760ea..da6fb907400 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 8451997fc88..b05bd4dc080 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 6f404a656ff..1719653b777 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 07f142d82a6..1f0e380cda3 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 a9b7196674e..e83a10d16ff 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 78ebd52c043..df379f82906 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 54b62699853..ef3a94b46ef 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 d0c8a736fa0..6c437ea98b1 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 db64802d40d..6f79c3442c2 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 e42b7627b11..47955e4f0e1 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; @@ -441,7 +441,7 @@ public void deleteAtlasTypeByName(@PathParam("typeName") final String typeName) perf = AtlasPerfTracer.getPerfTracer(PERF_LOG, "TypesREST.deleteAtlasTypeByName(" + typeName + ")"); } - typeDefStore.deleteTypeByName(typeName); + typeDefStore.deleteTypeByName(typeName, false); } finally { AtlasPerfTracer.log(perf); } 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 5058ebdf53d..acfaf59eaf0 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,16 +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.web.filters.StaleTransactionCleanupFilter; +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.AtlasKnoxSSOAuthenticationFilter; +import org.apache.atlas.server.common.security.AtlasAuthenticationFailureHandler; +import org.apache.atlas.server.common.security.AtlasAuthenticationProvider; +import org.apache.atlas.server.common.security.AtlasAuthenticationSuccessHandler; import org.apache.commons.configuration.Configuration; -import org.apache.commons.lang3.StringUtils; import org.keycloak.adapters.AdapterDeploymentContext; import org.keycloak.adapters.KeycloakConfigResolver; import org.keycloak.adapters.KeycloakDeployment; @@ -42,28 +41,23 @@ import org.keycloak.adapters.springsecurity.filter.QueryParamPresenceRequestMatcher; import org.keycloak.adapters.springsecurity.management.HttpSessionManager; import org.keycloak.representations.adapters.config.AdapterConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; -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.core.session.SessionRegistryImpl; import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; 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.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.OrRequestMatcher; @@ -71,38 +65,42 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import javax.inject.Inject; +import javax.servlet.Filter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import static org.apache.atlas.AtlasConstants.ATLAS_MIGRATION_MODE_FILENAME; -import static org.apache.atlas.web.filters.HeadersUtil.SERVER_KEY; - +/** + * Spring Security configuration for the Atlas main webapp. + * + * Why this class was created + * Before this commit, {@code server-common/AtlasSecurityConfig} was a concrete + * {@code @EnableWebSecurity} class. That caused Spring to register TWO security + * configurations when the webapp started — the server-common one (found on + * classpath) and any module-specific one. This bean conflict prevented the + * Kerberos authentication filter chain from initializing correctly. + * + * This class was created as the SINGLE {@code @EnableWebSecurity} authority + * for the webapp, extending the now-abstract server-common base. Spring Security + * sees only this concrete class and builds one correct filter chain. + * + * What is webapp-specific in this class (not in base) + * {@code staleTransactionCleanupFilter} — cleans up open JanusGraph transactions. + * Only exists in webapp's Spring context; rest-notification-webapp has no + * JanusGraph transactions. Was previously in server-common's constructor + * causing {@code NoSuchBeanDefinitionException} at rest-notification startup. + */ @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) @KeycloakConfiguration -public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { - private static final Logger LOG = LoggerFactory.getLogger(AtlasSecurityConfig.class); - +public class AtlasSecurityConfig extends org.apache.atlas.server.common.security.AtlasSecurityConfig { public static final RequestMatcher KEYCLOAK_REQUEST_MATCHER = new OrRequestMatcher(new AntPathRequestMatcher("/login.jsp"), new RequestHeaderRequestMatcher("Authorization"), new QueryParamPresenceRequestMatcher("access_token")); - 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 StaleTransactionCleanupFilter staleTransactionCleanupFilter; - private final ActiveServerFilter activeServerFilter; - private final boolean keycloakEnabled; + private final boolean keycloakEnabled; + private final ObjectProvider staleTransactionCleanupFilterProvider; @Value("${keycloak.configurationFile:WEB-INF/keycloak.json}") private Resource keycloakConfigFileResource; @@ -111,68 +109,33 @@ public class AtlasSecurityConfig extends WebSecurityConfigurerAdapter { private KeycloakConfigResolver keycloakConfigResolver; @Inject - public AtlasSecurityConfig(AtlasKnoxSSOAuthenticationFilter ssoAuthenticationFilter, - AtlasCSRFPreventionFilter atlasCSRFPreventionFilter, - AtlasAuthenticationFilter atlasAuthenticationFilter, - AtlasAuthenticationProvider authenticationProvider, - AtlasAuthenticationSuccessHandler successHandler, - AtlasAuthenticationFailureHandler failureHandler, - AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint, - Configuration configuration, - StaleTransactionCleanupFilter staleTransactionCleanupFilter, - 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.staleTransactionCleanupFilter = staleTransactionCleanupFilter; - this.activeServerFilter = activeServerFilter; - + public AtlasSecurityConfig(ObjectProvider ssoAuthenticationFilterProvider, + ObjectProvider csrfPreventionFilterProvider, + ObjectProvider atlasAuthenticationFilterProvider, + AtlasAuthenticationProvider authenticationProvider, + AtlasAuthenticationSuccessHandler successHandler, + AtlasAuthenticationFailureHandler failureHandler, + AtlasAuthenticationEntryPoint atlasAuthenticationEntryPoint, + Configuration configuration, + ObjectProvider activeServerFilterProvider, + @Qualifier("staleTransactionCleanupFilter") ObjectProvider staleTransactionCleanupFilterProvider) { + super(ssoAuthenticationFilterProvider, csrfPreventionFilterProvider, atlasAuthenticationFilterProvider, + authenticationProvider, successHandler, failureHandler, atlasAuthenticationEntryPoint, configuration, + activeServerFilterProvider); + this.staleTransactionCleanupFilterProvider = staleTransactionCleanupFilterProvider; this.keycloakEnabled = configuration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false); } + @Override public AuthenticationEntryPoint getAuthenticationEntryPoint() throws Exception { - AuthenticationEntryPoint authenticationEntryPoint; - if (keycloakEnabled) { KeycloakAuthenticationEntryPoint keycloakAuthenticationEntryPoint = new KeycloakAuthenticationEntryPoint(adapterDeploymentContext()); keycloakAuthenticationEntryPoint.setRealm("atlas.com"); keycloakAuthenticationEntryPoint.setLoginUri("/login.jsp"); - - authenticationEntryPoint = keycloakAuthenticationEntryPoint; - } else { - LinkedHashMap entryPointMap = new LinkedHashMap<>(); - - entryPointMap.put(new RequestHeaderRequestMatcher(HeadersUtil.USER_AGENT_KEY, HeadersUtil.USER_AGENT_VALUE), atlasAuthenticationEntryPoint); - - AtlasDelegatingAuthenticationEntryPoint basicAuthenticationEntryPoint = new AtlasDelegatingAuthenticationEntryPoint(entryPointMap); - - authenticationEntryPoint = basicAuthenticationEntryPoint; + return keycloakAuthenticationEntryPoint; } - - return authenticationEntryPoint; - } - - public DelegatingAuthenticationEntryPoint getDelegatingAuthenticationEntryPoint() throws Exception { - LinkedHashMap entryPointMap = new LinkedHashMap<>(); - - entryPointMap.put(new RequestHeaderRequestMatcher(HeadersUtil.USER_AGENT_KEY, HeadersUtil.USER_AGENT_VALUE), atlasAuthenticationEntryPoint); - - DelegatingAuthenticationEntryPoint entryPoint = new DelegatingAuthenticationEntryPoint(entryPointMap); - - entryPoint.setDefaultEntryPoint(getAuthenticationEntryPoint()); - - return entryPoint; - } - - @Inject - protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) { - authenticationManagerBuilder.authenticationProvider(authenticationProvider); + return super.getAuthenticationEntryPoint(); } @Override @@ -182,66 +145,28 @@ public void configure(WebSecurity web) { "/libs/**", "/n/libs/**", "/js/**", "/n/js/**", "/ieerror.html", "/migration-status.html", - "/api/atlas/admin/status")); + "/api/atlas/admin/status", + "/api/atlas/admin/prometheus")); if (!keycloakEnabled) { matchers.add("/login.jsp"); } - web.ignoring().antMatchers(matchers.toArray(new String[matchers.size()])); + web.ignoring().antMatchers(matchers.toArray(new String[0])); } + @Override 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(SERVER_KEY, HeadersUtil.getHeaderMap(SERVER_KEY))) - .and() - .servletApi() - .and() - .csrf().disable() - .sessionManagement() - .enableSessionUrlRewriting(false) - .sessionCreationPolicy(SessionCreationPolicy.ALWAYS) - .sessionFixation() - .newSession() - .and() - .httpBasic() - .authenticationEntryPoint(getDelegatingAuthenticationEntryPoint()) - .and() - .formLogin() - .loginPage("/login.jsp") - .loginProcessingUrl("/j_spring_security_check") - .successHandler(successHandler) - .failureHandler(failureHandler) - .usernameParameter("j_username") - .passwordParameter("j_password") - .and() - .logout() - .logoutSuccessUrl("/login.jsp") - .deleteCookies("ATLASSESSIONID") - .logoutUrl("/logout.html"); - - //@formatter:on - - boolean configMigrationEnabled = !StringUtils.isEmpty(configuration.getString(ATLAS_MIGRATION_MODE_FILENAME)); - - if (configuration.getBoolean("atlas.server.ha.enabled", false) || configMigrationEnabled) { - if (configMigrationEnabled) { - LOG.info("Atlas is in Migration Mode, enabling ActiveServerFilter"); - } else { - LOG.info("Atlas is in HA Mode, enabling ActiveServerFilter"); - } - - httpSecurity.addFilterAfter(activeServerFilter, BasicAuthenticationFilter.class); + configureCommonHttpSecurity(httpSecurity); + addWebUiFormLogin(httpSecurity); + addHaAndMigrationGuards(httpSecurity); + + Filter staleTransactionCleanupFilter = staleTransactionCleanupFilterProvider.getIfAvailable(); + if (staleTransactionCleanupFilter != null) { + httpSecurity.addFilterAfter(staleTransactionCleanupFilter, BasicAuthenticationFilter.class); } - httpSecurity.addFilterAfter(staleTransactionCleanupFilter, BasicAuthenticationFilter.class) - .addFilterBefore(ssoAuthenticationFilter, BasicAuthenticationFilter.class) - .addFilterAfter(atlasAuthenticationFilter, SecurityContextHolderAwareRequestFilter.class) - .addFilterAfter(csrfPreventionFilter, AtlasAuthenticationFilter.class); + addCommonAuthFilters(httpSecurity); if (keycloakEnabled) { httpSecurity.logout().addLogoutHandler(keycloakLogoutHandler()).and() diff --git a/webapp/src/main/java/org/apache/atlas/web/service/AtlasServiceStateProviderConfig.java b/webapp/src/main/java/org/apache/atlas/web/service/AtlasServiceStateProviderConfig.java new file mode 100644 index 00000000000..4aa96b01c27 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/service/AtlasServiceStateProviderConfig.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.service; + +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 AtlasServiceStateProviderConfig { + @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 04566a378c2..988831eadb8 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 b7f5ec4a396..b789160428d 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 e675a364577..a34c405e950 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 3020690dca0..a66e34b44c7 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"> @@ -90,7 +90,7 @@ + class="org.apache.atlas.server.common.security.AtlasAuthenticationProvider"> 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 76020d2da22..92e4efbb5a9 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 529d894ffaa..89bbe8a49c4 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 28693f767c8..eccd676345d 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 f424b348099..6e364e352b1 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 51d40a49d05..19268cdded9 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 8d2c747489d..ff9ebd17b8a 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 9057ae57986..1ee895844e6 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 c8571b4f6fe..cec8853837a 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 db3ccd6104b..0a91aac3eba 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 8e27e857111..353ffa4264f 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 f5eec9f4d86..579c99c57b2 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,7 +23,10 @@ import com.nimbusds.jwt.SignedJWT; import org.apache.atlas.ApplicationProperties; import org.apache.atlas.AtlasException; -import org.apache.atlas.web.security.AtlasAuthenticationProvider; +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.server.common.security.AtlasAuthenticationProvider; import org.apache.commons.configuration.Configuration; import org.mockito.Mock; import org.mockito.MockedStatic; @@ -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 582e3d94609..bb8a777afbb 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 d3927f698e9..167c6ca75c5 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 bb443cb627d..a95e4d20281 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 37a64628e64..9129a428613 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/ServiceStateChangeAuditHandlerTest.java b/webapp/src/test/java/org/apache/atlas/web/metrics/ServiceStateChangeAuditHandlerTest.java new file mode 100644 index 00000000000..38c3d3853f8 --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/web/metrics/ServiceStateChangeAuditHandlerTest.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 ServiceStateChangeAuditHandlerTest { + private static final String ATLAS_USER = "atlas"; + + @Mock + private AtlasAuditService auditService; + + private ServiceStateChangeAuditHandler handler; + + @BeforeMethod + public void setup() { + MockitoAnnotations.initMocks(this); + handler = new ServiceStateChangeAuditHandler(auditService); + } + + @Test + public void testOnServerStartInvokesAuditWithServerStartOperation() throws AtlasBaseException { + handler.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 { + handler.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()); + + handler.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 2f6b4a87d8b..29088573417 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 ef1c7fce1dc..755df2ab332 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 1f2c5ca40e3..1851da89a92 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 bc66d2cbb88..7d93eff3141 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 257a4b5ec58..8383e493c23 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; @@ -593,7 +593,7 @@ public void testDeleteAtlasTypeDefs_WithException() throws AtlasBaseException { public void testDeleteAtlasTypeByName_Success() throws AtlasBaseException { // Setup String typeName = "test_type"; - doNothing().when(typeDefStore).deleteTypeByName(typeName); + doNothing().when(typeDefStore).deleteTypeByName(typeName, false); try (MockedStatic mockedPerfTracer = mockStatic(AtlasPerfTracer.class)) { mockedPerfTracer.when(() -> AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false); @@ -602,7 +602,7 @@ public void testDeleteAtlasTypeByName_Success() throws AtlasBaseException { typesREST.deleteAtlasTypeByName(typeName); // Verify - verify(typeDefStore).deleteTypeByName(typeName); + verify(typeDefStore).deleteTypeByName(typeName, false); } } @@ -610,7 +610,7 @@ public void testDeleteAtlasTypeByName_Success() throws AtlasBaseException { public void testDeleteAtlasTypeByName_WithPerfTracerEnabled() throws AtlasBaseException { // Setup String typeName = "test_type_perf"; - doNothing().when(typeDefStore).deleteTypeByName(typeName); + doNothing().when(typeDefStore).deleteTypeByName(typeName, false); try (MockedStatic mockedPerfTracer = mockStatic(AtlasPerfTracer.class)) { mockedPerfTracer.when(() -> AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(true); @@ -620,7 +620,7 @@ public void testDeleteAtlasTypeByName_WithPerfTracerEnabled() throws AtlasBaseEx typesREST.deleteAtlasTypeByName(typeName); // Verify - verify(typeDefStore).deleteTypeByName(typeName); + verify(typeDefStore).deleteTypeByName(typeName, false); mockedPerfTracer.verify(() -> AtlasPerfTracer.isPerfTraceEnabled(any())); mockedPerfTracer.verify(() -> AtlasPerfTracer.getPerfTracer(any(), anyString())); } @@ -631,7 +631,7 @@ public void testDeleteAtlasTypeByName_WithException() throws AtlasBaseException // Setup String typeName = "test_type_error"; AtlasBaseException exception = new AtlasBaseException("Delete by name failed"); - doThrow(exception).when(typeDefStore).deleteTypeByName(typeName); + doThrow(exception).when(typeDefStore).deleteTypeByName(typeName, false); try (MockedStatic mockedPerfTracer = mockStatic(AtlasPerfTracer.class)) { mockedPerfTracer.when(() -> AtlasPerfTracer.isPerfTraceEnabled(any())).thenReturn(false); @@ -642,7 +642,7 @@ public void testDeleteAtlasTypeByName_WithException() throws AtlasBaseException }); assertEquals(thrownException.getMessage(), "Delete by name failed"); - verify(typeDefStore).deleteTypeByName(typeName); + verify(typeDefStore).deleteTypeByName(typeName, false); } } 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 4cc36d5dd2e..1cd6c20487d 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 1a9527551f4..d2b2f7efaaa 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 b351e0f4402..4dc6a855cac 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 d06b34e06cd..b786c36db47 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,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.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasAuthenticationProvider; +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; @@ -63,7 +69,7 @@ public class AtlasAuthenticationProviderTest { private AtlasPamAuthenticationProvider mockPamAuthenticationProvider; @Mock - private AtlasKeycloakAuthenticationProvider mockKeycloakAuthenticationProvider; + private org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider mockKeycloakAuthenticationProvider; @Mock private Authentication mockAuthentication; @@ -578,7 +584,7 @@ public void testConstructorAnnotation() throws Exception { AtlasFileAuthenticationProvider.class, AtlasADAuthenticationProvider.class, AtlasPamAuthenticationProvider.class, - AtlasKeycloakAuthenticationProvider.class + org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider.class ).getAnnotation(Inject.class); assertNotNull(injectAnnotation); } 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 b6dfabe11f8..807ded4843e 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 97fceba1f57..5cc6cf3ceff 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; @@ -80,7 +81,7 @@ public class AtlasKeycloakAuthenticationProviderTest { @Mock private AccessToken mockAccessToken; - private AtlasKeycloakAuthenticationProvider keycloakAuthenticationProvider; + private org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider keycloakAuthenticationProvider; @BeforeMethod public void setUp() { @@ -96,7 +97,7 @@ public void testConstructor_WithDefaultConfiguration() throws Exception { when(mockConfiguration.getString("atlas.authentication.method.keycloak.groups_claim")).thenReturn(null); // Execute - keycloakAuthenticationProvider = new AtlasKeycloakAuthenticationProvider(); + keycloakAuthenticationProvider = new org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider(); // Verify using reflection assertTrue((Boolean) getPrivateField(keycloakAuthenticationProvider, "groupsFromUGI")); @@ -114,7 +115,7 @@ public void testConstructor_WithCustomConfiguration() throws Exception { when(mockConfiguration.getString("atlas.authentication.method.keycloak.groups_claim")).thenReturn("customGroups"); // Execute - keycloakAuthenticationProvider = new AtlasKeycloakAuthenticationProvider(); + keycloakAuthenticationProvider = new org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider(); // Verify using reflection assertFalse((Boolean) getPrivateField(keycloakAuthenticationProvider, "groupsFromUGI")); @@ -132,7 +133,7 @@ public void testConstructor_WithException() throws Exception { // Execute & Verify try { - keycloakAuthenticationProvider = new AtlasKeycloakAuthenticationProvider(); + keycloakAuthenticationProvider = new org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider(); fail("Expected Exception to be thrown"); } catch (Exception e) { assertTrue(e.getMessage().contains("Configuration loading failed") || e instanceof RuntimeException); @@ -429,7 +430,7 @@ public void testSupports_WithNullClass() throws Exception { @Test public void testClassAnnotations() { // Verify @Component annotation - Component componentAnnotation = AtlasKeycloakAuthenticationProvider.class.getAnnotation(Component.class); + Component componentAnnotation = org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider.class.getAnnotation(Component.class); assertNotNull(componentAnnotation); } @@ -494,7 +495,7 @@ public void testConstructor_ConfigurationProperties() throws Exception { when(mockConfiguration.getString("atlas.authentication.method.keycloak.groups_claim")).thenReturn("custom_claim"); // Execute - keycloakAuthenticationProvider = new AtlasKeycloakAuthenticationProvider(); + keycloakAuthenticationProvider = new org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider(); // Verify configuration loading verify(mockConfiguration).getBoolean("atlas.authentication.method.keycloak.ugi-groups", true); @@ -513,7 +514,7 @@ private void setupProviderWithConfig(boolean groupsFromUGI, String groupsClaim) when(mockConfiguration.getBoolean("atlas.authentication.method.keycloak.ugi-groups", true)).thenReturn(groupsFromUGI); when(mockConfiguration.getString("atlas.authentication.method.keycloak.groups_claim")).thenReturn(groupsClaim); - keycloakAuthenticationProvider = new AtlasKeycloakAuthenticationProvider(); + keycloakAuthenticationProvider = new org.apache.atlas.server.common.security.AtlasKeycloakAuthenticationProvider(); } } 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 18e962ba048..08e2f50458c 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 c63d61e89a9..c5c64c4e2f4 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 46c5e2111c3..cc0e408a3cf 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,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.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.AtlasAuthenticationProvider; +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; @@ -44,6 +47,7 @@ import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.core.io.FileSystemResource; import org.springframework.core.io.Resource; import org.springframework.security.authentication.AuthenticationManager; @@ -153,23 +157,34 @@ public void setUp() { MockitoAnnotations.openMocks(this); } - @Test - public void testConstructor_WithKeycloakDisabled() throws Exception { - // Setup - when(mockConfiguration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false)).thenReturn(false); + @SuppressWarnings("unchecked") + private static ObjectProvider objectProvider(T value) { + ObjectProvider p = mock(ObjectProvider.class); + when(p.getIfAvailable()).thenReturn(value); + return p; + } - // Execute - atlasSecurityConfig = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, + private AtlasSecurityConfig newTestAtlasSecurityConfig() { + return new AtlasSecurityConfig( + objectProvider(mockSsoAuthenticationFilter), + objectProvider(mockCsrfPreventionFilter), + objectProvider(mockAtlasAuthenticationFilter), mockAuthenticationProvider, mockSuccessHandler, mockFailureHandler, mockAtlasAuthenticationEntryPoint, mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + objectProvider(mockActiveServerFilter), + objectProvider(mockStaleTransactionCleanupFilter)); + } + + @Test + public void testConstructor_WithKeycloakDisabled() throws Exception { + // Setup + when(mockConfiguration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false)).thenReturn(false); + + // Execute + atlasSecurityConfig = newTestAtlasSecurityConfig(); // Verify using reflection assertFalse((Boolean) getPrivateField(atlasSecurityConfig, "keycloakEnabled")); @@ -185,17 +200,7 @@ public void testConstructor_WithKeycloakEnabled() throws Exception { when(mockConfiguration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false)).thenReturn(true); // Execute - atlasSecurityConfig = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, - mockAuthenticationProvider, - mockSuccessHandler, - mockFailureHandler, - mockAtlasAuthenticationEntryPoint, - mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + atlasSecurityConfig = newTestAtlasSecurityConfig(); // Verify using reflection assertTrue((Boolean) getPrivateField(atlasSecurityConfig, "keycloakEnabled")); @@ -344,18 +349,8 @@ private void testHttpSecurityConfiguration(boolean migrationEnabled, boolean haE setupKeycloakConfiguration(); } - // Create AtlasSecurityConfig instance - AtlasSecurityConfig configInstance = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, - mockAuthenticationProvider, - mockSuccessHandler, - mockFailureHandler, - mockAtlasAuthenticationEntryPoint, - mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + // Execute + AtlasSecurityConfig configInstance = newTestAtlasSecurityConfig(); // Set up comprehensive HttpSecurity mocking first setupHttpSecurityMocks(); @@ -429,17 +424,7 @@ private void testDetailedHttpSecurityConfiguration(boolean migrationEnabled, boo } // Create fresh AtlasSecurityConfig instance - AtlasSecurityConfig configInstance = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, - mockAuthenticationProvider, - mockSuccessHandler, - mockFailureHandler, - mockAtlasAuthenticationEntryPoint, - mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + AtlasSecurityConfig configInstance = newTestAtlasSecurityConfig(); // Create fresh HttpSecurity mock for each test to avoid state pollution HttpSecurity freshHttpSecurity = mock(HttpSecurity.class); @@ -580,17 +565,7 @@ public void testAdapterDeploymentContext_WithConfigFile() throws Exception { when(mockConfiguration.getBoolean(AtlasAuthenticationProvider.KEYCLOAK_AUTH_METHOD, false)).thenReturn(true); setupKeycloakConfiguration("/path/to/keycloak.json"); - atlasSecurityConfig = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, - mockAuthenticationProvider, - mockSuccessHandler, - mockFailureHandler, - mockAtlasAuthenticationEntryPoint, - mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + atlasSecurityConfig = newTestAtlasSecurityConfig(); setPrivateField(atlasSecurityConfig, "keycloakConfigFileResource", mockKeycloakConfigFileResource); @@ -810,17 +785,17 @@ public void testStaticFields() throws Exception { @Test public void testInjectAnnotation() throws Exception { // Verify @Inject annotation on constructor - Inject injectAnnotation = AtlasSecurityConfig.class.getConstructor( - AtlasKnoxSSOAuthenticationFilter.class, - AtlasCSRFPreventionFilter.class, - AtlasAuthenticationFilter.class, + Inject injectAnnotation = AtlasSecurityConfig.class.getDeclaredConstructor( + ObjectProvider.class, + ObjectProvider.class, + ObjectProvider.class, AtlasAuthenticationProvider.class, AtlasAuthenticationSuccessHandler.class, AtlasAuthenticationFailureHandler.class, AtlasAuthenticationEntryPoint.class, Configuration.class, - StaleTransactionCleanupFilter.class, - ActiveServerFilter.class + ObjectProvider.class, + ObjectProvider.class ).getAnnotation(Inject.class); assertNotNull(injectAnnotation); } @@ -835,17 +810,7 @@ private void setupAtlasSecurityConfig(boolean keycloakEnabled) { setupKeycloakConfiguration(); } - atlasSecurityConfig = new AtlasSecurityConfig( - mockSsoAuthenticationFilter, - mockCsrfPreventionFilter, - mockAtlasAuthenticationFilter, - mockAuthenticationProvider, - mockSuccessHandler, - mockFailureHandler, - mockAtlasAuthenticationEntryPoint, - mockConfiguration, - mockStaleTransactionCleanupFilter, - mockActiveServerFilter); + atlasSecurityConfig = newTestAtlasSecurityConfig(); // Set the keycloakConfigFileResource using reflection setPrivateField(atlasSecurityConfig, "keycloakConfigFileResource", mockKeycloakConfigFileResource); @@ -941,9 +906,18 @@ private void setupKeycloakMocks() throws Exception { private void setPrivateField(Object target, String fieldName, Object value) { try { - Field field = target.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - field.set(target, value); + Class c = target.getClass(); + while (c != null) { + try { + Field field = c.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(target, value); + return; + } catch (NoSuchFieldException e) { + c = c.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); } catch (Exception e) { throw new RuntimeException("Failed to set private field: " + fieldName, e); } @@ -951,9 +925,17 @@ private void setPrivateField(Object target, String fieldName, Object value) { private Object getPrivateField(Object target, String fieldName) { try { - Field field = target.getClass().getDeclaredField(fieldName); - field.setAccessible(true); - return field.get(target); + Class c = target.getClass(); + while (c != null) { + try { + Field field = c.getDeclaredField(fieldName); + field.setAccessible(true); + return field.get(target); + } catch (NoSuchFieldException e) { + c = c.getSuperclass(); + } + } + throw new NoSuchFieldException(fieldName); } catch (Exception e) { throw new RuntimeException("Failed to get private field: " + fieldName, e); } 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 dc7503e0527..07d26bcf254 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 b753bebedd3..fa9d4b4ee9c 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,8 @@ package org.apache.atlas.web.security; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.server.common.security.AtlasAuthenticationException; +import org.apache.atlas.server.common.security.AtlasAuthenticationProvider; import org.apache.atlas.web.TestUtils; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.io.FileUtils; @@ -27,7 +29,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,8 +78,14 @@ public void setup() throws Exception { System.setProperty("atlas.conf", persistDir); - applicationContext = new ClassPathXmlApplicationContext("test-spring-security.xml"); - authProvider = applicationContext.getBean(org.apache.atlas.web.security.AtlasAuthenticationProvider.class); + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + + ((ConfigurableEnvironment) ctx.getEnvironment()).setActiveProfiles("testSpringSecurityBridge"); + ctx.register(TestSpringSecurityBridgeConfig.class); + ctx.refresh(); + + applicationContext = ctx; + authProvider = applicationContext.getBean(AtlasAuthenticationProvider.class); } @Test 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 9a9c43fe59c..99a0b3845d9 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 12ce66f036b..890d9dc8fcd 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 5ac970a345d..ac2245367f3 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 00000000000..5b560e75193 --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/web/security/TestSpringSecurityBridgeConfig.java @@ -0,0 +1,63 @@ +/* + * 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.apache.atlas.server.common.security.AtlasAuthenticationProvider; +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 79c71a754a1..d3a8bd8a661 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 71b8606a429..bd4224e9802 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 9efbc63f2cc..49332e077de 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.HighAvailability; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; +import org.apache.atlas.server.common.service.ServiceState; +import org.apache.atlas.server.common.service.ServiceStateChangeHandler; 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 HighAvailability highAvailability; + + @Mock + private HighAvailabilityProperties haProperties; + + @Mock + private ServiceStateChangeHandler serviceStateChangeHandler; @BeforeMethod - public void setup() { - System.setProperty(AtlasConstants.SYSTEM_PROPERTY_APP_PORT, "21000"); + public void setup() throws Exception { MockitoAnnotations.initMocks(this); + when(highAvailability.isHAEnabled(any(Configuration.class))).thenReturn(true); + when(highAvailability.selectServerId(any(Configuration.class))).thenReturn("id1"); + when(highAvailability.getZookeeperProperties(any(Configuration.class))).thenReturn(haProperties); + when(haProperties.getZkRoot()).thenReturn(DEFAULT_ZK_ROOT); + } + + private ActiveInstanceElectorService newElector(Set handlers) { + return new ActiveInstanceElectorService( + configuration, + handlers, + Collections.singleton(serviceStateChangeHandler), + curatorFactory, + activeInstanceState, + serviceState, + highAvailability); } @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(highAvailability); + when(highAvailability.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(serviceStateChangeHandler).onServerStart(); + verify(serviceStateChangeHandler).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(highAvailability); + when(highAvailability.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 68f7ee75598..75483aff060 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,13 @@ package org.apache.atlas.web.service; +import org.apache.atlas.AtlasException; +import org.apache.atlas.ha.AtlasServerIdSelector; 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.HighAvailability; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; import org.apache.commons.configuration.Configuration; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.api.CreateBuilder; @@ -48,6 +54,38 @@ 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 HighAvailability HA_FOR_TESTS = new HighAvailability() { + @Override + public boolean isHAEnabled(Configuration configuration) { + return true; + } + + @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()); + } + }; + @Mock private Configuration configuration; @@ -84,7 +122,7 @@ public void testSharedPathIsCreatedIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_FOR_TESTS); activeInstanceState.update("id1"); @@ -118,7 +156,7 @@ public void testSharedPathIsCreatedWithRightACLIfNotExists() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_FOR_TESTS); activeInstanceState.update("id1"); @@ -141,7 +179,7 @@ public void testDataIsUpdatedWithAtlasServerAddress() throws Exception { when(curatorFramework.setData()).thenReturn(setDataBuilder); - ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory); + ActiveInstanceState activeInstanceState = new ActiveInstanceState(configuration, curatorFactory, HA_FOR_TESTS); activeInstanceState.update("id1"); @@ -158,7 +196,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_FOR_TESTS); String actualServerAddress = activeInstanceState.getActiveServerAddress(); assertEquals(actualServerAddress, SERVER_ADDRESS); @@ -174,7 +212,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_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 d225a7e2cef..7f3e43ae54b 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 58aef3e0c3b..eb640044953 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.HighAvailability; +import org.apache.atlas.server.common.service.HighAvailabilityProperties; 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 HighAvailability highAvailability; @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(highAvailability.isHAEnabled(configuration)).thenReturn(true); - CuratorFactory curatorFactory = new CuratorFactory(configuration) { + return new CuratorFactory(configuration, highAvailability) { @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 6e55dbca629..5de6e20976b 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 57ab4cbec1c..a63b70c089d 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 5c209fa3f50..b14fb3df59b 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 fa532a5c37d..a65e4af457d 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.HighAvailability; +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 HighAvailability highAvailability; + @BeforeMethod public void setup() { MockitoAnnotations.initMocks(this); } + private ServiceState newServiceState(boolean haEnabled) { + when(highAvailability.isHAEnabled(configuration)).thenReturn(haEnabled); + return new ServiceState(configuration, highAvailability); + } + @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 79e62148f96..230125e3607 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 cea6bea7bf3..8c5231462f0 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 ba8f0255091..2356c629ca6 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 4fa04b60a53..6563902061d 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 c3635eca1c3..4e438e72f0a 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 f77c20bd68a..1559c2e072a 100644 --- a/webapp/src/test/resources/test-spring-security.xml +++ b/webapp/src/test/resources/test-spring-security.xml @@ -52,18 +52,18 @@ - - + + - - + + - - - - + + + + @@ -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 fd1d492fcdf..e149a285ef8 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