This is an automated email from the ASF dual-hosted git repository. zjffdu 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 b6c6124 [ZEPPELIN-4635]. Save note permission info into notebook-authorization.json b6c6124 is described below commit b6c6124d44c096a924f4efbcc9a785b8cd54e253 Author: Jeff Zhang <zjf...@apache.org> AuthorDate: Mon Feb 24 13:19:18 2020 +0800 [ZEPPELIN-4635]. Save note permission info into notebook-authorization.json ### What is this PR for? This PR is to revert the changes of ZEPPELIN-3985. Because it would cause the issue of ZEPPELIN-4612. In this PR I will make Zeppelin still save note permission info into `notebook-authorization.json`. But I also do some code refactoring, and we could store the permission info into other storage such as database in future. Because storing them into one file also has potential issue. such as scale issue, now each time we have to write all notes' permission info into file instead of in [...] ### What type of PR is it? [Bug Fix | Refactoring] ### Todos * [ ] - Task ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-4635 ### How should this be tested? * CI pass ### Screenshots (if appropriate) ### Questions: * Does the licenses files need update? NO * Is there breaking changes for older versions? No * Does this needs documentation? No Author: Jeff Zhang <zjf...@apache.org> Closes #3668 from zjffdu/ZEPPELIN-4635 and squashes the following commits: 01bd6453b [Jeff Zhang] [ZEPPELIN-4635]. Save note permission info into notebook-authorization.json --- .../zeppelin/cluster/event/ClusterEvent.java | 1 + .../zeppelin/conf/ZeppelinConfiguration.java | 2 +- .../notebook/repo/FileSystemNotebookRepo.java | 7 +- .../org/apache/zeppelin/rest/NotebookRestApi.java | 2 +- .../org/apache/zeppelin/server/ZeppelinServer.java | 2 + .../apache/zeppelin/service/NotebookService.java | 9 +- .../apache/zeppelin/socket/ConnectionManager.java | 1 - .../org/apache/zeppelin/socket/NotebookServer.java | 6 +- .../zeppelin/service/NotebookServiceTest.java | 24 +- .../zeppelin/notebook/AuthorizationService.java | 331 +++++++-------- .../java/org/apache/zeppelin/notebook/Note.java | 102 +---- .../org/apache/zeppelin/notebook/NoteAuth.java | 154 +++++++ .../org/apache/zeppelin/notebook/NoteManager.java | 7 +- .../org/apache/zeppelin/notebook/Notebook.java | 65 ++- .../zeppelin/notebook/NotebookAuthorization.java | 444 --------------------- .../notebook/NotebookAuthorizationInfoSaving.java | 11 +- .../zeppelin/notebook/repo/NotebookRepo.java | 8 +- .../zeppelin/notebook/repo/NotebookRepoSync.java | 36 +- .../notebook/repo/UpgradeNoteFileTool.java | 1 - .../zeppelin/notebook/repo/VFSNotebookRepo.java | 12 +- .../zeppelin/conf/ZeppelinConfigurationTest.java | 3 +- .../helium/HeliumApplicationFactoryTest.java | 5 + .../org/apache/zeppelin/notebook/NotebookTest.java | 26 +- .../notebook/repo/NotebookRepoSyncTest.java | 32 +- .../apache/zeppelin/search/LuceneSearchTest.java | 4 +- 25 files changed, 497 insertions(+), 798 deletions(-) diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/cluster/event/ClusterEvent.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/cluster/event/ClusterEvent.java index 9772b54..559894a 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/cluster/event/ClusterEvent.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/cluster/event/ClusterEvent.java @@ -31,6 +31,7 @@ public enum ClusterEvent { UPDATE_NOTE_PERMISSIONS, // CLUSTER_AUTH_EVENT_TOPIC SET_ROLES, + // (TODO) Consolidate the permission related events into one event SET_READERS_PERMISSIONS, SET_RUNNERS_PERMISSIONS, SET_WRITERS_PERMISSIONS, diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 2319606..dbdb614 100644 --- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -394,7 +394,7 @@ public class ZeppelinConfiguration extends XMLConfiguration { } public String getNotebookDir() { - return getString(ConfVars.ZEPPELIN_NOTEBOOK_DIR); + return getRelativeDir(ConfVars.ZEPPELIN_NOTEBOOK_DIR); } public String getNotebookRunId() { diff --git a/zeppelin-plugins/notebookrepo/filesystem/src/main/java/org/apache/zeppelin/notebook/repo/FileSystemNotebookRepo.java b/zeppelin-plugins/notebookrepo/filesystem/src/main/java/org/apache/zeppelin/notebook/repo/FileSystemNotebookRepo.java index f486451..d2c5cdd 100644 --- a/zeppelin-plugins/notebookrepo/filesystem/src/main/java/org/apache/zeppelin/notebook/repo/FileSystemNotebookRepo.java +++ b/zeppelin-plugins/notebookrepo/filesystem/src/main/java/org/apache/zeppelin/notebook/repo/FileSystemNotebookRepo.java @@ -69,7 +69,6 @@ public class FileSystemNotebookRepo implements NotebookRepo { return noteInfos; } - @Override public Note get(String noteId, String notePath, AuthenticationInfo subject) throws IOException { String content = this.fs.readFile( @@ -122,18 +121,18 @@ public class FileSystemNotebookRepo implements NotebookRepo { @Override public void close() { - LOGGER.warn("close is not implemented for HdfsNotebookRepo"); + LOGGER.warn("close is not implemented for FileSystemNotebookRepo"); } @Override public List<NotebookRepoSettingsInfo> getSettings(AuthenticationInfo subject) { - LOGGER.warn("getSettings is not implemented for HdfsNotebookRepo"); + LOGGER.warn("getSettings is not implemented for FileSystemNotebookRepo"); return null; } @Override public void updateSettings(Map<String, String> settings, AuthenticationInfo subject) { - LOGGER.warn("updateSettings is not implemented for HdfsNotebookRepo"); + LOGGER.warn("updateSettings is not implemented for FileSystemNotebookRepo"); } } 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 d0d7cb3..ab72ec4 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 @@ -295,7 +295,7 @@ public class NotebookRestApi extends AbstractRestApi { authorizationService.getReaders(noteId), authorizationService.getRunners(noteId), authorizationService.getWriters(noteId)); AuthenticationInfo subject = new AuthenticationInfo(authenticationService.getPrincipal()); - notebook.saveNote(note, subject); + authorizationService.saveNoteAuth(noteId, subject); notebookServer.broadcastNote(note); notebookServer.broadcastNoteList(subject, userAndRoles); return new JsonResponse<>(Status.OK).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 0c9b1cf..b231fd4 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 @@ -51,6 +51,7 @@ import org.apache.zeppelin.interpreter.InterpreterSettingManager; import org.apache.zeppelin.interpreter.recovery.RecoveryStorage; import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener; import org.apache.zeppelin.notebook.NoteEventListener; +import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.AuthorizationService; import org.apache.zeppelin.notebook.Paragraph; @@ -164,6 +165,7 @@ public class ZeppelinServer extends ResourceConfig { bindAsContract(AdminService.class).in(Singleton.class); bindAsContract(AuthorizationService.class).in(Singleton.class); bindAsContract(ConnectionManager.class).in(Singleton.class); + bindAsContract(NoteManager.class).in(Singleton.class); // TODO(jl): Will make it more beautiful if (!StringUtils.isBlank(conf.getShiroPath())) { bind(ShiroAuthenticationService.class).to(AuthenticationService.class).in(Singleton.class); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java index b6dda71..e6a7cab 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/service/NotebookService.java @@ -39,8 +39,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.display.AngularObject; import org.apache.zeppelin.display.AngularObjectRegistry; -import org.apache.zeppelin.interpreter.Interpreter; -import org.apache.zeppelin.interpreter.InterpreterNotFoundException; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion; @@ -50,7 +48,6 @@ import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.notebook.AuthorizationService; -import org.apache.zeppelin.notebook.ParagraphTextParser; import org.apache.zeppelin.notebook.repo.NotebookRepoWithVersionControl; import org.apache.zeppelin.notebook.scheduler.SchedulerService; import org.apache.zeppelin.notebook.socket.Message; @@ -150,7 +147,7 @@ public class NotebookService { try { Note note = notebook.createNote(normalizeNotePath(notePath), defaultInterpreterGroup, - context.getAutheInfo()); + context.getAutheInfo(), false); // it's an empty note. so add one paragraph note.addNewParagraph(context.getAutheInfo()); notebook.saveNote(note, context.getAutheInfo()); @@ -928,8 +925,8 @@ public class NotebookService { } public void moveNoteToTrash(String noteId, - ServiceContext context, - ServiceCallback<Note> callback) throws IOException { + ServiceContext context, + ServiceCallback<Note> callback) throws IOException { Note note = notebook.getNote(noteId); if (note == null) { callback.onFailure(new NoteNotFoundException(noteId), context); diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/ConnectionManager.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/ConnectionManager.java index 489a59f..906ee0f 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/ConnectionManager.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/ConnectionManager.java @@ -28,7 +28,6 @@ import org.apache.zeppelin.display.GUI; import org.apache.zeppelin.display.Input; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; -import org.apache.zeppelin.notebook.NotebookAuthorization; import org.apache.zeppelin.notebook.NotebookImportDeserializer; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.notebook.AuthorizationService; 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 b177f64..c0c6bd6 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 @@ -635,8 +635,9 @@ public class NotebookServer extends WebSocketServlet subject = new AuthenticationInfo(StringUtils.EMPTY); } //send first to requesting user + AuthorizationService authorizationService = getNotebookAuthorizationService(); List<NoteInfo> notesInfo = getNotebook().getNotesInfo( - noteId -> getNotebookAuthorizationService().isReader(noteId, userAndRoles)); + noteId -> authorizationService.isReader(noteId, userAndRoles)); Message message = new Message(OP.NOTES_INFO).put("notes", notesInfo); getConnectionManager().multicastToUser(subject.getUser(), message); //to others afterwards @@ -1983,7 +1984,8 @@ public class NotebookServer extends WebSocketServlet } List<InterpreterSetting> intpSettings = - note.getBindedInterpreterSettings(new ArrayList<>(note.getOwners())); + note.getBindedInterpreterSettings( + new ArrayList<>(getNotebookAuthorizationService().getOwners(note.getId()))); if (intpSettings.isEmpty()) { continue; } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java index 9fefd57..1d0b773 100644 --- a/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/service/NotebookServiceTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; @@ -40,6 +41,7 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; +import com.google.common.io.Files; import org.apache.commons.lang.StringUtils; import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.interpreter.Interpreter; @@ -53,16 +55,19 @@ import org.apache.zeppelin.interpreter.ManagedInterpreterGroup; import org.apache.zeppelin.notebook.AuthorizationService; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; +import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.notebook.repo.InMemoryNotebookRepo; import org.apache.zeppelin.notebook.repo.NotebookRepo; +import org.apache.zeppelin.notebook.repo.VFSNotebookRepo; import org.apache.zeppelin.notebook.scheduler.QuartzSchedulerService; import org.apache.zeppelin.notebook.scheduler.SchedulerService; import org.apache.zeppelin.search.LuceneSearch; import org.apache.zeppelin.search.SearchService; import org.apache.zeppelin.user.AuthenticationInfo; import org.apache.zeppelin.user.Credentials; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -74,6 +79,7 @@ public class NotebookServiceTest { private static NotebookService notebookService; + private File notebookDir; private ServiceContext context = new ServiceContext(AuthenticationInfo.ANONYMOUS, new HashSet<>()); @@ -84,8 +90,12 @@ public class NotebookServiceTest { @Before public void setUp() throws Exception { + notebookDir = Files.createTempDir().getAbsoluteFile(); + System.setProperty(ZeppelinConfiguration.ConfVars.ZEPPELIN_NOTEBOOK_DIR.getVarName(), + notebookDir.getAbsolutePath()); ZeppelinConfiguration zeppelinConfiguration = ZeppelinConfiguration.create(); - NotebookRepo notebookRepo = new InMemoryNotebookRepo(); + NotebookRepo notebookRepo = new VFSNotebookRepo(); + notebookRepo.init(zeppelinConfiguration); InterpreterSettingManager mockInterpreterSettingManager = mock(InterpreterSettingManager.class); InterpreterFactory mockInterpreterFactory = mock(InterpreterFactory.class); @@ -106,17 +116,20 @@ public class NotebookServiceTest { when(mockInterpreterSetting.getStatus()).thenReturn(InterpreterSetting.Status.READY); SearchService searchService = new LuceneSearch(zeppelinConfiguration); Credentials credentials = new Credentials(false, null, null); + NoteManager noteManager = new NoteManager(notebookRepo); + AuthorizationService authorizationService = new AuthorizationService(zeppelinConfiguration); Notebook notebook = new Notebook( zeppelinConfiguration, + authorizationService, notebookRepo, + noteManager, mockInterpreterFactory, mockInterpreterSettingManager, searchService, credentials, null); - AuthorizationService authorizationService = - new AuthorizationService(notebook, notebook.getConf()); + SchedulerService schedulerService = new QuartzSchedulerService(zeppelinConfiguration, notebook); notebookService = new NotebookService( @@ -128,6 +141,11 @@ public class NotebookServiceTest { .thenReturn(mockInterpreterSetting); } + @After + public void tearDown() { + notebookDir.delete(); + } + @Test public void testNoteOperations() throws IOException { // get home note diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/AuthorizationService.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/AuthorizationService.java index 75c3a0b..9235828 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/AuthorizationService.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/AuthorizationService.java @@ -17,8 +17,6 @@ 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.reflect.TypeToken; @@ -28,6 +26,7 @@ import org.apache.zeppelin.cluster.event.ClusterEvent; import org.apache.zeppelin.cluster.event.ClusterEventListener; import org.apache.zeppelin.cluster.event.ClusterMessage; import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.storage.ConfigStorage; import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,7 +35,6 @@ import javax.inject.Inject; import java.io.IOException; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -50,17 +48,63 @@ public class AuthorizationService implements ClusterEventListener { private static final Set<String> EMPTY_SET = new HashSet<>(); private ZeppelinConfiguration conf; - private Notebook notebook; + private ConfigStorage configStorage; + // contains roles for each user (username --> roles) private Map<String, Set<String>> userRoles = new HashMap<>(); + // cached note permission info. (noteId --> NoteAuth) + private Map<String, NoteAuth> notesAuth = new HashMap<>(); + @Inject - public AuthorizationService(Notebook notebook, ZeppelinConfiguration conf) { - this.notebook = notebook; + public AuthorizationService(ZeppelinConfiguration conf) { this.conf = conf; + try { + this.configStorage = ConfigStorage.getInstance(conf); + // init notesAuth by reading notebook-authorization.json + NotebookAuthorizationInfoSaving authorizationInfoSaving = configStorage.loadNotebookAuthorization(); + if (authorizationInfoSaving != null) { + for (Map.Entry<String, Map<String, Set<String>>> entry : authorizationInfoSaving.authInfo.entrySet()) { + String noteId = entry.getKey(); + Map<String, Set<String>> permissions = entry.getValue(); + notesAuth.put(noteId, new NoteAuth(noteId, permissions)); + } + } + } catch (IOException e) { + throw new RuntimeException("Fail to create ConfigStorage", e); + } } - private Set<String> validateUser(Set<String> users) { + /** + * Create NoteAuth, this method only create NoteAuth in memory, you need to call method + * saveNoteAuth to persistent it to storage. + * @param noteId + * @param subject + * @throws IOException + */ + public void createNoteAuth(String noteId, AuthenticationInfo subject) throws IOException { + NoteAuth noteAuth = new NoteAuth(noteId, subject); + this.notesAuth.put(noteId, noteAuth); + } + + public void cloneNoteMeta(String noteId, String sourceNoteId, AuthenticationInfo subject) throws IOException { + NoteAuth noteAuth = new NoteAuth(noteId, subject); + this.notesAuth.put(noteId, noteAuth); + } + + /** + * Persistent NoteAuth + * + * @param noteId + * @param subject + * @throws IOException + */ + public void saveNoteAuth(String noteId, AuthenticationInfo subject) throws IOException { + configStorage.save(new NotebookAuthorizationInfoSaving(this.notesAuth)); + } + + // skip empty user and remove the white space around user name. + private Set<String> normalizeUsers(Set<String> users) { Set<String> returnUser = new HashSet<>(); for (String user : users) { if (!user.trim().isEmpty()) { @@ -71,164 +115,167 @@ public class AuthorizationService implements ClusterEventListener { } public void setOwners(String noteId, Set<String> entities) throws IOException { - inlineSetOwners(noteId, entities); - broadcastClusterEvent(ClusterEvent.SET_OWNERS_PERMISSIONS, noteId, null, entities); - } - - private void inlineSetOwners(String noteId, Set<String> entities) throws IOException { - entities = validateUser(entities); - notebook.getNote(noteId).setOwners(entities); + setOwners(noteId, entities, true); } public void setReaders(String noteId, Set<String> entities) throws IOException { - inlineSetReaders(noteId, entities); - broadcastClusterEvent(ClusterEvent.SET_READERS_PERMISSIONS, noteId, null, entities); + setReaders(noteId, entities, true); } - private void inlineSetReaders(String noteId, Set<String> entities) throws IOException { - entities = validateUser(entities); - notebook.getNote(noteId).setReaders(entities); + public void setWriters(String noteId, Set<String> entities) throws IOException { + setWriters(noteId, entities, true); } public void setRunners(String noteId, Set<String> entities) throws IOException { - inlineSetRunners(noteId, entities); - broadcastClusterEvent(ClusterEvent.SET_RUNNERS_PERMISSIONS, noteId, null, entities); + setRunners(noteId, entities, true); } - private void inlineSetRunners(String noteId, Set<String> entities) throws IOException { - entities = validateUser(entities); - notebook.getNote(noteId).setRunners(entities); + public void setRoles(String user, Set<String> roles) { + setRoles(user, roles, true); } - public void setWriters(String noteId, Set<String> entities) throws IOException { - inlineSetWriters(noteId, entities); - broadcastClusterEvent(ClusterEvent.SET_WRITERS_PERMISSIONS, noteId, null, entities); + public void clearPermission(String noteId) throws IOException { + clearPermission(noteId, true); + } + + public void setOwners(String noteId, Set<String> entities, boolean broadcast) throws IOException { + entities = normalizeUsers(entities); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + throw new IOException("No note found for noteId: " + noteId); + } + noteAuth.setOwners(entities); + if (broadcast) { + broadcastClusterEvent(ClusterEvent.SET_OWNERS_PERMISSIONS, noteId, null, entities); + } + } + + public void setReaders(String noteId, Set<String> entities, boolean broadcast) throws IOException { + entities = normalizeUsers(entities); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + throw new IOException("No note found for noteId: " + noteId); + } + noteAuth.setReaders(entities); + if (broadcast) { + broadcastClusterEvent(ClusterEvent.SET_READERS_PERMISSIONS, noteId, null, entities); + } } - private void inlineSetWriters(String noteId, Set<String> entities) throws IOException { - entities = validateUser(entities); - notebook.getNote(noteId).setWriters(entities); + public void setRunners(String noteId, Set<String> entities, boolean broadcast) throws IOException { + entities = normalizeUsers(entities); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + throw new IOException("No note found for noteId: " + noteId); + } + noteAuth.setRunners(entities); + if (broadcast) { + broadcastClusterEvent(ClusterEvent.SET_RUNNERS_PERMISSIONS, noteId, null, entities); + } + } + + public void setWriters(String noteId, Set<String> entities, boolean broadcast) throws IOException { + entities = normalizeUsers(entities); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + throw new IOException("No note found for noteId: " + noteId); + } + noteAuth.setWriters(entities); + if (broadcast) { + broadcastClusterEvent(ClusterEvent.SET_WRITERS_PERMISSIONS, noteId, null, entities); + } + } + + public void setRoles(String user, Set<String> roles, boolean broadcast) { + if (StringUtils.isBlank(user)) { + LOGGER.warn("Setting roles for empty user"); + return; + } + roles = normalizeUsers(roles); + userRoles.put(user, roles); + if (broadcast) { + broadcastClusterEvent(ClusterEvent.SET_ROLES, null, user, roles); + } + } + + public void clearPermission(String noteId, boolean broadcast) throws IOException { + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + throw new IOException("No note found for noteId: " + noteId); + } + noteAuth.setReaders(Sets.newHashSet()); + noteAuth.setRunners(Sets.newHashSet()); + noteAuth.setWriters(Sets.newHashSet()); + noteAuth.setOwners(Sets.newHashSet()); + + if (broadcast) { + broadcastClusterEvent(ClusterEvent.CLEAR_PERMISSION, noteId, null, null); + } } public Set<String> getOwners(String noteId) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return EMPTY_SET; - } - return note.getOwners(); - } catch (IOException e) { - LOGGER.warn("Fail to getOwner for note: " + noteId, e); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + LOGGER.warn("No note found for noteId: " + noteId); return EMPTY_SET; } + return noteAuth.getOwners(); } public Set<String> getReaders(String noteId) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return EMPTY_SET; - } - return note.getReaders(); - } catch (IOException e) { - LOGGER.warn("Fail to getReaders for note: " + noteId, e); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + LOGGER.warn("No note found for noteId: " + noteId); return EMPTY_SET; } + return noteAuth.getReaders(); } public Set<String> getRunners(String noteId) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return EMPTY_SET; - } - return note.getRunners(); - } catch (IOException e) { - LOGGER.warn("Fail to getRunners for note: " + noteId, e); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + LOGGER.warn("No note found for noteId: " + noteId); return EMPTY_SET; } + return noteAuth.getRunners(); } public Set<String> getWriters(String noteId) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return EMPTY_SET; - } - return note.getWriters(); - } catch (IOException e) { - LOGGER.warn("Fail to getWriters for note: " + noteId, e); + NoteAuth noteAuth = notesAuth.get(noteId); + if (noteAuth == null) { + LOGGER.warn("No note found for noteId: " + noteId); return EMPTY_SET; } + return noteAuth.getWriters(); + } + + public Set<String> getRoles(String user) { + return userRoles.getOrDefault(user, Sets.newHashSet()); } public boolean isOwner(String noteId, Set<String> entities) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return false; - } - return isMember(entities, note.getOwners()) || isAdmin(entities); - } catch (IOException e) { - LOGGER.warn("Fail to check isOwner for note: " + noteId, e); - return false; - } + return isMember(entities, getOwners(noteId)) || isAdmin(entities); } public boolean isWriter(String noteId, Set<String> entities) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return false; - } - return isMember(entities, note.getWriters()) || - isMember(entities, note.getOwners()) || - isAdmin(entities); - } catch (IOException e) { - LOGGER.warn("Fail to check isWriter for note: " + noteId, e); - return false; - } + return isMember(entities, getWriters(noteId)) || + isMember(entities, getOwners(noteId)) || + isAdmin(entities); } public boolean isReader(String noteId, Set<String> entities) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return false; - } - return isMember(entities, note.getReaders()) || - isMember(entities, note.getOwners()) || - isMember(entities, note.getWriters()) || - isMember(entities, note.getRunners()) || - isAdmin(entities); - } catch (IOException e) { - LOGGER.warn("Fail to check isReader for note: " + noteId, e); - return false; - } + return isMember(entities, getReaders(noteId)) || + isMember(entities, getOwners(noteId)) || + isMember(entities, getWriters(noteId)) || + isMember(entities, getRunners(noteId)) || + isAdmin(entities); } public boolean isRunner(String noteId, Set<String> entities) { - try { - Note note = notebook.getNote(noteId); - if (note == null) { - LOGGER.warn("Note " + noteId + " not found"); - return false; - } - return isMember(entities, note.getRunners()) || - isMember(entities, note.getWriters()) || - isMember(entities, note.getOwners()) || - isAdmin(entities); - } catch (IOException e) { - LOGGER.warn("Fail to check isRunner for note: " + noteId, e); - return false; - } + return isMember(entities, getRunners(noteId)) || + isMember(entities, getWriters(noteId)) || + isMember(entities, getOwners(noteId)) || + isAdmin(entities); } private boolean isAdmin(Set<String> entities) { @@ -295,40 +342,6 @@ public class AuthorizationService implements ClusterEventListener { return conf.isNotebookPublic(); } - public void setRoles(String user, Set<String> roles) { - inlineSetRoles(user, roles); - broadcastClusterEvent(ClusterEvent.SET_ROLES, null, user, roles); - } - - private void inlineSetRoles(String user, Set<String> roles) { - if (StringUtils.isBlank(user)) { - LOGGER.warn("Setting roles for empty user"); - return; - } - roles = validateUser(roles); - userRoles.put(user, roles); - } - - public Set<String> getRoles(String user) { - Set<String> roles = Sets.newHashSet(); - if (userRoles.containsKey(user)) { - roles.addAll(userRoles.get(user)); - } - return roles; - } - - public void clearPermission(String noteId) throws IOException { - inlineClearPermission(noteId); - broadcastClusterEvent(ClusterEvent.CLEAR_PERMISSION, noteId, null, null); - } - - public void inlineClearPermission(String noteId) throws IOException { - notebook.getNote(noteId).setReaders(Sets.newHashSet()); - notebook.getNote(noteId).setRunners(Sets.newHashSet()); - notebook.getNote(noteId).setWriters(Sets.newHashSet()); - notebook.getNote(noteId).setOwners(Sets.newHashSet()); - } - @Override public void onClusterEvent(String msg) { if (LOGGER.isDebugEnabled()) { @@ -347,22 +360,22 @@ public class AuthorizationService implements ClusterEventListener { try { switch (message.clusterEvent) { case SET_READERS_PERMISSIONS: - inlineSetReaders(noteId, set); + setReaders(noteId, set, false); break; case SET_WRITERS_PERMISSIONS: - inlineSetWriters(noteId, set); + setWriters(noteId, set, false); break; case SET_OWNERS_PERMISSIONS: - inlineSetOwners(noteId, set); + setOwners(noteId, set, false); break; case SET_RUNNERS_PERMISSIONS: - inlineSetRunners(noteId, set); + setRunners(noteId, set, false); break; case SET_ROLES: - inlineSetRoles(user, set); + setRoles(user, set, false); break; case CLEAR_PERMISSION: - inlineClearPermission(noteId); + clearPermission(noteId, false); break; default: LOGGER.error("Unknown clusterEvent:{}, msg:{} ", message.clusterEvent, msg); diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java index ab7016a..777b47d 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java @@ -95,9 +95,7 @@ public class Note implements JsonSerializable { private String id; private String defaultInterpreterGroup; private String version; - // permissions -> users - // e.g. "owners" -> {"u1"}, "readers" -> {"u1", "u2"} - private Map<String, Set<String>> permissions = new HashMap<>(); + private Map<String, Object> noteParams = new LinkedHashMap<>(); private Map<String, Input> noteForms = new LinkedHashMap<>(); private Map<String, List<AngularObject>> angularObjects = new HashMap<>(); @@ -244,103 +242,6 @@ public class Note implements JsonSerializable { this.defaultInterpreterGroup = defaultInterpreterGroup; } - // used when creating new note - public void initPermissions(AuthenticationInfo subject) { - if (!AuthenticationInfo.isAnonymous(subject)) { - if (ZeppelinConfiguration.create().isNotebookPublic()) { - // add current user to owners - can be public - Set<String> owners = getOwners(); - owners.add(subject.getUser()); - setOwners(owners); - } else { - // add current user to owners, readers, runners, writers - private note - Set<String> entities = getOwners(); - entities.add(subject.getUser()); - setOwners(entities); - entities = getReaders(); - entities.add(subject.getUser()); - setReaders(entities); - entities = getRunners(); - entities.add(subject.getUser()); - setRunners(entities); - entities = getWriters(); - entities.add(subject.getUser()); - setWriters(entities); - } - } - } - - public void setOwners(Set<String> entities) { - permissions.put("owners", entities); - } - - public Set<String> getOwners() { - Set<String> owners = permissions.get("owners"); - if (owners == null) { - owners = new HashSet<>(); - } else { - owners = checkCaseAndConvert(owners); - } - return owners; - } - - public Set<String> getReaders() { - Set<String> readers = permissions.get("readers"); - if (readers == null) { - readers = new HashSet<>(); - } else { - readers = checkCaseAndConvert(readers); - } - return readers; - } - - public void setReaders(Set<String> entities) { - permissions.put("readers", entities); - } - - public Set<String> getRunners() { - Set<String> runners = permissions.get("runners"); - if (runners == null) { - runners = new HashSet<>(); - } else { - runners = checkCaseAndConvert(runners); - } - return runners; - } - - public void setRunners(Set<String> entities) { - permissions.put("runners", entities); - } - - public Set<String> getWriters() { - Set<String> writers = permissions.get("writers"); - if (writers == null) { - writers = new HashSet<>(); - } else { - writers = checkCaseAndConvert(writers); - } - return writers; - } - - public void setWriters(Set<String> entities) { - permissions.put("writers", entities); - } - - /* - * If case conversion is enforced, then change entity names to lower case - */ - private Set<String> checkCaseAndConvert(Set<String> entities) { - if (ZeppelinConfiguration.create().isUsernameForceLowerCase()) { - Set<String> set2 = new HashSet<String>(); - for (String name : entities) { - set2.add(name.toLowerCase()); - } - return set2; - } else { - return entities; - } - } - public Map<String, Object> getNoteParams() { return noteParams; } @@ -359,6 +260,7 @@ public class Note implements JsonSerializable { public void setName(String name) { this.name = name; + // for the notes before 0.9, get path from name. if (this.path == null) { if (name.startsWith("/")) { this.path = name; diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteAuth.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteAuth.java new file mode 100644 index 0000000..7f6c84b --- /dev/null +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteAuth.java @@ -0,0 +1,154 @@ +/* + * 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.notebook; + +import com.google.common.collect.Sets; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.user.AuthenticationInfo; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * Represent note authorization info, including (readers, writers, runners, owners) + * + */ +public class NoteAuth { + + private static Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + private String noteId; + private Set<String> readers = new HashSet<>(); + private Set<String> writers = new HashSet<>(); + private Set<String> runners = new HashSet<>(); + private Set<String> owners = new HashSet<>(); + + public NoteAuth(String noteId) { + this(noteId, AuthenticationInfo.ANONYMOUS); + } + + public NoteAuth(String noteId, AuthenticationInfo subject) { + this.noteId = noteId; + initPermissions(subject); + } + + public NoteAuth(String noteId, Map<String, Set<String>> permissions) { + this.noteId = noteId; + this.readers = permissions.getOrDefault("readers", Sets.newHashSet()); + this.writers = permissions.getOrDefault("writers", Sets.newHashSet()); + this.runners = permissions.getOrDefault("runners", Sets.newHashSet()); + this.owners = permissions.getOrDefault("owners", Sets.newHashSet()); + } + + // used when creating new note + public void initPermissions(AuthenticationInfo subject) { + if (!AuthenticationInfo.isAnonymous(subject)) { + if (ZeppelinConfiguration.create().isNotebookPublic()) { + // add current user to owners - can be public + this.owners.add(checkCaseAndConvert(subject.getUser())); + } else { + // add current user to owners, readers, runners, writers - private note + this.owners.add(checkCaseAndConvert(subject.getUser())); + this.readers.add(checkCaseAndConvert(subject.getUser())); + this.writers.add(checkCaseAndConvert(subject.getUser())); + this.runners.add(checkCaseAndConvert(subject.getUser())); + } + } + } + + public String getNoteId() { + return noteId; + } + + public void setOwners(Set<String> entities) { + this.owners = checkCaseAndConvert(entities); + } + + public void setReaders(Set<String> entities) { + this.readers = checkCaseAndConvert(entities); + } + + public void setWriters(Set<String> entities) { + this.writers = checkCaseAndConvert(entities); + } + + public void setRunners(Set<String> entities) { + this.runners = checkCaseAndConvert(entities); + } + + public Set<String> getOwners() { + return this.owners; + } + + public Set<String> getReaders() { + return this.readers; + } + + public Set<String> getWriters() { + return this.writers; + } + + public Set<String> getRunners() { + return this.runners; + } + + /* + * If case conversion is enforced, then change entity names to lower case + */ + private Set<String> checkCaseAndConvert(Set<String> entities) { + if (ZeppelinConfiguration.create().isUsernameForceLowerCase()) { + Set<String> set2 = new HashSet<String>(); + for (String name : entities) { + set2.add(name.toLowerCase()); + } + return set2; + } else { + return entities; + } + } + + private String checkCaseAndConvert(String entity) { + if (ZeppelinConfiguration.create().isUsernameForceLowerCase()) { + return entity.toLowerCase(); + } else { + return entity; + } + } + + public Map<String, Set<String>> toMap() { + Map<String, Set<String>> map = new HashMap<>(); + map.put("readers", readers); + map.put("writers", writers); + map.put("runners", runners); + map.put("owners", owners); + return map; + } + + public String toJson() { + return gson.toJson(this); + } + + public static NoteAuth fromJson(String json) { + return gson.fromJson(json, NoteAuth.class); + } + +} diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteManager.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteManager.java index 9e06750..c260208 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteManager.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NoteManager.java @@ -21,11 +21,12 @@ package org.apache.zeppelin.notebook; import org.apache.commons.lang3.StringUtils; import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.notebook.repo.NotebookRepo; -import org.apache.zeppelin.scheduler.Job; import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; +import javax.inject.Singleton; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -47,6 +48,7 @@ import java.util.stream.Collectors; * TODO(zjffdu) implement the lifecycle manager of Note * (release memory if note is not used for some period) */ +@Singleton public class NoteManager { private static final Logger LOGGER = LoggerFactory.getLogger(NoteManager.class); public static String TRASH_FOLDER = "~Trash"; @@ -57,6 +59,7 @@ public class NoteManager { // noteId -> notePath private Map<String, String> notesInfo; + @Inject public NoteManager(NotebookRepo notebookRepo) throws IOException { this.notebookRepo = notebookRepo; this.root = new Folder("/", notebookRepo); @@ -171,7 +174,6 @@ public class NoteManager { public void addNote(Note note, AuthenticationInfo subject) throws IOException { addOrUpdateNoteNode(note, true); - this.notebookRepo.save(note, subject); note.setLoaded(true); } @@ -222,7 +224,6 @@ public class NoteManager { this.notebookRepo.move(noteId, notePath, newNotePath, subject); } - public void moveFolder(String folderPath, String newFolderPath, AuthenticationInfo subject) throws IOException { diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java index c12790e..3e478d5 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java @@ -24,7 +24,6 @@ import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -64,8 +63,8 @@ import org.slf4j.LoggerFactory; public class Notebook { private static final Logger LOGGER = LoggerFactory.getLogger(Notebook.class); + private AuthorizationService authorizationService; private NoteManager noteManager; - private InterpreterFactory replFactory; private InterpreterSettingManager interpreterSettingManager; private ZeppelinConfiguration conf; @@ -83,14 +82,17 @@ public class Notebook { */ public Notebook( ZeppelinConfiguration conf, + AuthorizationService authorizationService, NotebookRepo notebookRepo, + NoteManager noteManager, InterpreterFactory replFactory, InterpreterSettingManager interpreterSettingManager, SearchService noteSearchService, Credentials credentials) throws IOException { - this.noteManager = new NoteManager(notebookRepo); this.conf = conf; + this.authorizationService = authorizationService; + this.noteManager = noteManager; this.notebookRepo = notebookRepo; this.replFactory = replFactory; this.interpreterSettingManager = interpreterSettingManager; @@ -106,7 +108,9 @@ public class Notebook { @Inject public Notebook( ZeppelinConfiguration conf, + AuthorizationService authorizationService, NotebookRepo notebookRepo, + NoteManager noteManager, InterpreterFactory replFactory, InterpreterSettingManager interpreterSettingManager, SearchService noteSearchService, @@ -115,7 +119,9 @@ public class Notebook { throws IOException { this( conf, + authorizationService, notebookRepo, + noteManager, replFactory, interpreterSettingManager, noteSearchService, @@ -126,6 +132,10 @@ public class Notebook { this.paragraphJobListener = (ParagraphJobListener) noteEventListener; } + public NoteManager getNoteManager() { + return noteManager; + } + /** * This method will be called only NotebookService to register {@link * * org.apache.zeppelin.notebook.ParagraphJobListener}. @@ -150,6 +160,23 @@ public class Notebook { } /** + * Creating new note. defaultInterpreterGroup is not provided, so the global + * defaultInterpreterGroup (zeppelin.interpreter.group.default) is used + * + * @param notePath + * @param subject + * @param save + * @return + * @throws IOException + */ + public Note createNote(String notePath, + AuthenticationInfo subject, + boolean save) throws IOException { + return createNote(notePath, interpreterSettingManager.getDefaultInterpreterSetting().getName(), + subject, save); + } + + /** * Creating new note. * * @param notePath @@ -161,11 +188,32 @@ public class Notebook { public Note createNote(String notePath, String defaultInterpreterGroup, AuthenticationInfo subject) throws IOException { + return createNote(notePath, defaultInterpreterGroup, subject, true); + } + + /** + * Creating new note. + * + * @param notePath + * @param defaultInterpreterGroup + * @param subject + * @return + * @throws IOException + */ + public Note createNote(String notePath, + String defaultInterpreterGroup, + AuthenticationInfo subject, + boolean save) throws IOException { Note note = - new Note(notePath, defaultInterpreterGroup, replFactory, interpreterSettingManager, - paragraphJobListener, credentials, noteEventListeners); - note.initPermissions(subject); + new Note(notePath, defaultInterpreterGroup, replFactory, interpreterSettingManager, + paragraphJobListener, credentials, noteEventListeners); noteManager.addNote(note, subject); + // init noteMeta + authorizationService.createNoteAuth(note.getId(), subject); + authorizationService.saveNoteAuth(note.getId(), subject); + if (save) { + noteManager.saveNote(note, subject); + } fireNoteCreateEvent(note, subject); return note; } @@ -225,12 +273,13 @@ public class Notebook { if (sourceNote == null) { throw new IOException("Source note: " + sourceNoteId + " not found"); } - Note newNote = createNote(newNotePath, subject); + Note newNote = createNote(newNotePath, subject, false); List<Paragraph> paragraphs = sourceNote.getParagraphs(); for (Paragraph p : paragraphs) { newNote.addCloneParagraph(p, subject); } saveNote(newNote, subject); + authorizationService.cloneNoteMeta(newNote.getId(), sourceNoteId, subject); return newNote; } @@ -502,6 +551,7 @@ public class Notebook { } public List<NoteInfo> getNotesInfo(Function<String, Boolean> func) { + LOGGER.info("Start getNoteList"); String homescreenNoteId = conf.getString(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN); boolean hideHomeScreenNotebookFromList = conf.getBoolean(ConfVars.ZEPPELIN_NOTEBOOK_HOMESCREEN_HIDE); @@ -525,6 +575,7 @@ public class Notebook { } return name1.compareTo(name2); }); + LOGGER.info("Finish getNoteList"); return notesInfo; } } 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 deleted file mode 100644 index d52dc77..0000000 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorization.java +++ /dev/null @@ -1,444 +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. - */ - -package org.apache.zeppelin.notebook; - -import java.io.IOException; -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.commons.lang.StringUtils; -import org.apache.zeppelin.conf.ZeppelinConfiguration; -import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; -import org.apache.zeppelin.scheduler.Job; -import org.apache.zeppelin.storage.ConfigStorage; -import org.apache.zeppelin.user.AuthenticationInfo; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.Sets; - -/** - * Contains authorization information for notes - */ -public class NotebookAuthorization implements NoteEventListener { - private static final Logger LOG = LoggerFactory.getLogger(NotebookAuthorization.class); - private static NotebookAuthorization instance = null; - /* - * { "note1": { "owners": ["u1"], "readers": ["u1", "u2"], "runners": ["u2"], - * "writers": ["u1"] }, "note2": ... } } - */ - private static Map<String, Map<String, Set<String>>> authInfo = new HashMap<>(); - /* - * contains roles for each user - */ - private static Map<String, Set<String>> userRoles = new HashMap<>(); - private static ZeppelinConfiguration conf; - - private static ConfigStorage configStorage; - - private NotebookAuthorization() {} - - public static NotebookAuthorization init(ZeppelinConfiguration config) { - if (instance == null) { - instance = new NotebookAuthorization(); - conf = config; - try { - configStorage = ConfigStorage.getInstance(config); - loadFromFile(); - } catch (IOException e) { - LOG.error("Error loading NotebookAuthorization", e); - } - } - return instance; - } - - public static NotebookAuthorization getInstance() { - if (instance == null) { - LOG.warn("Notebook authorization module was called without initialization," - + " initializing with default configuration"); - init(ZeppelinConfiguration.create()); - } - return instance; - } - - private static void loadFromFile() throws IOException { - NotebookAuthorizationInfoSaving info = configStorage.loadNotebookAuthorization(); - if (info != null) { - authInfo = info.authInfo; - } - } - - public void setRoles(String user, Set<String> roles) { - if (StringUtils.isBlank(user)) { - LOG.warn("Setting roles for empty user"); - return; - } - roles = validateUser(roles); - userRoles.put(user, roles); - } - - public Set<String> getRoles(String user) { - Set<String> roles = Sets.newHashSet(); - if (userRoles.containsKey(user)) { - roles.addAll(userRoles.get(user)); - } - return roles; - } - - private void saveToFile() { - synchronized (authInfo) { - NotebookAuthorizationInfoSaving info = new NotebookAuthorizationInfoSaving(); - info.authInfo = authInfo; - try { - configStorage.save(info); - } catch (IOException e) { - LOG.error("Error saving notebook authorization file", e); - } - } - } - - public boolean isPublic() { - return conf.isNotebookPublic(); - } - - private Set<String> validateUser(Set<String> users) { - Set<String> returnUser = new HashSet<>(); - for (String user : users) { - if (!user.trim().isEmpty()) { - returnUser.add(user.trim()); - } - } - return returnUser; - } - - public void setOwners(String noteId, Set<String> entities) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - entities = validateUser(entities); - if (noteAuthInfo == null) { - noteAuthInfo = new LinkedHashMap(); - noteAuthInfo.put("owners", new LinkedHashSet(entities)); - noteAuthInfo.put("readers", new LinkedHashSet()); - noteAuthInfo.put("runners", new LinkedHashSet()); - noteAuthInfo.put("writers", new LinkedHashSet()); - } else { - noteAuthInfo.put("owners", new LinkedHashSet(entities)); - } - authInfo.put(noteId, noteAuthInfo); - saveToFile(); - } - - public void setReaders(String noteId, Set<String> entities) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - entities = validateUser(entities); - if (noteAuthInfo == null) { - noteAuthInfo = new LinkedHashMap(); - noteAuthInfo.put("owners", new LinkedHashSet()); - noteAuthInfo.put("readers", new LinkedHashSet(entities)); - noteAuthInfo.put("runners", new LinkedHashSet()); - noteAuthInfo.put("writers", new LinkedHashSet()); - } else { - noteAuthInfo.put("readers", new LinkedHashSet(entities)); - } - authInfo.put(noteId, noteAuthInfo); - saveToFile(); - } - - public void setRunners(String noteId, Set<String> entities) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - entities = validateUser(entities); - if (noteAuthInfo == null) { - noteAuthInfo = new LinkedHashMap(); - noteAuthInfo.put("owners", new LinkedHashSet()); - noteAuthInfo.put("readers", new LinkedHashSet()); - noteAuthInfo.put("runners", new LinkedHashSet(entities)); - noteAuthInfo.put("writers", new LinkedHashSet()); - } else { - noteAuthInfo.put("runners", new LinkedHashSet(entities)); - } - authInfo.put(noteId, noteAuthInfo); - saveToFile(); - } - - - public void setWriters(String noteId, Set<String> entities) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - entities = validateUser(entities); - if (noteAuthInfo == null) { - noteAuthInfo = new LinkedHashMap(); - noteAuthInfo.put("owners", new LinkedHashSet()); - noteAuthInfo.put("readers", new LinkedHashSet()); - noteAuthInfo.put("runners", new LinkedHashSet()); - noteAuthInfo.put("writers", new LinkedHashSet(entities)); - } else { - noteAuthInfo.put("writers", new LinkedHashSet(entities)); - } - authInfo.put(noteId, noteAuthInfo); - saveToFile(); - } - - /* - * If case conversion is enforced, then change entity names to lower case - */ - private Set<String> checkCaseAndConvert(Set<String> entities) { - if (conf.isUsernameForceLowerCase()) { - Set<String> set2 = new HashSet<String>(); - for (String name : entities) { - set2.add(name.toLowerCase()); - } - return set2; - } else { - return entities; - } - } - - public Set<String> getOwners(String noteId) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - Set<String> entities = null; - if (noteAuthInfo == null) { - entities = new HashSet<>(); - } else { - entities = noteAuthInfo.get("owners"); - if (entities == null) { - entities = new HashSet<>(); - } else { - entities = checkCaseAndConvert(entities); - } - } - return entities; - } - - public Set<String> getReaders(String noteId) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - Set<String> entities = null; - if (noteAuthInfo == null) { - entities = new HashSet<>(); - } else { - entities = noteAuthInfo.get("readers"); - if (entities == null) { - entities = new HashSet<>(); - } else { - entities = checkCaseAndConvert(entities); - } - } - return entities; - } - - public Set<String> getRunners(String noteId) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - Set<String> entities = null; - if (noteAuthInfo == null) { - entities = new HashSet<>(); - } else { - entities = noteAuthInfo.get("runners"); - if (entities == null) { - entities = new HashSet<>(); - } else { - entities = checkCaseAndConvert(entities); - } - } - return entities; - } - - public Set<String> getWriters(String noteId) { - Map<String, Set<String>> noteAuthInfo = authInfo.get(noteId); - Set<String> entities = null; - if (noteAuthInfo == null) { - entities = new HashSet<>(); - } else { - entities = noteAuthInfo.get("writers"); - if (entities == null) { - entities = new HashSet<>(); - } else { - entities = checkCaseAndConvert(entities); - } - } - return entities; - } - - public boolean isOwner(String noteId, Set<String> entities) { - return isMember(entities, getOwners(noteId)) || isAdmin(entities); - } - - public boolean isWriter(String noteId, Set<String> entities) { - return isMember(entities, getWriters(noteId)) || - isMember(entities, getOwners(noteId)) || - isAdmin(entities); - } - - public boolean isReader(String noteId, Set<String> entities) { - return isMember(entities, getReaders(noteId)) || - isMember(entities, getOwners(noteId)) || - isMember(entities, getWriters(noteId)) || - isMember(entities, getRunners(noteId)) || - isAdmin(entities); - } - - public boolean isRunner(String noteId, Set<String> entities) { - return isMember(entities, getRunners(noteId)) || - isMember(entities, getWriters(noteId)) || - isMember(entities, getOwners(noteId)) || - isAdmin(entities); - } - - private boolean isAdmin(Set<String> entities) { - String adminRole = conf.getString(ConfVars.ZEPPELIN_OWNER_ROLE); - if (StringUtils.isBlank(adminRole)) { - return false; - } - return entities.contains(adminRole); - } - - // return true if b is empty or if (a intersection b) is non-empty - private boolean isMember(Set<String> a, Set<String> b) { - Set<String> intersection = new HashSet<>(b); - intersection.retainAll(a); - 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 boolean hasRunAuthorization(Set<String> userAndRoles, String noteId) { - if (conf.isAnonymousAllowed()) { - LOG.debug("Zeppelin runs in anonymous mode, everybody is runner"); - return true; - } - if (userAndRoles == null) { - return false; - } - return isRunner(noteId, userAndRoles); - } - - public void removeNote(String noteId) { - authInfo.remove(noteId); - saveToFile(); - } - - public List<NoteInfo> filterByUser(List<NoteInfo> notes, AuthenticationInfo subject) { - final Set<String> entities = Sets.newHashSet(); - if (subject != null) { - entities.add(subject.getUser()); - } - return FluentIterable.from(notes).filter(new Predicate<NoteInfo>() { - @Override - public boolean apply(NoteInfo input) { - return input != null && isReader(input.getId(), entities); - } - }).toList(); - } - - public void setNewNotePermissions(String noteId, AuthenticationInfo subject) { - if (!AuthenticationInfo.isAnonymous(subject)) { - if (isPublic()) { - // add current user to owners - can be public - Set<String> owners = getOwners(noteId); - owners.add(subject.getUser()); - setOwners(noteId, owners); - } else { - // add current user to owners, readers, runners, writers - private note - Set<String> entities = getOwners(noteId); - entities.add(subject.getUser()); - setOwners(noteId, entities); - - entities = getReaders(noteId); - entities.add(subject.getUser()); - setReaders(noteId, entities); - - entities = getRunners(noteId); - entities.add(subject.getUser()); - setRunners(noteId, entities); - - entities = getWriters(noteId); - entities.add(subject.getUser()); - setWriters(noteId, entities); - } - } - } - - @Override - public void onNoteCreate(Note note, AuthenticationInfo subject) { - setNewNotePermissions(note.getId(), subject); - } - - @Override - public void onNoteRemove(Note note, AuthenticationInfo subject) { - removeNote(note.getId()); - } - - @Override - public void onNoteUpdate(Note note, AuthenticationInfo subject) { - - } - - @Override - public void onParagraphRemove(Paragraph p) { - - } - - @Override - public void onParagraphCreate(Paragraph p) { - - } - - @Override - public void onParagraphUpdate(Paragraph p) throws IOException { - - } - - @Override - public void onParagraphStatusChange(Paragraph p, Job.Status status) { - - } -} diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorizationInfoSaving.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorizationInfoSaving.java index 629e400..338a0f2 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorizationInfoSaving.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/NotebookAuthorizationInfoSaving.java @@ -18,8 +18,10 @@ package org.apache.zeppelin.notebook; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import org.apache.zeppelin.common.JsonSerializable; +import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -28,10 +30,17 @@ import java.util.Set; */ public class NotebookAuthorizationInfoSaving implements JsonSerializable { - private static final Gson gson = new Gson(); + private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); public Map<String, Map<String, Set<String>>> authInfo; + public NotebookAuthorizationInfoSaving(Map<String, NoteAuth> notesAuth) { + this.authInfo = new HashMap<>(); + for (Map.Entry<String, NoteAuth> entry : notesAuth.entrySet()) { + this.authInfo.put(entry.getKey(), entry.getValue().toMap()); + } + } + public String toJson() { return gson.toJson(this); } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java index c7956e3..bae5cba 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepo.java @@ -28,7 +28,7 @@ import java.util.List; import java.util.Map; /** - * Notebook repository (persistence layer) abstraction + * Notebook repository (persistence layer) abstraction. */ public interface NotebookRepo { @@ -36,17 +36,17 @@ public interface NotebookRepo { /** * Lists notebook information about all notebooks in storage. This method should only read - * the metadata of note, rather than reading all notes which usually takes long time. + * the note file name, rather than reading all note content which usually takes long time. * * @param subject contains user information. - * @return + * @return Map of noteId -> NoteInfo * @throws IOException */ @ZeppelinApi Map<String, NoteInfo> list(AuthenticationInfo subject) throws IOException; /** - * Get the notebook with the given id and given notePath. + * Get the notebook with the given noteId and given notePath. * * @param noteId is note id. * @param notePath is note path diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java index 3b69c3e..d679c84 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/NotebookRepoSync.java @@ -18,12 +18,11 @@ package org.apache.zeppelin.notebook.repo; import com.google.common.collect.Lists; -import javax.inject.Inject; import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; import org.apache.zeppelin.notebook.Note; +import org.apache.zeppelin.notebook.NoteAuth; import org.apache.zeppelin.notebook.NoteInfo; -import org.apache.zeppelin.notebook.NotebookAuthorization; import org.apache.zeppelin.notebook.OldNoteInfo; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.plugin.PluginManager; @@ -32,8 +31,14 @@ import org.apache.zeppelin.util.Util; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.inject.Inject; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * Notebook repository sync with remote storage @@ -143,23 +148,6 @@ public class NotebookRepoSync implements NotebookRepoWithVersionControl { } } - public void mergeAuthorizationInfo() throws IOException { - LOGGER.info("Merge AuthorizationInfo into note file"); - NotebookAuthorization notebookAuthorization = NotebookAuthorization.getInstance(); - for (int i = 0; i < repos.size(); ++i) { - NotebookRepo notebookRepo = repos.get(i); - Map<String, NoteInfo> notesInfo = notebookRepo.list(AuthenticationInfo.ANONYMOUS); - for (NoteInfo noteInfo : notesInfo.values()) { - Note note = notebookRepo.get(noteInfo.getId(), noteInfo.getPath(), AuthenticationInfo.ANONYMOUS); - note.setOwners(notebookAuthorization.getOwners(noteInfo.getId())); - note.setRunners(notebookAuthorization.getRunners(noteInfo.getId())); - note.setReaders(notebookAuthorization.getReaders(noteInfo.getId())); - note.setWriters(notebookAuthorization.getWriters(noteInfo.getId())); - notebookRepo.save(note, AuthenticationInfo.ANONYMOUS); - } - } - } - public List<NotebookRepoWithSettings> getNotebookRepos(AuthenticationInfo subject) { List<NotebookRepoWithSettings> reposSetting = Lists.newArrayList(); @@ -201,26 +189,26 @@ public class NotebookRepoSync implements NotebookRepoWithVersionControl { return getRepo(0).list(subject); } - /* list from specific repo (for tests) */ + /* List NoteInfo from specific repo (for tests) */ List<NoteInfo> list(int repoIndex, AuthenticationInfo subject) throws IOException { return new ArrayList<>(getRepo(repoIndex).list(subject).values()); } /** - * Returns from Notebook from the first repository + * Get Note from the first repository */ @Override public Note get(String noteId, String notePath, AuthenticationInfo subject) throws IOException { return getRepo(0).get(noteId, notePath, subject); } - /* get note from specific repo (for tests) */ + /* Get Note from specific repo (for tests) */ Note get(int repoIndex, String noteId, String noteName, AuthenticationInfo subject) throws IOException { return getRepo(repoIndex).get(noteId, noteName, subject); } /** - * Saves to all repositories + * Saves note to all repositories */ @Override public void save(Note note, AuthenticationInfo subject) throws IOException { diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/UpgradeNoteFileTool.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/UpgradeNoteFileTool.java index 6429e74..c39b116 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/UpgradeNoteFileTool.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/UpgradeNoteFileTool.java @@ -40,6 +40,5 @@ public class UpgradeNoteFileTool { ZeppelinConfiguration conf = ZeppelinConfiguration.create(); NotebookRepoSync notebookRepoSync = new NotebookRepoSync(conf); notebookRepoSync.convertNoteFiles(conf, cmd.hasOption("d")); - notebookRepoSync.mergeAuthorizationInfo(); } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java index e452235..50fffaf 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/repo/VFSNotebookRepo.java @@ -30,7 +30,6 @@ import java.util.Map; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.vfs2.FileObject; -import org.apache.commons.vfs2.FileSystemException; import org.apache.commons.vfs2.FileSystemManager; import org.apache.commons.vfs2.NameScope; import org.apache.commons.vfs2.Selectors; @@ -39,6 +38,7 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.conf.ZeppelinConfiguration.ConfVars; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; +import org.apache.zeppelin.notebook.NoteAuth; import org.apache.zeppelin.user.AuthenticationInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -61,7 +61,7 @@ public class VFSNotebookRepo implements NotebookRepo { @Override public void init(ZeppelinConfiguration conf) throws IOException { this.conf = conf; - setNotebookDirectory(conf.getRelativeDir(conf.getNotebookDir())); + setNotebookDirectory(conf.getNotebookDir()); } protected void setNotebookDirectory(String notebookDirPath) throws IOException { @@ -123,9 +123,6 @@ public class VFSNotebookRepo implements NotebookRepo { } catch (IOException e) { LOGGER.warn(e.getMessage()); } - - } else { - LOGGER.debug("Unrecognized note file: " + noteFileName); } } return noteInfos; @@ -163,7 +160,9 @@ public class VFSNotebookRepo implements NotebookRepo { } @Override - public void move(String noteId, String notePath, String newNotePath, + public void move(String noteId, + String notePath, + String newNotePath, AuthenticationInfo subject) throws IOException { LOGGER.info("Move note " + noteId + " from " + notePath + " to " + newNotePath); FileObject fileObject = rootNotebookFileObject.resolveFile( @@ -246,6 +245,5 @@ public class VFSNotebookRepo implements NotebookRepo { LOGGER.error("Cannot update notebook directory", e); } } - } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/conf/ZeppelinConfigurationTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/conf/ZeppelinConfigurationTest.java index 1771fd3..c730e5f 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/conf/ZeppelinConfigurationTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/conf/ZeppelinConfigurationTest.java @@ -80,10 +80,9 @@ public class ZeppelinConfigurationTest { @Test public void getNotebookDirTest() throws ConfigurationException { - ZeppelinConfiguration conf = new ZeppelinConfiguration(this.getClass().getResource("/zeppelin-site.xml")); String notebookLocation = conf.getNotebookDir(); - Assert.assertEquals("notebook", notebookLocation); + assertTrue(notebookLocation.endsWith("notebook")); } @Test diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java index a39c83e..1cafc23 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/helium/HeliumApplicationFactoryTest.java @@ -29,7 +29,9 @@ import org.apache.zeppelin.interpreter.InterpreterException; import org.apache.zeppelin.interpreter.InterpreterNotFoundException; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.notebook.ApplicationState; +import org.apache.zeppelin.notebook.AuthorizationService; import org.apache.zeppelin.notebook.Note; +import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.notebook.repo.NotebookRepo; @@ -57,11 +59,14 @@ public class HeliumApplicationFactoryTest extends AbstractInterpreterTest { } SearchService search = mock(SearchService.class); + AuthorizationService authorizationService = mock(AuthorizationService.class); notebookRepo = mock(NotebookRepo.class); notebook = new Notebook( conf, + authorizationService, notebookRepo, + new NoteManager(notebookRepo), interpreterFactory, interpreterSettingManager, search, diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java index 17d8c85..bc47de1 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java @@ -27,13 +27,12 @@ import org.apache.zeppelin.interpreter.InterpreterGroup; import org.apache.zeppelin.interpreter.InterpreterNotFoundException; import org.apache.zeppelin.interpreter.InterpreterOption; import org.apache.zeppelin.interpreter.InterpreterResult; -import org.apache.zeppelin.interpreter.InterpreterResultMessage; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.interpreter.remote.RemoteInterpreter; -import org.apache.zeppelin.notebook.repo.InMemoryNotebookRepo; import org.apache.zeppelin.notebook.repo.NotebookRepo; import org.apache.zeppelin.notebook.repo.NotebookRepoSettingsInfo; import org.apache.zeppelin.notebook.repo.NotebookRepoWithVersionControl; +import org.apache.zeppelin.notebook.repo.VFSNotebookRepo; import org.apache.zeppelin.notebook.scheduler.QuartzSchedulerService; import org.apache.zeppelin.notebook.scheduler.SchedulerService; import org.apache.zeppelin.resource.LocalResourcePool; @@ -78,6 +77,7 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo private static final Logger logger = LoggerFactory.getLogger(NotebookTest.class); private Notebook notebook; + private NoteManager noteManager; private NotebookRepo notebookRepo; private AuthorizationService authorizationService; private Credentials credentials; @@ -92,20 +92,23 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo super.setUp(); SearchService search = mock(SearchService.class); - notebookRepo = new InMemoryNotebookRepo(); + notebookRepo = new VFSNotebookRepo(); + notebookRepo.init(conf); + noteManager = new NoteManager(notebookRepo); + authorizationService = new AuthorizationService(conf); credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath(), null); - notebook = new Notebook(conf, notebookRepo, interpreterFactory, interpreterSettingManager, search, + notebook = new Notebook(conf, authorizationService, notebookRepo, noteManager, interpreterFactory, interpreterSettingManager, search, credentials, null); - authorizationService = new AuthorizationService(notebook, notebook.getConf()); notebook.setParagraphJobListener(this); schedulerService = new QuartzSchedulerService(conf, notebook); - } @After public void tearDown() throws Exception { super.tearDown(); + System.clearProperty(ConfVars.ZEPPELIN_NOTEBOOK_PUBLIC.getVarName()); + System.clearProperty(ConfVars.ZEPPELIN_NOTEBOOK_CRON_ENABLE.getVarName()); } @Test @@ -114,13 +117,13 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo Notebook notebook; notebookRepo = new DummyNotebookRepo(); - notebook = new Notebook(conf, notebookRepo, interpreterFactory, + notebook = new Notebook(conf, mock(AuthorizationService.class), notebookRepo, new NoteManager(notebookRepo), interpreterFactory, interpreterSettingManager, null, credentials, null); assertFalse("Revision is not supported in DummyNotebookRepo", notebook.isRevisionSupported()); notebookRepo = new DummyNotebookRepoWithVersionControl(); - notebook = new Notebook(conf, notebookRepo, interpreterFactory, + notebook = new Notebook(conf, mock(AuthorizationService.class), notebookRepo, new NoteManager(notebookRepo), interpreterFactory, interpreterSettingManager, null, credentials, null); assertTrue("Revision is supported in DummyNotebookRepoWithVersionControl", @@ -139,7 +142,6 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo return new HashMap<>(); } - @Override public Note get(String noteId, String notePath, AuthenticationInfo subject) throws IOException { return null; @@ -297,13 +299,14 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo Note note = notebook.createNote("note1", AuthenticationInfo.ANONYMOUS); Paragraph p1 = note.insertNewParagraph(0, AuthenticationInfo.ANONYMOUS); p1.setText("%md hello world"); + notebook.saveNote(note, AuthenticationInfo.ANONYMOUS); // when load notebook.reloadAllNotes(anonymous); assertEquals(1, notebook.getAllNotes().size()); // then interpreter factory should be injected into all the paragraphs - note = notebook.getAllNotes().get(0); + note = notebook.getNote(note.getId()); try { note.getParagraphs().get(0).getBindedInterpreter(); fail("Should throw InterpreterNotFoundException"); @@ -339,10 +342,9 @@ public class NotebookTest extends AbstractInterpreterTest implements ParagraphJo // delete notebook from notebook list when reloadAllNotes() is called - ((InMemoryNotebookRepo) notebookRepo).reset(); notebook.reloadAllNotes(anonymous); notes = notebook.getAllNotes(); - assertEquals(notes.size(), 0); + assertEquals(notes.size(), 2); } @Test diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java index 726fb93..7a3b4c8 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/repo/NotebookRepoSyncTest.java @@ -39,6 +39,7 @@ import org.apache.zeppelin.interpreter.remote.RemoteInterpreterProcessListener; import org.apache.zeppelin.notebook.AuthorizationService; import org.apache.zeppelin.notebook.Note; import org.apache.zeppelin.notebook.NoteInfo; +import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.search.SearchService; @@ -52,20 +53,20 @@ import org.quartz.SchedulerException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -// TODO(zjffdu) move it to zeppelin-zengine public class NotebookRepoSyncTest { private File ZEPPELIN_HOME; private ZeppelinConfiguration conf; private File mainNotebookDir; private File secNotebookDir; - private Notebook notebookSync; + private Notebook notebook; private NotebookRepoSync notebookRepoSync; private InterpreterFactory factory; private InterpreterSettingManager interpreterSettingManager; private SearchService search; private Credentials credentials; private AuthenticationInfo anonymous; + private NoteManager noteManager; private AuthorizationService authorizationService; private static final Logger LOG = LoggerFactory.getLogger(NotebookRepoSyncTest.class); @@ -99,10 +100,11 @@ public class NotebookRepoSyncTest { search = mock(SearchService.class); notebookRepoSync = new NotebookRepoSync(conf); + noteManager = new NoteManager(notebookRepoSync); + authorizationService = new AuthorizationService(conf); credentials = new Credentials(conf.credentialsPersist(), conf.getCredentialsPath(), null); - notebookSync = new Notebook(conf, notebookRepoSync, factory, interpreterSettingManager, search, credentials, null); + notebook = new Notebook(conf, authorizationService, notebookRepoSync, noteManager, factory, interpreterSettingManager, search, credentials, null); anonymous = new AuthenticationInfo("anonymous"); - authorizationService = new AuthorizationService(notebookSync, conf); } @After @@ -123,14 +125,14 @@ public class NotebookRepoSyncTest { assertEquals(0, notebookRepoSync.list(1, anonymous).size()); /* create note */ - Note note = notebookSync.createNote("test", "", anonymous); + Note note = notebook.createNote("test", "", anonymous); // check that automatically saved on both storages assertEquals(1, notebookRepoSync.list(0, anonymous).size()); assertEquals(1, notebookRepoSync.list(1, anonymous).size()); assertEquals(notebookRepoSync.list(0, anonymous).get(0).getId(), notebookRepoSync.list(1, anonymous).get(0).getId()); - notebookSync.removeNote(notebookRepoSync.list(0, null).get(0).getId(), anonymous); + notebook.removeNote(notebookRepoSync.list(0, null).get(0).getId(), anonymous); } @Test @@ -140,7 +142,7 @@ public class NotebookRepoSyncTest { assertEquals(0, notebookRepoSync.list(0, anonymous).size()); assertEquals(0, notebookRepoSync.list(1, anonymous).size()); - Note note = notebookSync.createNote("test", "", anonymous); + Note note = notebook.createNote("test", "", anonymous); /* check that created in both storage systems */ assertEquals(1, notebookRepoSync.list(0, anonymous).size()); @@ -148,7 +150,7 @@ public class NotebookRepoSyncTest { assertEquals(notebookRepoSync.list(0, anonymous).get(0).getId(), notebookRepoSync.list(1, anonymous).get(0).getId()); /* remove Note */ - notebookSync.removeNote(notebookRepoSync.list(0, anonymous).get(0).getId(), anonymous); + notebook.removeNote(notebookRepoSync.list(0, anonymous).get(0).getId(), anonymous); /* check that deleted in both storages */ assertEquals(0, notebookRepoSync.list(0, anonymous).size()); @@ -160,7 +162,7 @@ public class NotebookRepoSyncTest { public void testSyncUpdateMain() throws IOException { /* create note */ - Note note = notebookSync.createNote("/test", "test", anonymous); + Note note = notebook.createNote("/test", "test", anonymous); note.setInterpreterFactory(mock(InterpreterFactory.class)); Paragraph p1 = note.addNewParagraph(AuthenticationInfo.ANONYMOUS); Map config = p1.getConfig(); @@ -229,7 +231,7 @@ public class NotebookRepoSyncTest { assertEquals(2, notebookRepoSync.list(1, anonymous).size()); // After reloading notebooks repos should be synchronized - notebookSync.reloadAllNotes(anonymous); + notebook.reloadAllNotes(anonymous); assertEquals(2, notebookRepoSync.list(0, anonymous).size()); assertEquals(2, notebookRepoSync.list(1, anonymous).size()); } @@ -240,7 +242,7 @@ public class NotebookRepoSyncTest { System.setProperty(ConfVars.ZEPPELIN_NOTEBOOK_ONE_WAY_SYNC.getVarName(), "true"); conf = ZeppelinConfiguration.create(); notebookRepoSync = new NotebookRepoSync(conf); - notebookSync = new Notebook(conf, notebookRepoSync, factory, interpreterSettingManager, search, credentials, null); + notebook = new Notebook(conf, mock(AuthorizationService.class), notebookRepoSync, new NoteManager(notebookRepoSync), factory, interpreterSettingManager, search, credentials, null); // check that both storage repos are empty assertTrue(notebookRepoSync.getRepoCount() > 1); @@ -260,7 +262,7 @@ public class NotebookRepoSyncTest { assertEquals(2, notebookRepoSync.list(1, null).size()); // after reloading the notebook should be wiped from secondary storage - notebookSync.reloadAllNotes(null); + notebook.reloadAllNotes(null); assertEquals(0, notebookRepoSync.list(0, null).size()); assertEquals(0, notebookRepoSync.list(1, null).size()); @@ -276,7 +278,7 @@ public class NotebookRepoSyncTest { assertEquals(0, notebookRepoSync.list(1, null).size()); // after reloading notebooks repos should be synchronized - notebookSync.reloadAllNotes(null); + notebook.reloadAllNotes(null); assertEquals(2, notebookRepoSync.list(0, null).size()); assertEquals(2, notebookRepoSync.list(1, null).size()); } @@ -287,7 +289,7 @@ public class NotebookRepoSyncTest { ZeppelinConfiguration vConf = ZeppelinConfiguration.create(); NotebookRepoSync vRepoSync = new NotebookRepoSync(vConf); - Notebook vNotebookSync = new Notebook(vConf, vRepoSync, factory, interpreterSettingManager, search, credentials, null); + Notebook vNotebookSync = new Notebook(vConf, mock(AuthorizationService.class), vRepoSync, new NoteManager(vRepoSync), factory, interpreterSettingManager, search, credentials, null); // one git versioned storage initialized assertThat(vRepoSync.getRepoCount()).isEqualTo(1); @@ -327,7 +329,7 @@ public class NotebookRepoSyncTest { public void testSyncWithAcl() throws IOException { /* scenario 1 - note exists with acl on main storage */ AuthenticationInfo user1 = new AuthenticationInfo("user1"); - Note note = notebookSync.createNote("/test", "test", user1); + Note note = notebook.createNote("/test", "test", user1); assertEquals(0, note.getParagraphs().size()); // saved on both storages diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java index 52178dc..1e65115 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/search/LuceneSearchTest.java @@ -29,7 +29,9 @@ import org.apache.zeppelin.conf.ZeppelinConfiguration; import org.apache.zeppelin.interpreter.InterpreterFactory; import org.apache.zeppelin.interpreter.InterpreterSetting; import org.apache.zeppelin.interpreter.InterpreterSettingManager; +import org.apache.zeppelin.notebook.AuthorizationService; import org.apache.zeppelin.notebook.Note; +import org.apache.zeppelin.notebook.NoteManager; import org.apache.zeppelin.notebook.Notebook; import org.apache.zeppelin.notebook.Paragraph; import org.apache.zeppelin.notebook.repo.NotebookRepo; @@ -54,7 +56,7 @@ public class LuceneSearchTest { InterpreterSetting defaultInterpreterSetting = mock(InterpreterSetting.class); when(defaultInterpreterSetting.getName()).thenReturn("test"); when(interpreterSettingManager.getDefaultInterpreterSetting()).thenReturn(defaultInterpreterSetting); - notebook = new Notebook(ZeppelinConfiguration.create(), mock(NotebookRepo.class), + notebook = new Notebook(ZeppelinConfiguration.create(), mock(AuthorizationService.class), mock(NotebookRepo.class), mock(NoteManager.class), mock(InterpreterFactory.class), interpreterSettingManager, noteSearchService, mock(Credentials.class), null);