Repository: zeppelin
Updated Branches:
  refs/heads/master 794c66b08 -> 32fe23f2e


[ZEPPELIN-1586] Add security check in NotebookRestApi

### What is this PR for?

Bring some security check in `NotebookRestApi`.
### What type of PR is it?

[Bug Fix | Improvement | Refactoring]
### Todos
- [x] - Create a proper way to throw webapp error
- [x] - Add in `NotebookAuthorization` some method to check if user is owner, 
reader or writer
- [x] - Add Authorization check in `NotebookRestapi`
- [x] - Add New test for security in notebook rest api

### What is the Jira issue?
- [ZEPPELIN-1586](https://issues.apache.org/jira/browse/ZEPPELIN-1586)
### How should this be tested?

First, force Zeppelin to use auth.
- In `conf/zeppelin-site.xml` change `zeppelin.anonymous.allowed` to **false**

  ```
  <property>
  <name>zeppelin.anonymous.allowed</name>
  <value>false</value>
  <description>Anonymous user allowed by default</description>
  </property>
  ```
- In `conf/shiro.ini` set Shiro to use `Auth` at the end of the file

  ```
  #/** = anon
  /** = authc
  ```
- Start Zeppelin, login and set some permission to a note
- try to get a note from Zeppelin Rest Api 
`http://localhost:8080/api/notebook/{noteId}` (you can use your browser or curl 
(if you use curl please add shiro token to curl cookie))
### Screenshots (if appropriate)

![note_permission_rest_api](https://cloud.githubusercontent.com/assets/3139557/19827600/ffd68a06-9dea-11e6-8dd5-43f3bd401011.gif)
### Questions:
- Does the licenses files need update? No
- Is there breaking changes for older versions? No
- Does this needs documentation? Maybe

Author: Anthony Corbacho <[email protected]>

Closes #1567 from anthonycorbacho/fix/ZEPPELIN-1586 and squashes the following 
commits:

6615935 [Anthony Corbacho] Clean anonymous allowed property when shutting down 
zeppelin server
30815c1 [Anthony Corbacho] Fix typo
bab7e60 [Anthony Corbacho] Rewording
decd1e9 [Anthony Corbacho] Simple implementation of notebook test with shiro 
(security)
b412266 [Anthony Corbacho] Refactored Abstract rest api test to also handle the 
case of tests with shiro (security), I also added some utility http method to 
do action with authenticated user
db0c39c [Anthony Corbacho] Adress review and fix typos
eacfa8e [Anthony Corbacho] Fix typo and bad copy paste for isOwner
c8c42b2 [Anthony Corbacho] Change cxf version from 2.7.7 to 2.7.8 to avoid 
method not found where throw WebAppException
ed404a4 [Anthony Corbacho] Rename permission check note :: be more meaningful
6030776 [Anthony Corbacho] Handle security check
fe380ab [Anthony Corbacho] Add webapp exception handler :)
21f9288 [Anthony Corbacho] Replace check of aninonimous by method
0e4cc3c [Anthony Corbacho] Add new method to check if user and roles are member 
of the note (at least owner, reader, writer)
da3415f [Anthony Corbacho] Add new method to help to determinate if user is 
part of writer and/or owner for the given note
4a43b07 [Anthony Corbacho] Add new method on ZeppelinConfiguration to get is 
zeppelin is running on anonimous mode or not


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/32fe23f2
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/32fe23f2
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/32fe23f2

Branch: refs/heads/master
Commit: 32fe23f2e283f6e555f54861ff20376507d2b705
Parents: 794c66b
Author: Anthony Corbacho <[email protected]>
Authored: Thu Nov 3 11:59:07 2016 +0900
Committer: Mina Lee <[email protected]>
Committed: Thu Nov 3 15:16:14 2016 +0900

----------------------------------------------------------------------
 zeppelin-server/pom.xml                         |   2 +-
 .../apache/zeppelin/rest/NotebookRestApi.java   | 217 +++++++++++--------
 .../rest/exception/NotFoundException.java       |  59 +++++
 .../rest/exception/UnauthorizedException.java   |  50 +++++
 .../apache/zeppelin/socket/NotebookServer.java  |  42 ++--
 .../apache/zeppelin/utils/ExceptionUtils.java   |  36 +++
 .../zeppelin/rest/AbstractTestRestApi.java      | 115 +++++++++-
 .../rest/NotebookSecurityRestApiTest.java       | 156 +++++++++++++
 .../zeppelin/conf/ZeppelinConfiguration.java    |   6 +-
 .../notebook/NotebookAuthorization.java         |  60 ++++-
 10 files changed, 623 insertions(+), 120 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/pom.xml
----------------------------------------------------------------------
diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml
index 862fc30..ee53a37 100644
--- a/zeppelin-server/pom.xml
+++ b/zeppelin-server/pom.xml
@@ -33,7 +33,7 @@
   <name>Zeppelin: Server</name>
 
   <properties>
-    <cxf.version>2.7.7</cxf.version>
+    <cxf.version>2.7.8</cxf.version>
     <commons.httpclient.version>4.3.6</commons.httpclient.version>
     <hadoop-common.version>2.6.0</hadoop-common.version>
   </properties>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/main/java/org/apache/zeppelin/rest/NotebookRestApi.java
----------------------------------------------------------------------
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 ae66330..5b27d0e 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
@@ -23,6 +23,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
 import javax.ws.rs.DELETE;
 import javax.ws.rs.GET;
 import javax.ws.rs.POST;
@@ -34,31 +35,33 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.Response.Status;
 
-import com.google.common.collect.Sets;
-import com.google.common.reflect.TypeToken;
-import com.google.gson.Gson;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.zeppelin.interpreter.InterpreterResult;
-import org.apache.zeppelin.utils.InterpreterBindingUtils;
-import org.quartz.CronExpression;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
 import org.apache.zeppelin.annotation.ZeppelinApi;
+import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.apache.zeppelin.notebook.Note;
 import org.apache.zeppelin.notebook.Notebook;
 import org.apache.zeppelin.notebook.NotebookAuthorization;
 import org.apache.zeppelin.notebook.Paragraph;
+import org.apache.zeppelin.rest.exception.NotFoundException;
+import org.apache.zeppelin.rest.exception.UnauthorizedException;
 import org.apache.zeppelin.rest.message.CronRequest;
-import org.apache.zeppelin.types.InterpreterSettingsList;
 import org.apache.zeppelin.rest.message.NewNoteRequest;
 import org.apache.zeppelin.rest.message.NewParagraphRequest;
 import org.apache.zeppelin.rest.message.RunParagraphWithParametersRequest;
 import org.apache.zeppelin.search.SearchService;
 import org.apache.zeppelin.server.JsonResponse;
 import org.apache.zeppelin.socket.NotebookServer;
+import org.apache.zeppelin.types.InterpreterSettingsList;
 import org.apache.zeppelin.user.AuthenticationInfo;
+import org.apache.zeppelin.utils.InterpreterBindingUtils;
 import org.apache.zeppelin.utils.SecurityUtils;
+import org.quartz.CronExpression;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Sets;
+import com.google.common.reflect.TypeToken;
+import com.google.gson.Gson;
 
 /**
  * Rest api endpoint for the notebook.
@@ -90,6 +93,8 @@ public class NotebookRestApi {
   @Path("{noteId}/permissions")
   @ZeppelinApi
   public Response getNotePermissions(@PathParam("noteId") String noteId) {
+    checkIfUserCanRead(noteId,
+        "Insufficient privileges you cannot get the list of permissions for 
this note");
     HashMap<String, Set<String>> permissionsMap = new HashMap<>();
     permissionsMap.put("owners", notebookAuthorization.getOwners(noteId));
     permissionsMap.put("readers", notebookAuthorization.getReaders(noteId));
@@ -106,6 +111,60 @@ public class NotebookRestApi {
   }
 
   /**
+   * Set of utils method to check if current user can perform action to the 
note.
+   * Since we only have security on notebook level, from now we keep this 
logic in this class.
+   * In the future we might want to generalize this for the rest of the api 
enmdpoints.
+   */
+  
+  /**
+   * Check if the current user own the given note.
+   */
+  private void checkIfUserIsOwner(String noteId, String errorMsg) {
+    Set<String> userAndRoles = Sets.newHashSet();
+    userAndRoles.add(SecurityUtils.getPrincipal());
+    userAndRoles.addAll(SecurityUtils.getRoles());
+    if (!notebookAuthorization.isOwner(userAndRoles, noteId)) {
+      throw new UnauthorizedException(errorMsg);
+    }
+  }
+  
+  /**
+   * Check if the current user is either Owner or Writer for the given note.
+   */
+  private void checkIfUserCanWrite(String noteId, String errorMsg) {
+    Set<String> userAndRoles = Sets.newHashSet();
+    userAndRoles.add(SecurityUtils.getPrincipal());
+    userAndRoles.addAll(SecurityUtils.getRoles());
+    if (!notebookAuthorization.hasWriteAuthorization(userAndRoles, noteId)) {
+      throw new UnauthorizedException(errorMsg);
+    }
+  }
+  
+  /**
+   * Check if the current user can access (at least he have to be reader) the 
given note.
+   */
+  private void checkIfUserCanRead(String noteId, String errorMsg) {
+    Set<String> userAndRoles = Sets.newHashSet();
+    userAndRoles.add(SecurityUtils.getPrincipal());
+    userAndRoles.addAll(SecurityUtils.getRoles());
+    if (!notebookAuthorization.hasReadAuthorization(userAndRoles, noteId)) {
+      throw new UnauthorizedException(errorMsg);
+    }
+  }
+  
+  private void checkIfNoteIsNotNull(Note note) {
+    if (note == null) {
+      throw new NotFoundException("note not found");
+    }
+  }
+  
+  private void checkIfParagraphIsNotNull(Paragraph paragraph) {
+    if (paragraph == null) {
+      throw new NotFoundException("paragraph not found");
+    }
+  }
+  
+  /**
    * set note authorization information
    */
   @PUT
@@ -113,22 +172,21 @@ public class NotebookRestApi {
   @ZeppelinApi
   public Response putNotePermissions(@PathParam("noteId") String noteId, 
String req)
       throws IOException {
-    HashMap<String, HashSet<String>> permMap =
-        gson.fromJson(req, new TypeToken<HashMap<String, HashSet<String>>>() {
-        }.getType());
-    Note note = notebook.getNote(noteId);
     String principal = SecurityUtils.getPrincipal();
     HashSet<String> roles = SecurityUtils.getRoles();
-    LOG.info("Set permissions {} {} {} {} {}", noteId, principal, 
permMap.get("owners"),
-        permMap.get("readers"), permMap.get("writers"));
-
     HashSet<String> userAndRoles = new HashSet<>();
     userAndRoles.add(principal);
     userAndRoles.addAll(roles);
-    if (!notebookAuthorization.isOwner(noteId, userAndRoles)) {
-      return new JsonResponse<>(Status.FORBIDDEN,
-          ownerPermissionError(userAndRoles, 
notebookAuthorization.getOwners(noteId))).build();
-    }
+    
+    checkIfUserIsOwner(noteId,
+        ownerPermissionError(userAndRoles, 
notebookAuthorization.getOwners(noteId)));
+    
+    HashMap<String, HashSet<String>> permMap =
+        gson.fromJson(req, new TypeToken<HashMap<String, HashSet<String>>>() 
{}.getType());
+    Note note = notebook.getNote(noteId);
+    
+    LOG.info("Set permissions {} {} {} {} {}", noteId, principal, 
permMap.get("owners"),
+        permMap.get("readers"), permMap.get("writers"));
 
     HashSet<String> readers = permMap.get("readers");
     HashSet<String> owners = permMap.get("owners");
@@ -170,6 +228,9 @@ public class NotebookRestApi {
   @Path("interpreter/bind/{noteId}")
   @ZeppelinApi
   public Response bind(@PathParam("noteId") String noteId, String req) throws 
IOException {
+    checkIfUserCanWrite(noteId,
+        "Insufficient privileges you cannot bind any interpreters to this 
note");
+
     List<String> settingIdList = gson.fromJson(req, new 
TypeToken<List<String>>() {
     }.getType());
     notebook.bindInterpretersToNote(SecurityUtils.getPrincipal(), noteId, 
settingIdList);
@@ -183,6 +244,8 @@ public class NotebookRestApi {
   @Path("interpreter/bind/{noteId}")
   @ZeppelinApi
   public Response bind(@PathParam("noteId") String noteId) {
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get any 
interpreters settings");
+
     List<InterpreterSettingsList> settingList =
         InterpreterBindingUtils.getInterpreterBindings(notebook, noteId);
     notebookServer.broadcastInterpreterBindings(noteId, settingList);
@@ -204,9 +267,8 @@ public class NotebookRestApi {
   @ZeppelinApi
   public Response getNote(@PathParam("noteId") String noteId) throws 
IOException {
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get this 
note");
 
     return new JsonResponse<>(Status.OK, "", note).build();
   }
@@ -222,6 +284,7 @@ public class NotebookRestApi {
   @Path("export/{noteId}")
   @ZeppelinApi
   public Response exportNote(@PathParam("noteId") String noteId) throws 
IOException {
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot export this 
note");
     String exportJson = notebook.exportNote(noteId);
     return new JsonResponse<>(Status.OK, "", exportJson).build();
   }
@@ -290,6 +353,7 @@ public class NotebookRestApi {
   @ZeppelinApi
   public Response deleteNote(@PathParam("noteId") String noteId) throws 
IOException {
     LOG.info("Delete note {} ", noteId);
+    checkIfUserIsOwner(noteId, "Insufficient privileges you cannot delete this 
note");
     AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
     if (!(noteId.isEmpty())) {
       Note note = notebook.getNote(noteId);
@@ -315,6 +379,7 @@ public class NotebookRestApi {
   public Response cloneNote(@PathParam("noteId") String noteId, String message)
       throws IOException, CloneNotSupportedException, IllegalArgumentException 
{
     LOG.info("clone note by JSON {}", message);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot clone this 
note");
     NewNoteRequest request = gson.fromJson(message, NewNoteRequest.class);
     String newNoteName = null;
     if (request != null) {
@@ -342,9 +407,8 @@ public class NotebookRestApi {
     LOG.info("insert paragraph {} {}", noteId, message);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot add 
paragraph to this note");
 
     NewParagraphRequest request = gson.fromJson(message, 
NewParagraphRequest.class);
 
@@ -379,14 +443,10 @@ public class NotebookRestApi {
     LOG.info("get paragraph {} {}", noteId, paragraphId);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
-    }
-
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get this 
paragraph");
     Paragraph p = note.getParagraph(paragraphId);
-    if (p == null) {
-      return new JsonResponse(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(p);
 
     return new JsonResponse<>(Status.OK, "", p).build();
   }
@@ -407,14 +467,11 @@ public class NotebookRestApi {
     LOG.info("move paragraph {} {} {}", noteId, paragraphId, newIndex);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot move 
paragraph");
 
     Paragraph p = note.getParagraph(paragraphId);
-    if (p == null) {
-      return new JsonResponse(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(p);
 
     try {
       note.moveParagraph(paragraphId, Integer.parseInt(newIndex), true);
@@ -444,14 +501,12 @@ public class NotebookRestApi {
     LOG.info("delete paragraph {} {}", noteId, paragraphId);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId,
+        "Insufficient privileges you cannot remove paragraph from this note");
 
     Paragraph p = note.getParagraph(paragraphId);
-    if (p == null) {
-      return new JsonResponse(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(p);
 
     AuthenticationInfo subject = new 
AuthenticationInfo(SecurityUtils.getPrincipal());
     note.removeParagraph(SecurityUtils.getPrincipal(), paragraphId);
@@ -475,9 +530,8 @@ public class NotebookRestApi {
       throws IOException, IllegalArgumentException {
     LOG.info("run note jobs {} ", noteId);
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot run job 
for this note");
 
     try {
       note.runAll();
@@ -504,9 +558,8 @@ public class NotebookRestApi {
       throws IOException, IllegalArgumentException {
     LOG.info("stop note jobs {} ", noteId);
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot stop this 
job for this note");
 
     for (Paragraph p : note.getParagraphs()) {
       if (!p.isTerminated()) {
@@ -530,9 +583,8 @@ public class NotebookRestApi {
       throws IOException, IllegalArgumentException {
     LOG.info("get note job status.");
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get job 
status");
 
     return new JsonResponse<>(Status.OK, null, 
note.generateParagraphsInfo()).build();
   }
@@ -553,14 +605,11 @@ public class NotebookRestApi {
       throws IOException, IllegalArgumentException {
     LOG.info("get note paragraph job status.");
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get job 
status");
 
     Paragraph paragraph = note.getParagraph(paragraphId);
-    if (paragraph == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(paragraph);
 
     return new JsonResponse<>(Status.OK, null, 
note.generateSingleParagraphInfo(paragraphId)).
       build();
@@ -583,14 +632,10 @@ public class NotebookRestApi {
     LOG.info("run paragraph job asynchronously {} {} {}", noteId, paragraphId, 
message);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
-
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot run job 
for this note");
     Paragraph paragraph = note.getParagraph(paragraphId);
-    if (paragraph == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(paragraph);
 
     // handle params if presented
     handleParagraphParams(message, note, paragraph);
@@ -625,14 +670,10 @@ public class NotebookRestApi {
     LOG.info("run paragraph synchronously {} {} {}", noteId, paragraphId, 
message);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
-
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot run 
paragraph");
     Paragraph paragraph = note.getParagraph(paragraphId);
-    if (paragraph == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(paragraph);
 
     // handle params if presented
     handleParagraphParams(message, note, paragraph);
@@ -667,14 +708,10 @@ public class NotebookRestApi {
       @PathParam("paragraphId") String paragraphId) throws IOException, 
IllegalArgumentException {
     LOG.info("stop paragraph job {} ", noteId);
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
-
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot stop 
paragraph");
     Paragraph p = note.getParagraph(paragraphId);
-    if (p == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "paragraph not 
found.").build();
-    }
+    checkIfParagraphIsNotNull(p);
     p.abort();
     return new JsonResponse<>(Status.OK).build();
   }
@@ -696,9 +733,8 @@ public class NotebookRestApi {
     CronRequest request = gson.fromJson(message, CronRequest.class);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanWrite(noteId, "Insufficient privileges you cannot set a cron 
job for this note");
 
     if (!CronExpression.isValidExpression(request.getCronString())) {
       return new JsonResponse<>(Status.BAD_REQUEST, "wrong cron 
expressions.").build();
@@ -727,9 +763,9 @@ public class NotebookRestApi {
     LOG.info("Remove cron job note {}", noteId);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserIsOwner(noteId,
+        "Insufficient privileges you cannot remove this cron job from this 
note");
 
     Map<String, Object> config = note.getConfig();
     config.put("cron", null);
@@ -754,9 +790,8 @@ public class NotebookRestApi {
     LOG.info("Get cron job note {}", noteId);
 
     Note note = notebook.getNote(noteId);
-    if (note == null) {
-      return new JsonResponse<>(Status.NOT_FOUND, "note not found.").build();
-    }
+    checkIfNoteIsNotNull(note);
+    checkIfUserCanRead(noteId, "Insufficient privileges you cannot get cron 
information");
 
     return new JsonResponse<>(Status.OK, note.getConfig().get("cron")).build();
   }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/NotFoundException.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/NotFoundException.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/NotFoundException.java
new file mode 100644
index 0000000..7f9c17d
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/NotFoundException.java
@@ -0,0 +1,59 @@
+/*
+ * 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.zeppelin.rest.exception;
+import static javax.ws.rs.core.Response.Status.NOT_FOUND;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import org.apache.zeppelin.utils.ExceptionUtils;
+
+/**
+ * Not Found handler for WebApplicationException.
+ * 
+ */
+public class NotFoundException extends WebApplicationException {
+  private static final long serialVersionUID = 2459398393216512293L;
+
+  /**
+   * Create a HTTP 404 (Not Found) exception.
+   */
+  public NotFoundException() {
+    super(ExceptionUtils.jsonResponse(NOT_FOUND));
+  }
+
+  /**
+   * Create a HTTP 404 (Not Found) exception.
+   * @param message the String that is the entity of the 404 response.
+   */
+  public NotFoundException(String message) {
+    super(notFoundJson(message));
+  }
+
+  private static Response notFoundJson(String message) {
+    return ExceptionUtils.jsonResponseContent(NOT_FOUND, message);
+  }
+
+  public NotFoundException(Throwable cause) {
+    super(cause, notFoundJson(cause.getMessage()));
+  }
+
+  public NotFoundException(Throwable cause, String message) {
+    super(cause, notFoundJson(message));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/UnauthorizedException.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/UnauthorizedException.java
 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/UnauthorizedException.java
new file mode 100644
index 0000000..7b968ab
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/exception/UnauthorizedException.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.zeppelin.rest.exception;
+
+import static javax.ws.rs.core.Response.Status.FORBIDDEN;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.Response;
+
+import org.apache.zeppelin.utils.ExceptionUtils;
+
+/**
+ * UnauthorizedException handler for WebApplicationException.
+ * 
+ */
+public class UnauthorizedException extends WebApplicationException {
+  private static final long serialVersionUID = 4394749068760407567L;
+  private static final String UNAUTHORIZED_MSG = "Authorization required";
+
+  public UnauthorizedException() {
+    super(unauthorizedJson(UNAUTHORIZED_MSG));
+  }
+
+  private static Response unauthorizedJson(String message) {
+    return ExceptionUtils.jsonResponseContent(FORBIDDEN, message);
+  }
+  
+  public UnauthorizedException(Throwable cause, String message) {
+    super(cause, unauthorizedJson(message));
+  }
+  
+  public UnauthorizedException(String message) {
+    super(unauthorizedJson(message));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/main/java/org/apache/zeppelin/socket/NotebookServer.java
----------------------------------------------------------------------
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 28a9ac3..4934265 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
@@ -16,10 +16,22 @@
  */
 package org.apache.zeppelin.socket;
 
-import com.google.common.base.Strings;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.reflect.TypeToken;
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+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.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.vfs2.FileSystemException;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
@@ -36,7 +48,13 @@ import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry;
 import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener;
 import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
-import org.apache.zeppelin.notebook.*;
+import org.apache.zeppelin.notebook.JobListenerFactory;
+import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.notebook.Notebook;
+import org.apache.zeppelin.notebook.NotebookAuthorization;
+import org.apache.zeppelin.notebook.NotebookEventListener;
+import org.apache.zeppelin.notebook.Paragraph;
+import org.apache.zeppelin.notebook.ParagraphJobListener;
 import org.apache.zeppelin.notebook.repo.NotebookRepo.Revision;
 import org.apache.zeppelin.notebook.socket.Message;
 import org.apache.zeppelin.notebook.socket.Message.OP;
@@ -54,13 +72,10 @@ import org.quartz.SchedulerException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.net.UnknownHostException;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
+import com.google.common.base.Strings;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.reflect.TypeToken;
 
 /**
  * Zeppelin websocket service.
@@ -147,8 +162,7 @@ public class NotebookServer extends WebSocketServlet 
implements
       }
 
       ZeppelinConfiguration conf = ZeppelinConfiguration.create();
-      boolean allowAnonymous = conf.
-          
getBoolean(ZeppelinConfiguration.ConfVars.ZEPPELIN_ANONYMOUS_ALLOWED);
+      boolean allowAnonymous = conf.isAnonymousAllowed();
       if (!allowAnonymous && messagereceived.principal.equals("anonymous")) {
         throw new Exception("Anonymous access not allowed ");
       }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/main/java/org/apache/zeppelin/utils/ExceptionUtils.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/ExceptionUtils.java 
b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/ExceptionUtils.java
new file mode 100644
index 0000000..ce87e5e
--- /dev/null
+++ 
b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/ExceptionUtils.java
@@ -0,0 +1,36 @@
+/*
+ * 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.zeppelin.utils;
+
+import javax.ws.rs.core.Response.Status;
+
+import org.apache.zeppelin.server.JsonResponse;
+
+/**
+ * Utility method for exception in rest api.
+ *
+ */
+public class ExceptionUtils {
+
+  public static javax.ws.rs.core.Response jsonResponse(Status status) {
+    return new JsonResponse<>(status).build();
+  }
+  
+  public static javax.ws.rs.core.Response jsonResponseContent(Status status, 
String message) {
+    return new JsonResponse<>(status, message).build();
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/test/java/org/apache/zeppelin/rest/AbstractTestRestApi.java
----------------------------------------------------------------------
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 823b1dd..6d10337 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
@@ -26,18 +26,22 @@ import java.util.List;
 import java.util.Properties;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
+import java.util.regex.Pattern;
 
 import org.apache.commons.exec.CommandLine;
 import org.apache.commons.exec.DefaultExecutor;
 import org.apache.commons.exec.PumpStreamHandler;
 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
 import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
 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.httpclient.methods.RequestEntity;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.interpreter.InterpreterSetting;
 import org.apache.zeppelin.server.ZeppelinServer;
@@ -47,6 +51,7 @@ import org.hamcrest.TypeSafeMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.gson.Gson;
 import com.google.gson.JsonElement;
 import com.google.gson.JsonParseException;
 import com.google.gson.JsonParser;
@@ -60,6 +65,29 @@ public abstract class AbstractTestRestApi {
   protected static final boolean wasRunning = checkIfServerIsRunning();
   static boolean pySpark = false;
   static boolean sparkR = false;
+  static Gson gson = new Gson();
+  static boolean isRunningWithAuth = false;
+
+  private static File shiroIni = null;
+  private static String zeppelinShiro =
+      "[users]\n" +
+      "admin = password1, admin\n" +
+      "user1 = password2, role1, role2\n" +
+      "user2 = password3, role3\n" +
+      "user3 = password4, role2\n" +
+      "[main]\n" +
+      "sessionManager = 
org.apache.shiro.web.session.mgt.DefaultWebSessionManager\n" +
+      "securityManager.sessionManager = $sessionManager\n" +
+      "securityManager.sessionManager.globalSessionTimeout = 86400000\n" +
+      "shiro.loginUrl = /api/login\n" +
+      "[roles]\n" +
+      "role1 = *\n" +
+      "role2 = *\n" +
+      "role3 = *\n" +
+      "admin = *" +
+      "[urls]\n" +
+      "/api/version = anon\n" +
+      "/** = authc";
 
   private String getUrl(String path) {
     String url;
@@ -95,15 +123,27 @@ public abstract class AbstractTestRestApi {
     }
   };
 
-  protected static void startUp() throws Exception {
+  private static void start(boolean withAuth) throws Exception {
     if (!wasRunning) {
       
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_HOME.getVarName(), 
"../");
       
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_WAR.getVarName(), 
"../zeppelin-web/dist");
       LOG.info("Staring test Zeppelin up...");
+      ZeppelinConfiguration conf = ZeppelinConfiguration.create();
 
+      if (withAuth) {
+        isRunningWithAuth = true;
+        // Set Anonymous session to false.
+        
System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_ANONYMOUS_ALLOWED.getVarName(),
 "false");
+        
+        // Create a shiro env test.
+        shiroIni = new File("../conf/shiro.ini");
+        if (!shiroIni.exists()) {
+          shiroIni.createNewFile();
+        }
+        FileUtils.writeStringToFile(shiroIni, zeppelinShiro);
+      }
 
       // exclude org.apache.zeppelin.rinterpreter.* for scala 2.11 test
-      ZeppelinConfiguration conf = ZeppelinConfiguration.create();
       String interpreters = 
conf.getString(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS);
       String interpretersCompatibleWithScala211Test = null;
 
@@ -184,6 +224,14 @@ public abstract class AbstractTestRestApi {
       }
     }
   }
+  
+  protected static void startUpWithAuthenticationEnable() throws Exception {
+    start(true);
+  }
+  
+  protected static void startUp() throws Exception {
+    start(false);
+  }
 
   private static String getHostname() {
     try {
@@ -244,7 +292,9 @@ public abstract class AbstractTestRestApi {
       for (String setting : settingList) {
         ZeppelinServer.notebook.getInterpreterFactory().restart(setting);
       }
-
+      if (shiroIni != null) {
+        FileUtils.deleteQuietly(shiroIni);
+      }
       LOG.info("Terminating test Zeppelin...");
       ZeppelinServer.jettyWebServer.stop();
       executor.shutdown();
@@ -265,6 +315,11 @@ public abstract class AbstractTestRestApi {
       LOG.info("Test Zeppelin terminated.");
 
       
System.clearProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_INTERPRETERS.getVarName());
+      if (isRunningWithAuth) {
+        isRunningWithAuth = false;
+        System
+            
.clearProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_ANONYMOUS_ALLOWED.getVarName());
+      }
     }
   }
 
@@ -286,49 +341,97 @@ public abstract class AbstractTestRestApi {
   }
 
   protected static GetMethod httpGet(String path) throws IOException {
+    return httpGet(path, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+  
+  protected static GetMethod httpGet(String path, String user, String pwd) 
throws IOException {
     LOG.info("Connecting to {}", url + path);
     HttpClient httpClient = new HttpClient();
     GetMethod getMethod = new GetMethod(url + path);
     getMethod.addRequestHeader("Origin", url);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      getMethod.setRequestHeader("Cookie", "JSESSIONID="+ getCookie(user, 
pwd));
+    }
     httpClient.executeMethod(getMethod);
     LOG.info("{} - {}", getMethod.getStatusCode(), getMethod.getStatusText());
     return getMethod;
   }
 
   protected static DeleteMethod httpDelete(String path) throws IOException {
+    return httpDelete(path, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static DeleteMethod httpDelete(String path, String user, String 
pwd) throws IOException {
     LOG.info("Connecting to {}", url + path);
     HttpClient httpClient = new HttpClient();
     DeleteMethod deleteMethod = new DeleteMethod(url + path);
     deleteMethod.addRequestHeader("Origin", url);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      deleteMethod.setRequestHeader("Cookie", "JSESSIONID="+ getCookie(user, 
pwd));
+    }
     httpClient.executeMethod(deleteMethod);
     LOG.info("{} - {}", deleteMethod.getStatusCode(), 
deleteMethod.getStatusText());
     return deleteMethod;
   }
 
   protected static PostMethod httpPost(String path, String body) throws 
IOException {
+    return httpPost(path, body, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static PostMethod httpPost(String path, String request, String 
user, String pwd)
+      throws IOException {
     LOG.info("Connecting to {}", url + path);
     HttpClient httpClient = new HttpClient();
     PostMethod postMethod = new PostMethod(url + path);
-    postMethod.addRequestHeader("Origin", url);
-    RequestEntity entity = new ByteArrayRequestEntity(body.getBytes("UTF-8"));
-    postMethod.setRequestEntity(entity);
+    postMethod.setRequestBody(request);
+    postMethod.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      postMethod.setRequestHeader("Cookie", "JSESSIONID="+ getCookie(user, 
pwd));
+    }
     httpClient.executeMethod(postMethod);
     LOG.info("{} - {}", postMethod.getStatusCode(), 
postMethod.getStatusText());
     return postMethod;
   }
 
   protected static PutMethod httpPut(String path, String body) throws 
IOException {
+    return httpPut(path, body, StringUtils.EMPTY, StringUtils.EMPTY);
+  }
+
+  protected static PutMethod httpPut(String path, String body, String user, 
String pwd) throws IOException {
     LOG.info("Connecting to {}", url + path);
     HttpClient httpClient = new HttpClient();
     PutMethod putMethod = new PutMethod(url + path);
     putMethod.addRequestHeader("Origin", url);
     RequestEntity entity = new ByteArrayRequestEntity(body.getBytes("UTF-8"));
     putMethod.setRequestEntity(entity);
+    if (userAndPasswordAreNotBlank(user, pwd)) {
+      putMethod.setRequestHeader("Cookie", "JSESSIONID="+ getCookie(user, 
pwd));
+    }
     httpClient.executeMethod(putMethod);
     LOG.info("{} - {}", putMethod.getStatusCode(), putMethod.getStatusText());
     return putMethod;
   }
 
+  private static String getCookie(String user, String password) throws 
IOException {
+    HttpClient httpClient = new HttpClient();
+    PostMethod postMethod = new PostMethod(url + "/login");
+    postMethod.addRequestHeader("Origin", url);
+    postMethod.setParameter("password", password);
+    postMethod.setParameter("userName", user);
+    httpClient.executeMethod(postMethod);
+    LOG.info("{} - {}", postMethod.getStatusCode(), 
postMethod.getStatusText());
+    Pattern pattern = Pattern.compile("JSESSIONID=([a-zA-Z0-9-]*)");
+    java.util.regex.Matcher matcher = 
pattern.matcher(postMethod.getResponseHeaders("Set-Cookie")[0].toString());
+    return matcher.find()? matcher.group(1) : StringUtils.EMPTY;
+  }
+
+  protected static boolean userAndPasswordAreNotBlank(String user, String pwd) 
{
+    if (StringUtils.isBlank(user) && StringUtils.isBlank(pwd)) {
+      return false;
+    }
+    return true;
+  }
+  
   protected Matcher<HttpMethodBase> responsesWith(final int 
expectedStatusCode) {
     return new TypeSafeMatcher<HttpMethodBase>() {
       WeakReference<HttpMethodBase> method;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookSecurityRestApiTest.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookSecurityRestApiTest.java
 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookSecurityRestApiTest.java
new file mode 100644
index 0000000..3c5978f
--- /dev/null
+++ 
b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/NotebookSecurityRestApiTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.zeppelin.rest;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.commons.httpclient.HttpMethodBase;
+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.zeppelin.notebook.Note;
+import org.apache.zeppelin.server.ZeppelinServer;
+import org.hamcrest.Matcher;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+public class NotebookSecurityRestApiTest extends AbstractTestRestApi {
+
+  Gson gson = new Gson();
+
+  @BeforeClass
+  public static void init() throws Exception {
+    AbstractTestRestApi.startUpWithAuthenticationEnable();
+  }
+
+  @AfterClass
+  public static void destroy() throws Exception {
+    AbstractTestRestApi.shutDown();
+  }
+
+  @Before
+  public void setUp() {}
+  
+  @Test
+  public void testThatUserCanCreateAndRemoveNote() throws IOException {
+    String noteId = createNoteForUser("test", "admin", "password1");
+    assertNotNull(noteId);
+    String id = getNoteIdForUser(noteId, "admin", "password1");
+    assertThat(id, is(noteId));
+    deleteNoteForUser(noteId, "admin", "password1");
+  }
+  
+  @Test
+  public void testThatOtherUserCanAccessNoteIfPermissionNotSet() throws 
IOException {
+    String noteId = createNoteForUser("test", "admin", "password1");
+    
+    userTryGetNote(noteId, "user1", "password2", isAllowed());
+    
+    deleteNoteForUser(noteId, "admin", "password1");
+  }
+  
+  @Test
+  public void testThatOtherUserCannotAccessNoteIfPermissionSet() throws 
IOException {
+    String noteId = createNoteForUser("test", "admin", "password1");
+    
+    //set permission
+    String payload = "{ \"owners\": [\"admin\"], \"readers\": [\"user2\"], 
\"writers\": [\"user2\"] }";
+    PutMethod put = httpPut("/notebook/" + noteId + "/permissions", payload , 
"admin", "password1");
+    assertThat("test set note premission method:", put, isAllowed());
+    put.releaseConnection();
+    
+    userTryGetNote(noteId, "user1", "password2", isForbiden());
+    
+    userTryGetNote(noteId, "user2", "password3", isAllowed());
+    
+    deleteNoteForUser(noteId, "admin", "password1");
+  }
+  
+  @Test
+  public void testThatWriterCannotRemoveNote() throws IOException {
+    String noteId = createNoteForUser("test", "admin", "password1");
+    
+    //set permission
+    String payload = "{ \"owners\": [\"admin\", \"user1\"], \"readers\": 
[\"user2\"], \"writers\": [\"user2\"] }";
+    PutMethod put = httpPut("/notebook/" + noteId + "/permissions", payload , 
"admin", "password1");
+    assertThat("test set note premission method:", put, isAllowed());
+    put.releaseConnection();
+    
+    userTryRemoveNote(noteId, "user2", "password3", isForbiden());
+    userTryRemoveNote(noteId, "user1", "password2", isAllowed());
+    
+    Note deletedNote = ZeppelinServer.notebook.getNote(noteId);
+    assertNull("Deleted note should be null", deletedNote);
+  }
+  
+  private void userTryRemoveNote(String noteId, String user, String pwd, 
Matcher<? super HttpMethodBase> m) throws IOException {
+    DeleteMethod delete = httpDelete(("/notebook/" + noteId), user, pwd);
+    assertThat(delete, m);
+    delete.releaseConnection();
+  }
+  
+  private void userTryGetNote(String noteId, String user, String pwd, 
Matcher<? super HttpMethodBase> m) throws IOException {
+    GetMethod get = httpGet("/notebook/" + noteId, user, pwd);
+    assertThat(get, m);
+    get.releaseConnection();
+  }
+
+  private String getNoteIdForUser(String noteId, String user, String pwd) 
throws IOException {
+    GetMethod get = httpGet("/notebook/" + noteId, user, pwd);
+    assertThat("test note create method:", get, isAllowed());
+    Map<String, Object> resp = gson.fromJson(get.getResponseBodyAsString(), 
new TypeToken<Map<String, Object>>() {
+    }.getType());
+    get.releaseConnection();
+    return (String) ((Map<String, Object>)resp.get("body")).get("id");
+  }
+  
+  private String createNoteForUser(String noteName, String user, String pwd) 
throws IOException {
+    String jsonRequest = "{\"name\":\"" + noteName + "\"}";
+    PostMethod post = httpPost("/notebook/", jsonRequest, user, pwd);
+    assertThat("test note create method:", post, isCreated());
+    Map<String, Object> resp = gson.fromJson(post.getResponseBodyAsString(), 
new TypeToken<Map<String, Object>>() {
+    }.getType());
+    post.releaseConnection();
+    String newNoteId =  (String) resp.get("body");
+    Note newNote = ZeppelinServer.notebook.getNote(newNoteId);
+    assertNotNull("Can not find new note by id", newNote);
+    return newNoteId;
+  }
+
+  private void deleteNoteForUser(String noteId, String user, String pwd) 
throws IOException {
+    DeleteMethod delete = httpDelete(("/notebook/" + noteId), user, pwd);
+    assertThat("Test delete method:", delete, isAllowed());
+    delete.releaseConnection();
+    // make sure note is deleted
+    if (!noteId.isEmpty()) {
+      Note deletedNote = ZeppelinServer.notebook.getNote(noteId);
+      assertNull("Deleted note should be null", deletedNote);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
index 88cc4ee..b972fff 100644
--- 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
+++ 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java
@@ -344,7 +344,7 @@ public class ZeppelinConfiguration extends XMLConfiguration 
{
   public String getNotebookDir() {
     return getString(ConfVars.ZEPPELIN_NOTEBOOK_DIR);
   }
-
+  
   public String getUser() {
     return getString(ConfVars.ZEPPELIN_NOTEBOOK_S3_USER);
   }
@@ -430,6 +430,10 @@ public class ZeppelinConfiguration extends 
XMLConfiguration {
   public boolean isWindowsPath(String path){
     return path.matches("^[A-Za-z]:\\\\.*");
   }
+  
+  public boolean isAnonymousAllowed() {
+    return getBoolean(ConfVars.ZEPPELIN_ANONYMOUS_ALLOWED);
+  }
 
   public String getConfDir() {
     return getString(ConfVars.ZEPPELIN_CONF_DIR);

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/32fe23f2/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java
----------------------------------------------------------------------
diff --git 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java
 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java
index c199c96..d835c89 100644
--- 
a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java
+++ 
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java
@@ -17,18 +17,31 @@
 
 package org.apache.zeppelin.notebook;
 
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.Sets;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.zeppelin.conf.ZeppelinConfiguration;
 import org.apache.zeppelin.user.AuthenticationInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.*;
-import java.util.*;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
 
 /**
  * Contains authorization information for notes
@@ -239,6 +252,39 @@ public class NotebookAuthorization {
     return (b.isEmpty() || (intersection.size() > 0));
   }
 
+  public boolean isOwner(Set<String> userAndRoles, String noteId) {
+    if (conf.isAnonymousAllowed()) {
+      LOG.debug("Zeppelin runs in anonymous mode, everybody is owner");
+      return true;
+    }
+    if (userAndRoles == null) {
+      return false;
+    }
+    return isOwner(noteId, userAndRoles);
+  }
+  
+  public boolean hasWriteAuthorization(Set<String> userAndRoles, String 
noteId) {
+    if (conf.isAnonymousAllowed()) {
+      LOG.debug("Zeppelin runs in anonymous mode, everybody is writer");
+      return true;
+    }
+    if (userAndRoles == null) {
+      return false;
+    }
+    return isWriter(noteId, userAndRoles);
+  }
+  
+  public boolean hasReadAuthorization(Set<String> userAndRoles, String noteId) 
{
+    if (conf.isAnonymousAllowed()) {
+      LOG.debug("Zeppelin runs in anonymous mode, everybody is reader");
+      return true;
+    }
+    if (userAndRoles == null) {
+      return false;
+    }
+    return isReader(noteId, userAndRoles);
+  }
+
   public void removeNote(String noteId) {
     authInfo.remove(noteId);
     saveToFile();

Reply via email to