From commits-return-12893-archive-asf-public=cust-asf.ponee.io@sentry.apache.org Tue Dec 4 23:03:48 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 02E971807A3 for ; Tue, 4 Dec 2018 23:03:46 +0100 (CET) Received: (qmail 96561 invoked by uid 500); 4 Dec 2018 22:03:46 -0000 Mailing-List: contact commits-help@sentry.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sentry.apache.org Delivered-To: mailing list commits@sentry.apache.org Received: (qmail 96354 invoked by uid 99); 4 Dec 2018 22:03:46 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 04 Dec 2018 22:03:46 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id A1B73E13AB; Tue, 4 Dec 2018 22:03:45 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: spena@apache.org To: commits@sentry.apache.org Date: Tue, 04 Dec 2018 22:03:52 -0000 Message-Id: In-Reply-To: References: X-Mailer: ASF-Git Admin Mailer Subject: [08/11] sentry git commit: SENTRY-2458: Separate Web UI and service from service-server to prevent circular dependencies (Brian Towels, reviewed by Na Li, Stephen Moist, kalyan kumar kalvagadda) http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-server/src/main/webapp/css/sentry.css ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/webapp/css/sentry.css b/sentry-service/sentry-service-server/src/main/webapp/css/sentry.css deleted file mode 100644 index 69cba19..0000000 --- a/sentry-service/sentry-service-server/src/main/webapp/css/sentry.css +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -html { - position: relative; - min-height: 100%; -} - -body { - /* Margin bottom by footer height */ - margin-bottom: 60px; - padding-top: 80px; -} - -.navbar-collapse {margin-top:10px} - -.footer { - position: absolute; - bottom: 0; - width: 100%; - /* Set the fixed height of the footer here */ - height: 60px; - background-color: #f5f5f5; -} - -.container .text-muted { - margin: 20px 0; -} - -.footer > .container { - padding-right: 15px; - padding-left: 15px; -} - -code { - font-size: 80%; -} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-server/src/main/webapp/sentry.png ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/main/webapp/sentry.png b/sentry-service/sentry-service-server/src/main/webapp/sentry.png deleted file mode 100644 index 67edd90..0000000 Binary files a/sentry-service/sentry-service-server/src/main/webapp/sentry.png and /dev/null differ http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryWebServerWithoutSecurity.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryWebServerWithoutSecurity.java b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryWebServerWithoutSecurity.java index 6e741e8..29adced 100644 --- a/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryWebServerWithoutSecurity.java +++ b/sentry-service/sentry-service-server/src/test/java/org/apache/sentry/api/service/thrift/TestSentryWebServerWithoutSecurity.java @@ -22,6 +22,7 @@ import java.net.URL; import org.apache.commons.io.IOUtils; import org.apache.sentry.service.thrift.SentryServiceIntegrationBase; +import org.apache.sentry.service.web.ConfServlet; import org.junit.After; import org.junit.Assert; import org.junit.Before; http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/pom.xml b/sentry-service/sentry-service-web/pom.xml new file mode 100644 index 0000000..53c6e3f --- /dev/null +++ b/sentry-service/sentry-service-web/pom.xml @@ -0,0 +1,118 @@ + + + + + + sentry-service + org.apache.sentry + 2.2.0-SNAPSHOT + + 4.0.0 + + org.apache.sentry + sentry-service-web + + + ${basedir}/src/main/java + ${basedir}/src/test/java + + + ${basedir}/src/main + + webapp/** + + true + + + ${basedir}/src/main/resources + + + + + org.apache.maven.plugins + maven-resources-plugin + + + eot + png + svg + ttf + woff + woff2 + + + + + + + + + org.projectlombok + lombok + provided + + + org.apache.sentry + sentry-core-common + ${project.version} + + + org.apache.sentry + sentry-service-providers + ${project.version} + + + org.apache.sentry + sentry-spi + ${project.version} + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + org.apache.hadoop + hadoop-common + + + org.mockito + mockito-all + test + + + org.apache.hadoop + hadoop-minikdc + test + + + junit + junit + test + + + http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/ConfServlet.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/ConfServlet.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/ConfServlet.java new file mode 100644 index 0000000..b0fda0e --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/ConfServlet.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.sentry.service.web; + +import java.io.IOException; +import java.io.Writer; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.conf.Configuration; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +/** + * Servlet to print out all sentry configuration. + */ +public class ConfServlet extends HttpServlet { + public static final String CONF_CONTEXT_ATTRIBUTE = "sentry.conf"; + public static final String FORMAT_JSON = "json"; + public static final String FORMAT_XML = "xml"; + public static final String FORMAT_PARAM = "format"; + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String format = request.getParameter(FORMAT_PARAM); + if (format == null) { + format = FORMAT_XML; + } + + if (FORMAT_XML.equals(format)) { + response.setContentType("text/xml; charset=utf-8"); + } else if (FORMAT_JSON.equals(format)) { + response.setContentType("application/json; charset=utf-8"); + } + + Configuration conf = (Configuration)getServletContext().getAttribute( + CONF_CONTEXT_ATTRIBUTE); + assert conf != null; + + Writer out = response.getWriter(); + if (FORMAT_JSON.equals(format)) { + Configuration.dumpConfiguration(conf, out); + } else if (FORMAT_XML.equals(format)) { + conf.writeXml(out); + } else { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Bad format: " + escapeHtml(format)); + } + out.close(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/DefaultWebServicesProvider.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/DefaultWebServicesProvider.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/DefaultWebServicesProvider.java new file mode 100644 index 0000000..9aafa29 --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/DefaultWebServicesProvider.java @@ -0,0 +1,168 @@ +/* + * 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.sentry.service.web; + +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.DispatcherType; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.SecurityUtil; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; +import org.apache.sentry.server.provider.webservice.AttributeDesc; +import org.apache.sentry.server.provider.webservice.FilterDesc; +import org.apache.sentry.server.provider.webservice.WebServiceProvider; +import org.apache.sentry.server.provider.webservice.WebServiceProviderFactory; +import org.apache.sentry.server.provider.webservice.ServletDesc; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.FilterHolder; +import org.eclipse.jetty.servlet.ServletHolder; + +/** + * Provides the Web Interface functions for default Sentry services. + */ +@Slf4j +public class DefaultWebServicesProvider implements WebServiceProvider, + WebServiceProviderFactory { + + public static final String ID = "default"; + private static final String STATIC_RESOURCE_DIR = "/webapp"; + + private Configuration config; + + @Override + public List getServlets() { + List servlets = new ArrayList<>(); + servlets.add(ServletDesc.of("/conf", new ServletHolder(ConfServlet.class))); + servlets.add(ServletDesc.of("/admin/logLevel", new ServletHolder(LogLevelServlet.class))); + if (config.getBoolean(ServerConfig.SENTRY_WEB_PUBSUB_SERVLET_ENABLED, + ServerConfig.SENTRY_WEB_PUBSUB_SERVLET_ENABLED_DEFAULT)) { + servlets.add(ServletDesc.of("/admin/publishMessage", new ServletHolder(PubSubServlet.class))); + } + if (config.getBoolean(ServerConfig.SENTRY_WEB_ADMIN_SERVLET_ENABLED, + ServerConfig.SENTRY_WEB_ADMIN_SERVLET_ENABLED_DEFAULT)) { + // Static files holder + ServletHolder staticHolder = new ServletHolder(new DefaultServlet()); + staticHolder.setInitParameter("pathInfoOnly", "true"); + URL url = this.getClass().getResource(STATIC_RESOURCE_DIR); + staticHolder.setInitParameter("resourceBase", url.toString()); + servlets.add(ServletDesc.of("/*", staticHolder)); + } + + return servlets; + } + + @Override + public List getAttributes() { + return Arrays.asList(AttributeDesc.of(ConfServlet.CONF_CONTEXT_ATTRIBUTE, config)); + } + + @Override + public List getFilters() { + List filters = new ArrayList<>(); + String authMethod = config.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE); + if (!ServerConfig.SENTRY_WEB_SECURITY_TYPE_NONE.equalsIgnoreCase(authMethod)) { + /** + * SentryAuthFilter is a subclass of AuthenticationFilter and + * AuthenticationFilter tagged as private and unstable interface: + * While there are not guarantees that this interface will not change, + * it is fairly stable and used by other projects (ie - Oozie) + */ + FilterHolder sentryAuthFilterHolder = new FilterHolder(SentryAuthFilter.class); + sentryAuthFilterHolder.setInitParameters(loadWebAuthenticationConf(config)); + filters.add(FilterDesc.of("/*", sentryAuthFilterHolder, EnumSet.of(DispatcherType.REQUEST))); + } + return filters; + } + + private static Map loadWebAuthenticationConf(Configuration conf) { + Map prop = new HashMap(); + prop.put(AuthenticationFilter.CONFIG_PREFIX, ServerConfig.SENTRY_WEB_SECURITY_PREFIX); + String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS); + if (allowUsers == null || allowUsers.equals("")) { + allowUsers = conf.get(ServerConfig.ALLOW_CONNECT); + conf.set(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS, allowUsers); + } + validateConf(conf); + for (Map.Entry entry : conf) { + String name = entry.getKey(); + if (name.startsWith(ServerConfig.SENTRY_WEB_SECURITY_PREFIX)) { + String value = conf.get(name); + prop.put(name, value); + } + } + return prop; + } + + private static void validateConf(Configuration conf) { + String authHandlerName = conf.get(ServerConfig.SENTRY_WEB_SECURITY_TYPE); + Preconditions.checkNotNull(authHandlerName, "Web authHandler should not be null."); + String allowUsers = conf.get(ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS); + Preconditions.checkNotNull(allowUsers, "Allow connect user(s) should not be null."); + if (ServerConfig.SENTRY_WEB_SECURITY_TYPE_KERBEROS.equalsIgnoreCase(authHandlerName)) { + String principal = conf.get(ServerConfig.SENTRY_WEB_SECURITY_PRINCIPAL); + Preconditions.checkNotNull(principal, "Kerberos principal should not be null."); + Preconditions.checkArgument(principal.length() != 0, "Kerberos principal is not right."); + String keytabFile = conf.get(ServerConfig.SENTRY_WEB_SECURITY_KEYTAB); + Preconditions.checkNotNull(keytabFile, "Keytab File should not be null."); + Preconditions.checkArgument(keytabFile.length() != 0, "Keytab File is not right."); + try { + UserGroupInformation.setConfiguration(conf); + String hostPrincipal = SecurityUtil + .getServerPrincipal(principal, ServerConfig.RPC_ADDRESS_DEFAULT); + UserGroupInformation.loginUserFromKeytab(hostPrincipal, keytabFile); + } catch (IOException ex) { + throw new IllegalArgumentException("Can't use Kerberos authentication, principal [" + + principal + "] keytab [" + keytabFile + "]", ex); + } + LOGGER + .info("Using Kerberos authentication, principal [{}] keytab [{}]", principal, keytabFile); + } + } + + @Override + public void init(Configuration config) { + this.config = config; + } + + @Override + public WebServiceProvider create() { + return this; + } + + @Override + public String getId() { + return ID; + } + + @Override + public void close() { + + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/LogLevelServlet.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/LogLevelServlet.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/LogLevelServlet.java new file mode 100644 index 0000000..a496779 --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/LogLevelServlet.java @@ -0,0 +1,123 @@ +/* + * 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.sentry.service.web; + +import org.apache.log4j.Level; +import org.apache.log4j.LogManager; +import org.apache.log4j.Logger; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +public class LogLevelServlet extends HttpServlet { + private static final String LF = "\n"; + private static final String BR = "
"; + private static final String B_BR = "%s
"; + private static final String FORMS_HEAD = + "

" + "Log Level" + "

" + + LF + BR + "

Results

" + + LF + " Submitted Log Name: " + B_BR; + private static final String FORMS_CONTENT_GET = + LF + " Effective level: " + B_BR; + private static final String FORMS_CONTENT_SET = + LF + " Submitted Level: " + B_BR + + LF + " Setting Level to %s" + BR + + LF + " Effective level: " + B_BR; + private static final String FORMS_END = + LF + BR + "

Get / Set

" + + LF + "
Log: " + + "" + "
" + + LF + "
Log: " + + "Level: " + + "" + "
"; + private static final String FORMS_GET = FORMS_HEAD + FORMS_CONTENT_GET; + private static final String FORMS_SET = FORMS_HEAD + FORMS_CONTENT_SET; + + /** + * Return parameter on servlet request for the given name + * + * @param request: Servlet request + * @param name: Name of parameter in servlet request + * @return Parameter in servlet request for the given name, return null if can't find parameter. + */ + private String getParameter(ServletRequest request, String name) { + String s = request.getParameter(name); + if (s == null) { + return null; + } + s = s.trim(); + return s.length() == 0 ? null : s; + } + + /** + * Check the validity of the log level. + * @param level: The log level to be checked + * @return + * true: The log level is valid + * false: The log level is invalid + */ + private boolean isLogLevelValid(String level) { + return level.equals(Level.toLevel(level).toString()); + } + + /** + * Parse the class name and log level in the http servlet request. + * If the request contains only class name, return the log level in the response message. + * If the request contains both class name and level, set the log level to the requested level + * and return the setting result in the response message. + */ + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String logName = getParameter(request, "log"); + String level = getParameter(request, "level"); + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + PrintWriter out = response.getWriter(); + + if (logName != null) { + Logger logInstance = LogManager.getLogger(logName); + if (level == null) { + out.write(String.format(FORMS_GET, + escapeHtml(logName), + logInstance.getEffectiveLevel().toString())); + } else if (isLogLevelValid(level)) { + logInstance.setLevel(Level.toLevel(level)); + out.write(String.format(FORMS_SET, + escapeHtml(logName), + escapeHtml(level), + escapeHtml(level), + logInstance.getEffectiveLevel().toString())); + } else { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Invalid log level: " + escapeHtml(level)); + return; + } + } + out.write(FORMS_END); + out.close(); + response.flushBuffer(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/PubSubServlet.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/PubSubServlet.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/PubSubServlet.java new file mode 100644 index 0000000..c7e281b --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/PubSubServlet.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.sentry.service.web; + +import org.apache.sentry.core.common.utils.PubSub; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; + +/** + * This servlet facilitates sending {topic, message } tuples to Servlet components + * subscribed to specific topics. + *

+ * It uses publish-subscribe mechanism implemented by PubSub class. + * The form generated by this servlet consists of the following elements: + *

+ * a) Topic: pull-down menu of existing topics, i.e. the topics registered with + * PubSub by calling PubSub.subscribe() API. This prevents entering invalid topic. + *

+ * b) Message: text field for entering a message + *

+ * c) Submit: button to submit (topic, message) tuple + *

+ * d) Status: text area printing status of the request or help information. + */ +public class PubSubServlet extends HttpServlet { + + private static final Logger LOGGER = LoggerFactory.getLogger(PubSubServlet.class); + + private static final String FORM_GET = + "" + + "" + + "" + + "

" + + "

Topic:

" + + "" + + "

Message:

" + + "" + + "

" + + "" + + "
" + + "

Status:

" + + "" + + "" + + ""; + + /** + * Return parameter on servlet request for the given name + * + * @param request: Servlet request + * @param name: Name of parameter in servlet request + * @return Parameter in servlet request for the given name, return null if can't find parameter. + */ + private static String getParameter(ServletRequest request, String name) { + String s = request.getParameter(name); + if (s == null) { + return null; + } + s = s.trim(); + return s.isEmpty() ? null : s; + } + + /** + * Parse the topic and message values and submit them via PubSub.submit() API. + * Reject request for unknown topic, i.e. topic no one is subscribed to. + */ + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) + throws ServletException, IOException { + String topic = getParameter(request, "topic"); + String message = getParameter(request, "message"); + response.setContentType("text/html;charset=utf-8"); + response.setStatus(HttpServletResponse.SC_OK); + PrintWriter out = response.getWriter(); + + String msg = "Topic is required, Message is optional.\nValid topics: " + PubSub.getInstance().getTopics(); + if (topic != null) { + LOGGER.info("Submitting topic " + topic + ", message " + message); + try { + PubSub.getInstance().publish(PubSub.Topic.fromString(topic), message); + msg = "Submitted topic " + topic + ", message " + message; + } catch (Exception e) { + msg = "Failed to submit topic " + topic + ", message " + message + " - " + e.getMessage(); + LOGGER.error(msg); + response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg); + return; + } + } + + StringBuilder topics = new StringBuilder(); + for (PubSub.Topic t : PubSub.getInstance().getTopics()) { + topics.append(""); + } + + String output = String.format(FORM_GET, topics.toString(), escapeHtml(msg)); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("HTML Page: " + output); + } + out.write(output); + out.close(); + response.flushBuffer(); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryAuthFilter.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryAuthFilter.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryAuthFilter.java new file mode 100644 index 0000000..a6d75ad --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryAuthFilter.java @@ -0,0 +1,90 @@ +/* + * 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.sentry.service.web; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Properties; +import java.util.Set; + +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; +import org.apache.hadoop.util.StringUtils; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.Sets; + +/** + * SentryAuthFilter is a subclass of AuthenticationFilter, + * add authorization: Only allowed users could connect the web server. + */ +public class SentryAuthFilter extends AuthenticationFilter { + + private static final Logger LOG = LoggerFactory.getLogger(SentryAuthFilter.class); + + public static final String ALLOW_WEB_CONNECT_USERS = ServerConfig.SENTRY_WEB_SECURITY_ALLOW_CONNECT_USERS; + + private Set allowUsers; + + @Override + protected void doFilter(FilterChain filterChain, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + String userName = request.getRemoteUser(); + LOG.debug("Authenticating user: " + userName + " from request."); + if (!allowUsers.contains(userName)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, + "Unauthorized user status code: " + HttpServletResponse.SC_FORBIDDEN); + throw new ServletException(userName + " is unauthorized. status code: " + HttpServletResponse.SC_FORBIDDEN); + } + super.doFilter(filterChain, request, response); + } + + /** + * Override getConfiguration to get ALLOW_WEB_CONNECT_USERS. + */ + @Override + protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + Properties props = new Properties(); + Enumeration names = filterConfig.getInitParameterNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + if (name.startsWith(configPrefix)) { + String value = filterConfig.getInitParameter(name); + if (ALLOW_WEB_CONNECT_USERS.equals(name)) { + allowUsers = parseConnectUsersFromConf(value); + } else { + props.put(name.substring(configPrefix.length()), value); + } + } + } + return props; + } + + private static Set parseConnectUsersFromConf(String value) { + //Removed the logic to convert the allowed users to lower case, as user names need to be case sensitive + return Sets.newHashSet(StringUtils.getStrings(value)); + } +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryWebServer.java ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryWebServer.java b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryWebServer.java new file mode 100644 index 0000000..61af4e1 --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/java/org/apache/sentry/service/web/SentryWebServer.java @@ -0,0 +1,180 @@ +/* + * 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.sentry.service.web; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +import java.util.EventListener; +import java.util.List; +import java.util.Set; +import lombok.extern.slf4j.Slf4j; +import org.apache.hadoop.conf.Configuration; +import org.apache.sentry.server.provider.webservice.AttributeDesc; +import org.apache.sentry.server.provider.webservice.FilterDesc; +import org.apache.sentry.server.provider.webservice.WebServiceProvider; +import org.apache.sentry.server.provider.webservice.WebServiceProviderFactory; +import org.apache.sentry.server.provider.webservice.WebServiceSpi; +import org.apache.sentry.server.provider.webservice.ServletDesc; +import org.apache.sentry.service.common.ServiceConstants.ServerConfig; +import org.apache.sentry.spi.ProviderManager; +import org.eclipse.jetty.security.ConstraintMapping; +import org.eclipse.jetty.security.ConstraintSecurityHandler; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.security.Constraint; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +@Slf4j +public class SentryWebServer { + + private Server server; + + public SentryWebServer(Configuration conf) { + server = new Server(); + + // Create a channel connector for "http/https" requests + ServerConnector connector; + int port = conf.getInt(ServerConfig.SENTRY_WEB_PORT, ServerConfig.SENTRY_WEB_PORT_DEFAULT); + if (conf.getBoolean(ServerConfig.SENTRY_WEB_USE_SSL, false)) { + SslContextFactory sslContextFactory = new SslContextFactory(); + sslContextFactory.setKeyStorePath(conf.get(ServerConfig.SENTRY_WEB_SSL_KEYSTORE_PATH, "")); + sslContextFactory.setKeyStorePassword( + conf.get(ServerConfig.SENTRY_WEB_SSL_KEYSTORE_PASSWORD, "")); + // Exclude SSL blacklist protocols + sslContextFactory.setExcludeProtocols(ServerConfig.SENTRY_SSL_PROTOCOL_BLACKLIST_DEFAULT); + Set moreExcludedSSLProtocols = + Sets.newHashSet(Splitter.on(",").trimResults().omitEmptyStrings() + .split(Strings.nullToEmpty(conf.get(ServerConfig.SENTRY_SSL_PROTOCOL_BLACKLIST)))); + sslContextFactory.addExcludeProtocols(moreExcludedSSLProtocols.toArray( + new String[moreExcludedSSLProtocols.size()])); + + HttpConfiguration httpConfiguration = new HttpConfiguration(); + httpConfiguration.setSecurePort(port); + httpConfiguration.setSecureScheme("https"); + httpConfiguration.addCustomizer(new SecureRequestCustomizer()); + + connector = new ServerConnector( + server, + new SslConnectionFactory(sslContextFactory, "http/1.1"), + new HttpConnectionFactory(httpConfiguration)); + + LOGGER.info("Now using SSL mode."); + } else { + connector = new ServerConnector(server, new HttpConnectionFactory()); + } + + connector.setPort(port); + server.setConnectors(new Connector[]{connector}); + + ServletContextHandler contextHandler = new ServletContextHandler(); + + // Load all of the Web Service Provider + + // get the web service providers + List serviceProviderFactories = ProviderManager.getInstance() + .load(WebServiceSpi.ID); + + // initialize the factories + for (WebServiceProviderFactory providerFactory : serviceProviderFactories) { + providerFactory.init(conf); + WebServiceProvider provider = providerFactory.create(); + + // register its listeners + for (EventListener listener : provider.getListeners()) { + contextHandler.addEventListener(listener); + } + + //register its attributes + for (AttributeDesc attributeEntry : provider.getAttributes()) { + contextHandler.getServletContext() + .setAttribute(attributeEntry.getName(), attributeEntry.getAttribute()); + } + + // register its servlets + for (ServletDesc servletEntry : provider.getServlets()) { + contextHandler + .addServlet(servletEntry.getServletHolder(), servletEntry.getPathSpec()); + } + + // register its filters + for (FilterDesc filterDesc : provider.getFilters()) { + contextHandler.addFilter(filterDesc.getFilterHolder(), filterDesc.getPathSpec(), + filterDesc.getDispatcherTypes()); + } + } + + ContextHandlerCollection contextHandlerCollection = new ContextHandlerCollection(); + contextHandlerCollection.setHandlers(new Handler[]{contextHandler, + new DefaultHandler()}); + + server.setHandler(disableTraceMethod(contextHandlerCollection)); + } + + /** + * Disables the HTTP TRACE method request which leads to Cross-Site Tracking (XST) problems. + * + * To disable it, we need to wrap the Handler (which has the HTTP TRACE enabled) with a constraint + * that denies access to the HTTP TRACE method. + * + * @param handler The Handler which has the HTTP TRACE enabled. + * @return A new Handler wrapped with the HTTP TRACE constraint and the Handler passed as + * parameter. + */ + private Handler disableTraceMethod(Handler handler) { + Constraint disableTraceConstraint = new Constraint(); + disableTraceConstraint.setName("Disable TRACE"); + disableTraceConstraint.setAuthenticate(true); + + ConstraintMapping mapping = new ConstraintMapping(); + mapping.setConstraint(disableTraceConstraint); + mapping.setMethod("TRACE"); + mapping.setPathSpec("/"); + + ConstraintSecurityHandler constraintSecurityHandler = new ConstraintSecurityHandler(); + constraintSecurityHandler.addConstraintMapping(mapping); + constraintSecurityHandler.setHandler(handler); + + return constraintSecurityHandler; + } + + public void start() throws Exception { + server.start(); + } + + public void stop() throws Exception { + server.stop(); + } + + public boolean isAlive() { + return server != null && server.isStarted(); + } + + +} http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/resources/META-INF/services/org.apache.sentry.server.provider.webservice.WebServiceProviderFactory ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/resources/META-INF/services/org.apache.sentry.server.provider.webservice.WebServiceProviderFactory b/sentry-service/sentry-service-web/src/main/resources/META-INF/services/org.apache.sentry.server.provider.webservice.WebServiceProviderFactory new file mode 100644 index 0000000..8111e69 --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/resources/META-INF/services/org.apache.sentry.server.provider.webservice.WebServiceProviderFactory @@ -0,0 +1,20 @@ +# +# 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. +# +# + +org.apache.sentry.service.web.DefaultWebServicesProvider \ No newline at end of file http://git-wip-us.apache.org/repos/asf/sentry/blob/ea7a33b7/sentry-service/sentry-service-web/src/main/webapp/index.html ---------------------------------------------------------------------- diff --git a/sentry-service/sentry-service-web/src/main/webapp/index.html b/sentry-service/sentry-service-web/src/main/webapp/index.html new file mode 100644 index 0000000..08df9d1 --- /dev/null +++ b/sentry-service/sentry-service-web/src/main/webapp/index.html @@ -0,0 +1,84 @@ + + + + + + Sentry Service + + + + + + + + + + + +
+ +
+
+

Runtime Information

+
+ +
+
+
+

Runtime Modifications

+
+ +
+
+
+

Service Information

+
+
+ +
+
+
+ + + +