/*
* 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.*;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.ArrayList;
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 permission method:", put, isAllowed());
put.releaseConnection();
userTryGetNote(noteId, "user1", "password2", isForbidden());
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 permission method:", put, isAllowed());
put.releaseConnection();
userTryRemoveNote(noteId, "user2", "password3", isForbidden());
userTryRemoveNote(noteId, "user1", "password2", isAllowed());
Note deletedNote = ZeppelinServer.notebook.getNote(noteId);
assertNull("Deleted note should be null", deletedNote);
}
@Test
public void testThatUserCanSearchNote() throws IOException {
String noteId1 = createNoteForUser("test1", "admin", "password1");
createParagraphForUser(noteId1, "admin", "password1", "title1", "ThisIsToTestSearchMethodWithPermissions 1");
String noteId2 = createNoteForUser("test2", "user1", "password2");
createParagraphForUser(noteId1, "admin", "password1", "title2", "ThisIsToTestSearchMethodWithPermissions 2");
//set permission for each note
setPermissionForNote(noteId1, "admin", "password1");
setPermissionForNote(noteId1, "user1", "password2");
searchNoteBasedOnPermission("ThisIsToTestSearchMethodWithPermissions", "admin", "password1");
deleteNoteForUser(noteId1, "admin", "password1");
deleteNoteForUser(noteId2, "user1", "password2");
}
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, isAllowed());
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);
}
}
private void createParagraphForUser(String noteId, String user, String pwd, String title, String text) throws IOException {
String payload = "{\"title\": \"" + title + "\",\"text\": \"" + text + "\"}";
PostMethod post = httpPost(("/notebook/" + noteId + "/paragraph"), payload, user, pwd);
post.releaseConnection();
}
private void setPermissionForNote(String noteId, String user, String pwd) throws IOException {
String payload = "{\"owners\":[\"" + user + "\"],\"readers\":[\"" + user + "\"],\"writers\":[\"" + user + "\"]}";
PutMethod put = httpPut(("/notebook/" + noteId + "/permissions"), payload, user, pwd);
put.releaseConnection();
}
private void searchNoteBasedOnPermission(String searchText, String user, String pwd) throws IOException{
GetMethod searchNote = httpGet(("/notebook/search?q=" + searchText), user, pwd);
Map<String, Object> respSearchResult = gson.fromJson(searchNote.getResponseBodyAsString(),
new TypeToken<Map<String, Object>>() {
}.getType());
ArrayList searchBody = (ArrayList) respSearchResult.get("body");
assertEquals("At-least one search results is there", true, searchBody.size() >= 1);
for (int i = 0; i < searchBody.size(); i++) {
Map<String, String> searchResult = (Map<String, String>) searchBody.get(i);
String userId = searchResult.get("id").split("/", 2)[0];
GetMethod getPermission = httpGet(("/notebook/" + userId + "/permissions"), user, pwd);
Map<String, Object> resp = gson.fromJson(getPermission.getResponseBodyAsString(),
new TypeToken<Map<String, Object>>() {
}.getType());
Map<String, ArrayList> permissions = (Map<String, ArrayList>) resp.get("body");
ArrayList owners = permissions.get("owners");
ArrayList readers = permissions.get("readers");
ArrayList writers = permissions.get("writers");
if (owners.size() != 0 && readers.size() != 0 && writers.size() != 0) {
assertEquals("User has permissions ", true, (owners.contains(user) || readers.contains(user) ||
writers.contains(user)));
}
getPermission.releaseConnection();
}
searchNote.releaseConnection();
}
}