roleNames = new LinkedHashSet<>();
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
index 3381a516c5..97c223c2fc 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/realm/LdapRealm.java
@@ -109,6 +109,9 @@ import javax.naming.ldap.PagedResultsControl;
* # optional mapping from physical groups to logical application roles
* ldapRealm.rolesByGroup = \ LDN_USERS: user_role,\ NYK_USERS: user_role,\
* HKG_USERS: user_role,\ GLOBAL_ADMIN: admin_role,\ DEMOS: self-install_role
+ *
+ *
# optional list of roles that are allowed to authenticate
+ * ldapRealm.allowedRolesForAuthentication = admin_role,user_role
*
*
ldapRealm.permissionsByRole=\ user_role = *:ToDoItemsJdo:*:*,\
* *:ToDoItem:*:*; \ self-install_role = *:ToDoItemsFixturesService:install:* ;
@@ -176,6 +179,7 @@ public class LdapRealm extends JndiLdapRealm {
private String memberAttributeValueSuffix = "";
private final Map rolesByGroup = new LinkedHashMap();
+ private final List allowedRolesForAuthentication = new ArrayList();
private final Map> permissionsByRole =
new LinkedHashMap>();
@@ -202,6 +206,29 @@ public class LdapRealm extends JndiLdapRealm {
}
}
+ /**
+ * This overrides the implementation of queryForAuthenticationInfo inside JndiLdapRealm.
+ * In addition to calling the super method for authentication it also tries to validate
+ * if this user has atleast one of the allowed roles for authentication. In case the property
+ * allowedRolesForAuthentication is empty this check always returns true.
+ *
+ * @param token the submitted authentication token that triggered the authentication attempt.
+ * @param ldapContextFactory factory used to retrieve LDAP connections.
+ * @return AuthenticationInfo instance representing the authenticated user's information.
+ * @throws NamingException if any LDAP errors occur.
+ */
+ @Override
+ protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
+ LdapContextFactory ldapContextFactory)
+ throws NamingException {
+ AuthenticationInfo info = super.queryForAuthenticationInfo(token, ldapContextFactory);
+ // Credentials were verified. Verify that the principal has all allowedRulesForAuthentication
+ if (!hasAllowedAuthenticationRules(info.getPrincipals(), ldapContextFactory)) {
+ throw new NamingException("Principal does not have any of the allowedRolesForAuthentication");
+ }
+ return info;
+ }
+
/**
* Get groups from LDAP.
*
@@ -231,6 +258,23 @@ public class LdapRealm extends JndiLdapRealm {
return simpleAuthorizationInfo;
}
+ private boolean hasAllowedAuthenticationRules(PrincipalCollection principals,
+ final LdapContextFactory ldapContextFactory)
+ throws NamingException {
+ boolean allowed = allowedRolesForAuthentication.isEmpty();
+ if (!allowed) {
+ Set roles = getRoles(principals, ldapContextFactory);
+ for (String allowedRole: allowedRolesForAuthentication) {
+ if (roles.contains(allowedRole)) {
+ log.debug("Allowed role for user [" + allowedRole + "] found.");
+ allowed = true;
+ break;
+ }
+ }
+ }
+ return allowed;
+ }
+
private Set getRoles(PrincipalCollection principals,
final LdapContextFactory ldapContextFactory)
throws NamingException {
@@ -524,6 +568,10 @@ public class LdapRealm extends JndiLdapRealm {
this.memberAttributeValueSuffix = suffix;
}
+ public void setAllowedRolesForAuthentication(List allowedRolesForAuthencation) {
+ this.allowedRolesForAuthentication.addAll(allowedRolesForAuthencation);
+ }
+
public void setRolesByGroup(Map rolesByGroup) {
this.rolesByGroup.putAll(rolesByGroup);
}
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
index 8ca04762d3..9c511d46fe 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
@@ -588,17 +588,17 @@ public class NotebookRestApi {
throws IOException, IllegalArgumentException {
LOG.info("run note jobs {} ", noteId);
Note note = notebook.getNote(noteId);
+ AuthenticationInfo subject = new AuthenticationInfo(SecurityUtils.getPrincipal());
checkIfNoteIsNotNull(note);
checkIfUserCanWrite(noteId, "Insufficient privileges you cannot run job for this note");
try {
- note.runAll();
+ note.runAll(subject);
} catch (Exception ex) {
LOG.error("Exception from run", ex);
return new JsonResponse<>(Status.PRECONDITION_FAILED,
ex.getMessage() + "- Not selected or Invalid Interpreter bind").build();
}
-
return new JsonResponse<>(Status.OK).build();
}
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
index 9d9f5510a0..42edd235ed 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/ZeppelinRestApi.java
@@ -17,12 +17,18 @@
package org.apache.zeppelin.rest;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
import org.apache.zeppelin.annotation.ZeppelinApi;
import org.apache.zeppelin.server.JsonResponse;
import org.apache.zeppelin.util.Util;
+import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
/**
@@ -52,4 +58,25 @@ public class ZeppelinRestApi {
public Response getVersion() {
return new JsonResponse<>(Response.Status.OK, "Zeppelin version", Util.getVersion()).build();
}
+
+ /**
+ * Set the log level for root logger
+ * @param request
+ * @param logLevel new log level for Rootlogger
+ * @return
+ */
+ @PUT
+ @Path("log/level/{logLevel}")
+ public Response changeRootLogLevel(@Context HttpServletRequest request,
+ @PathParam("logLevel") String logLevel) {
+ Level level = Level.toLevel(logLevel);
+ if (logLevel.toLowerCase().equalsIgnoreCase(level.toString().toLowerCase())) {
+ Logger.getRootLogger().setLevel(level);
+ return new JsonResponse<>(Response.Status.OK).build();
+ } else {
+ return new JsonResponse<>(Response.Status.NOT_ACCEPTABLE,
+ "Please check LOG level specified. Valid values: DEBUG, ERROR, FATAL, "
+ + "INFO, TRACE, WARN").build();
+ }
+ }
}
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
index 2b67dfdc3a..826ae5f863 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/server/ZeppelinServer.java
@@ -27,7 +27,6 @@ import javax.servlet.DispatcherType;
import javax.ws.rs.core.Application;
import org.apache.commons.lang.StringUtils;
-import org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet;
import org.apache.shiro.web.env.EnvironmentLoaderListener;
import org.apache.shiro.web.servlet.ShiroFilter;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
@@ -286,7 +285,7 @@ public class ZeppelinServer extends Application {
final ServletHolder servletHolder = new ServletHolder(notebookWsServer);
servletHolder.setInitParameter("maxTextMessageSize", maxTextMessageSize);
- final ServletContextHandler cxfContext = new ServletContextHandler(
+ final ServletContextHandler context = new ServletContextHandler(
ServletContextHandler.SESSIONS);
webapp.addServlet(servletHolder, "/ws/*");
@@ -316,13 +315,15 @@ public class ZeppelinServer extends Application {
private static void setupRestApiContextHandler(WebAppContext webapp,
ZeppelinConfiguration conf) {
- final ServletHolder cxfServletHolder = new ServletHolder(new CXFNonSpringJaxrsServlet());
- cxfServletHolder.setInitParameter("javax.ws.rs.Application", ZeppelinServer.class.getName());
- cxfServletHolder.setName("rest");
- cxfServletHolder.setForcedPath("rest");
+ final ServletHolder servletHolder = new ServletHolder(
+ new org.glassfish.jersey.servlet.ServletContainer());
+
+ servletHolder.setInitParameter("javax.ws.rs.Application", ZeppelinServer.class.getName());
+ servletHolder.setName("rest");
+ servletHolder.setForcedPath("rest");
webapp.setSessionHandler(new SessionHandler());
- webapp.addServlet(cxfServletHolder, "/api/*");
+ webapp.addServlet(servletHolder, "/api/*");
String shiroIniPath = conf.getShiroPath();
if (!StringUtils.isBlank(shiroIniPath)) {
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
index 87d0009473..5588be0218 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
@@ -19,15 +19,9 @@ package org.apache.zeppelin.socket;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
-import java.util.Set;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Matcher;
@@ -1696,6 +1690,23 @@ public class NotebookServer extends WebSocketServlet
p.setErrorMessage((String) fromMessage.get("errorMessage"));
p.setStatusWithoutNotification(status);
+ // Spell uses ISO 8601 formatted string generated from moment
+ String dateStarted = (String) fromMessage.get("dateStarted");
+ String dateFinished = (String) fromMessage.get("dateFinished");
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX");
+
+ try {
+ p.setDateStarted(df.parse(dateStarted));
+ } catch (ParseException e) {
+ LOG.error("Failed parse dateStarted", e);
+ }
+
+ try {
+ p.setDateFinished(df.parse(dateFinished));
+ } catch (ParseException e) {
+ LOG.error("Failed parse dateFinished", e);
+ }
+
addNewParagraphIfLastParagraphIsExecuted(note, p);
if (!persistNoteWithAuthInfo(conn, note, p)) {
return;
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
index dcb5a1f339..19eb980c61 100644
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java
@@ -34,6 +34,7 @@ import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.zeppelin.conf.ZeppelinConfiguration;
+import org.apache.zeppelin.realm.ActiveDirectoryGroupRealm;
import org.apache.zeppelin.realm.LdapRealm;
import org.mortbay.log.Log;
import org.slf4j.Logger;
@@ -133,6 +134,9 @@ public class SecurityUtils {
} else if (name.equals("org.apache.zeppelin.realm.LdapRealm")) {
allRoles = ((LdapRealm) realm).getListRoles();
break;
+ } else if (name.equals("org.apache.zeppelin.realm.ActiveDirectoryGroupRealm")) {
+ allRoles = ((ActiveDirectoryGroupRealm) realm).getListRoles();
+ break;
}
}
if (allRoles != null) {
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
index add23ac0fb..fa5bed994b 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/ParagraphActionsIT.java
@@ -253,7 +253,6 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
} catch (Exception e) {
handleException("Exception in ParagraphActionsIT while testDisableParagraphRunButton ", e);
}
-
}
@Test
@@ -594,6 +593,7 @@ public class ParagraphActionsIT extends AbstractZeppelinIT {
CoreMatchers.equalTo("Howdy "));
Select dropDownMenu = new Select(driver.findElement(By.xpath("(" + (getParagraphXPath(1) + "//select)[1]"))));
+
dropDownMenu.selectByVisibleText("Alice");
collector.checkThat("After selection in drop down menu, output should display the newly selected option",
driver.findElement(By.xpath(getParagraphXPath(1) + "//div[contains(@class, 'text plainTextContent')]")).getText(),
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
index 2cc6a6c8a2..0aa03541ff 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/integration/SparkParagraphIT.java
@@ -32,6 +32,8 @@ import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.List;
+
public class SparkParagraphIT extends AbstractZeppelinIT {
private static final Logger LOG = LoggerFactory.getLogger(SparkParagraphIT.class);
@@ -182,10 +184,16 @@ public class SparkParagraphIT extends AbstractZeppelinIT {
);
}
- WebElement paragraph1Result = driver.findElement(By.xpath(
- getParagraphXPath(1) + "//div[contains(@id,\"_graph\")]/div/div/div/div/div[1]"));
+ // Age, Job, Marital, Education, Balance
+ List tableHeaders = driver.findElements(By.cssSelector("span.ui-grid-header-cell-label"));
+ String headerNames = "";
+
+ for(WebElement header : tableHeaders) {
+ headerNames += header.getText().toString() + "|";
+ }
+
collector.checkThat("Paragraph from SparkParagraphIT of testSqlSpark result: ",
- paragraph1Result.getText().toString(), CoreMatchers.equalTo("age\n▼\njob\n▼\nmarital\n▼\neducation\n▼\nbalance\n▼\n30 unemployed married primary 1787"));
+ headerNames, CoreMatchers.equalTo("Age|Job|Marital|Education|Balance|"));
} catch (Exception e) {
handleException("Exception in SparkParagraphIT while testSqlSpark", e);
}
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
index 7ea2774c9d..00b82fdd6f 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
@@ -429,13 +429,19 @@ public abstract class AbstractTestRestApi {
LOG.info("{} - {}", postMethod.getStatusCode(), postMethod.getStatusText());
Pattern pattern = Pattern.compile("JSESSIONID=([a-zA-Z0-9-]*)");
Header[] setCookieHeaders = postMethod.getResponseHeaders("Set-Cookie");
+ String jsessionId = null;
for (Header setCookie : setCookieHeaders) {
java.util.regex.Matcher matcher = pattern.matcher(setCookie.toString());
if (matcher.find()) {
- return matcher.group(1);
+ jsessionId = matcher.group(1);
}
}
- return StringUtils.EMPTY;
+
+ if (jsessionId != null) {
+ return jsessionId;
+ } else {
+ return StringUtils.EMPTY;
+ }
}
protected static boolean userAndPasswordAreNotBlank(String user, String pwd) {
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
index 4ee5a0078a..5093cb838f 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/ZeppelinRestApiTest.java
@@ -167,7 +167,7 @@ public class ZeppelinRestApiTest extends AbstractTestRestApi {
assertEquals("paragraph col width check failed", 9.0, p.getConfig().get("colWidth"));
assertTrue("paragraph show title check failed", ((boolean) p.getConfig().get("title")));
Map graph = ((List