You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by pr...@apache.org on 2020/03/14 05:52:15 UTC

[zeppelin] branch master updated: ZEPPELIN-4679: Add API validation for creating interpreter

This is an automated email from the ASF dual-hosted git repository.

prabhjyotsingh pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/master by this push:
     new a792b50  ZEPPELIN-4679: Add API validation for creating interpreter
a792b50 is described below

commit a792b5028ccdfce9bb29ad876fdde11cbbb62f98
Author: Prabhjyot Singh <pr...@gmail.com>
AuthorDate: Thu Mar 12 15:19:23 2020 +0530

    ZEPPELIN-4679: Add API validation for creating interpreter
    
    ### What is this PR for?
    This is an extension of ZEPPELIN-4377, I believe we should have an API validation for the same.
    
    ### What type of PR is it?
    [Bug Fix]
    
    ### What is the Jira issue?
    * https://issues.apache.org/jira/browse/ZEPPELIN-4679
    
    ### How should this be tested?
    Calling directly API should fail, and if somehow this is by pass from UI, UI should also return proper error.
    ```
    curl 'http://localhost:8080/api/interpreter/setting' -H 'Content-Type: application/json;charset=UTF-8' --data-binary '{"name":"test space","group":"angular","properties":{},"dependencies":[],"option":{"remote":true,"isExistingProcess":false,"setPermission":false,"session":false,"process":false,"perNote":"shared","perUser":"shared","owners":[]},"propertyValue":"","propertyKey":"","propertyType":"textarea"}'
    ```
    
    ### Screenshots (if appropriate)
    N/A
    
    ### Questions:
    * Does the licenses files need update? N/A
    * Is there breaking changes for older versions? N/A
    * Does this needs documentation? N/A
    
    Author: Prabhjyot Singh <pr...@gmail.com>
    
    Closes #3684 from prabhjyotsingh/ZEPPELIN-4679 and squashes the following commits:
    
    1fdddaf [Prabhjyot Singh] fix travis
    485f8ff [Prabhjyot Singh] make pattern static constant to avoid its compilation everytime
    d787e75 [Prabhjyot Singh] add unit test
    2c7a406 [Prabhjyot Singh] ZEPPELIN-4679: add API validation
---
 .../zeppelin/rest/InterpreterRestApiTest.java      | 57 ++++++++++++++++++++++
 .../interpreter/InterpreterSettingManager.java     | 19 +++++---
 2 files changed, 70 insertions(+), 6 deletions(-)

diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
index 9819b87..48d6c51 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/InterpreterRestApiTest.java
@@ -25,6 +25,7 @@ import org.apache.commons.httpclient.methods.DeleteMethod;
 import org.apache.commons.httpclient.methods.GetMethod;
 import org.apache.commons.httpclient.methods.PostMethod;
 import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.zeppelin.interpreter.InterpreterOption;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.notebook.Note;
@@ -234,6 +235,62 @@ public class InterpreterRestApiTest extends AbstractTestRestApi {
     post.releaseConnection();
   }
 
+  @Test
+  public void testSettingsCreateWithInvalidName() throws IOException {
+    String reqBody = "{"
+        + "\"name\": \"mdName\","
+        + "\"group\": \"md\","
+        + "\"properties\": {"
+        + "\"propname\": {"
+        + "\"value\": \"propvalue\","
+        + "\"name\": \"propname\","
+        + "\"type\": \"textarea\""
+        + "}"
+        + "},"
+        + "\"interpreterGroup\": ["
+        + "{"
+        + "\"class\": \"org.apache.zeppelin.markdown.Markdown\","
+        + "\"name\": \"md\""
+        + "}"
+        + "],"
+        + "\"dependencies\": [],"
+        + "\"option\": {"
+        + "\"remote\": true,"
+        + "\"session\": false"
+        + "}"
+        + "}";
+    JsonObject jsonRequest = gson.fromJson(StringUtils.replace(reqBody, "mdName", "mdValidName"), JsonElement.class).getAsJsonObject();
+    PostMethod post = httpPost("/interpreter/setting/", jsonRequest.toString());
+    String postResponse = post.getResponseBodyAsString();
+    LOG.info("testSetting with valid name\n" + post.getResponseBodyAsString());
+    InterpreterSetting created = convertResponseToInterpreterSetting(postResponse);
+    String newSettingId = created.getId();
+    // then : call create setting API
+    assertThat("test create method:", post, isAllowed());
+    post.releaseConnection();
+
+    // when: call delete setting API
+    DeleteMethod delete = httpDelete("/interpreter/setting/" + newSettingId);
+    LOG.info("testSetting delete response\n" + delete.getResponseBodyAsString());
+    // then: call delete setting API
+    assertThat("Test delete method:", delete, isAllowed());
+    delete.releaseConnection();
+
+
+    JsonObject jsonRequest2 = gson.fromJson(StringUtils.replace(reqBody, "mdName", "name space"), JsonElement.class).getAsJsonObject();
+    PostMethod post2 = httpPost("/interpreter/setting/", jsonRequest2.toString());
+    LOG.info("testSetting with name with space\n" + post2.getResponseBodyAsString());
+    assertThat("test create method with space:", post2, isNotFound());
+    post2.releaseConnection();
+
+    JsonObject jsonRequest3 = gson.fromJson(StringUtils.replace(reqBody, "mdName", ""), JsonElement.class).getAsJsonObject();
+    PostMethod post3 = httpPost("/interpreter/setting/", jsonRequest3.toString());
+    LOG.info("testSetting with empty name\n" + post3.getResponseBodyAsString());
+    assertThat("test create method with empty name:", post3, isNotFound());
+    post3.releaseConnection();
+
+  }
+
   public void testInterpreterRestart() throws IOException, InterruptedException {
     Note note = null;
     try {
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSettingManager.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSettingManager.java
index 415f1a1..69bdf9d 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSettingManager.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterSettingManager.java
@@ -26,15 +26,14 @@ import com.google.gson.Gson;
 import com.google.gson.GsonBuilder;
 import com.google.gson.reflect.TypeToken;
 
-import java.util.Properties;
 import java.util.LinkedHashMap;
 import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import javax.inject.Inject;
 import org.apache.commons.io.FileUtils;
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang.ArrayUtils;
 import org.apache.commons.lang.StringUtils;
-import org.apache.hadoop.yarn.webapp.hamlet.Hamlet;
 import org.apache.zeppelin.cluster.ClusterManagerServer;
 import org.apache.zeppelin.cluster.event.ClusterEvent;
 import org.apache.zeppelin.cluster.event.ClusterEventListener;
@@ -105,6 +104,7 @@ import static org.apache.zeppelin.cluster.ClusterManagerServer.CLUSTER_INTP_SETT
 @ManagedObject("interpreterSettingManager")
 public class InterpreterSettingManager implements NoteEventListener, ClusterEventListener {
 
+  private static final Pattern VALID_INTERPRETER_NAME = Pattern.compile("^[-_a-zA-Z0-9]+$");
   private static final Logger LOGGER = LoggerFactory.getLogger(InterpreterSettingManager.class);
   private static final Map<String, Object> DEFAULT_EDITOR = ImmutableMap.of(
       "language", (Object) "text",
@@ -767,9 +767,16 @@ public class InterpreterSettingManager implements NoteEventListener, ClusterEven
                                                     InterpreterOption option,
                                                     Map<String, InterpreterProperty> properties)
       throws IOException {
-    if (name.indexOf(".") >= 0) {
-      throw new IOException("'.' is invalid for InterpreterSetting name.");
+    if (name == null) {
+      throw new IOException("Interpreter name shouldn't be empty.");
     }
+
+    // check if name is valid
+    Matcher matcher = VALID_INTERPRETER_NAME.matcher(name);
+    if(!matcher.find()){
+      throw new IOException("Interpreter name shouldn't be empty, and can consist only of: -_a-zA-Z0-9");
+    }
+
     // check if name is existed
     for (InterpreterSetting interpreterSetting : interpreterSettings.values()) {
       if (interpreterSetting.getName().equals(name)) {
@@ -827,7 +834,7 @@ public class InterpreterSettingManager implements NoteEventListener, ClusterEven
     dependencyResolver.delRepo(id);
     saveToFile();
   }
-  
+
   /** restart in interpreter setting page */
   private InterpreterSetting inlineSetPropertyAndRestart(
       String id,