From e2affca7b3423141be98fbca041257c5aa81a38d Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Thu, 31 Dec 2015 19:40:19 +0200 Subject: [PATCH 01/14] Securing the HTTP channel only. Websocket security is done in the next commit --- bin/zeppelin.sh | 2 +- conf/shiro.ini | 43 ++++++++++ conf/zeppelin-site.xml.template | 6 ++ pom.xml | 12 +++ security-readme.md | 13 +++ zeppelin-server/pom.xml | 10 +++ .../apache/zeppelin/rest/SecurityRestApi.java | 74 +++++++++++++++++ .../zeppelin/server/ZeppelinServer.java | 10 +++ .../zeppelin/ticket/TicketContainer.java | 82 +++++++++++++++++++ .../apache/zeppelin/utils/SecurityUtils.java | 17 ++++ .../zeppelin/rest/SecurityRestApiTest.java | 58 +++++++++++++ .../zeppelin/ticket/TicketContainerTest.java | 62 ++++++++++++++ .../zeppelin/conf/ZeppelinConfiguration.java | 3 +- 13 files changed, 390 insertions(+), 2 deletions(-) create mode 100644 conf/shiro.ini create mode 100644 security-readme.md create mode 100644 zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java create mode 100644 zeppelin-server/src/main/java/org/apache/zeppelin/ticket/TicketContainer.java create mode 100644 zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java create mode 100644 zeppelin-server/src/test/java/org/apache/zeppelin/ticket/TicketContainerTest.java diff --git a/bin/zeppelin.sh b/bin/zeppelin.sh index 17950e8bbdd..bf620f156ee 100755 --- a/bin/zeppelin.sh +++ b/bin/zeppelin.sh @@ -85,4 +85,4 @@ if [[ ! -d "${ZEPPELIN_NOTEBOOK_DIR}" ]]; then $(mkdir -p "${ZEPPELIN_NOTEBOOK_DIR}") fi -$(exec $ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_SERVER "$@") +$(exec $ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CONF_DIR/shiro.ini:$ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_SERVER "$@") diff --git a/conf/shiro.ini b/conf/shiro.ini new file mode 100644 index 00000000000..8934ca764af --- /dev/null +++ b/conf/shiro.ini @@ -0,0 +1,43 @@ +# +# 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. +# + +[users] +# List of users with their password allowed to access Zeppelin. +# To use a different strategy (LDAP / Database / ...) check the shiro doc at http://shiro.apache.org/configuration.html#Configuration-INISections +admin = password1 +user1 = password2 +user2 = password3 + +[main] + +# Let's use some in-memory caching to reduce the number of runtime lookups against Stormpath. +# A real application might want to use a more robust caching solution (e.g. ehcache or a +# distributed cache). When using such caches, be aware of your cache TTL settings: too high +# a TTL and the cache won't reflect any potential changes in Stormpath fast enough. Too low +# and the cache could evict too often, reducing performance. +cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager +securityManager.cacheManager = $cacheManager + + +[urls] + +# anon means the access is anonymous. +# authcBasic means Basic Auth Security +# To enfore security, comment the line below and uncomment the next one +/** = anon +#/** = authcBasic + diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index b6aca75d626..16a99e82632 100755 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -180,5 +180,11 @@ Allowed sources for REST and WebSocket requests (i.e. http://onehost:8080,http://otherhost.com). If you leave * you are vulnerable to https://issues.apache.org/jira/browse/ZEPPELIN-173 + + zeppelin.anonymous.allowed + false + Anonymous user allowed by default + + diff --git a/pom.xml b/pom.xml index 5e492fab3ae..08f4b5044f4 100755 --- a/pom.xml +++ b/pom.xml @@ -208,6 +208,18 @@ 4.11 test + + + + org.apache.shiro + shiro-core + 1.2.3 + + + org.apache.shiro + shiro-web + 1.2.3 + diff --git a/security-readme.md b/security-readme.md new file mode 100644 index 00000000000..2489329b29f --- /dev/null +++ b/security-readme.md @@ -0,0 +1,13 @@ +#Shiro Authentication +To connect to Zeppelin, users will be asked to enter their credentials. Once logged, a user has access to all notes including other users notes. +This a a first step toward full security as implemented by this pull request (https://github.com/apache/incubator-zeppelin/pull/53). + +#Security setup +1. Secure the HTTP channel: Comment the line "/** = anon" and uncomment the line "/** = authcBasic" in the file conf/shiro.ini. Read more about he shiro.ini file format at the following URL http://shiro.apache.org/configuration.html#Configuration-INISections. +2. Secure the Websocket channel : Set to property "zeppelin.anonymous.allowed" to "true" in the file conf/zeppelin-site.xml. You can start by renaming conf/zeppelin-site.xml.template to conf/zeppelin-site.xml +3. Start Zeppelin : bin/zeppelin.sh +4. point your browser to http://localhost:8080 +5. Login using one of the user/password combinations defined in the conf/shiro.ini file. + + + diff --git a/zeppelin-server/pom.xml b/zeppelin-server/pom.xml index e77ee6ca38d..73e878a58f5 100644 --- a/zeppelin-server/pom.xml +++ b/zeppelin-server/pom.xml @@ -269,6 +269,16 @@ 1.9.0 test + + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java new file mode 100644 index 00000000000..d6f3dec05d4 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/SecurityRestApi.java @@ -0,0 +1,74 @@ +/* + * 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 org.apache.zeppelin.conf.ZeppelinConfiguration; +import org.apache.zeppelin.server.JsonResponse; +import org.apache.zeppelin.ticket.TicketContainer; +import org.apache.zeppelin.utils.SecurityUtils; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Response; +import java.util.HashMap; +import java.util.Map; + +/** + * Zeppelin security rest api endpoint. + * + */ +@Path("/security") +@Produces("application/json") +public class SecurityRestApi { + /** + * Required by Swagger. + */ + public SecurityRestApi() { + super(); + } + + /** + * Get ticket + * Returns username & ticket + * for anonymous access, username is always anonymous. + * After getting this ticket, access through websockets become safe + * + * @return 200 response + */ + @GET + @Path("ticket") + public Response ticket() { + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + String principal = SecurityUtils.getPrincipal(); + JsonResponse response; + // ticket set to anonymous for anonymous user. Simplify testing. + String ticket; + if ("anonymous".equals(principal)) + ticket = "anonymous"; + else + ticket = TicketContainer.instance.getTicket(principal); + + Map data = new HashMap<>(); + data.put("principal", principal); + data.put("ticket", ticket); + + response = new JsonResponse(Response.Status.OK, "", data); + return response.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 fd115ee18cb..bd2d2c89a6f 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 @@ -36,6 +36,7 @@ import org.apache.zeppelin.notebook.repo.NotebookRepoSync; import org.apache.zeppelin.rest.InterpreterRestApi; import org.apache.zeppelin.rest.NotebookRestApi; +import org.apache.zeppelin.rest.SecurityRestApi; import org.apache.zeppelin.rest.ZeppelinRestApi; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.apache.zeppelin.search.SearchService; @@ -226,6 +227,12 @@ private static ServletContextHandler setupRestApiContextHandler(ZeppelinConfigur cxfContext.addFilter(new FilterHolder(CorsFilter.class), "/*", EnumSet.allOf(DispatcherType.class)); + + cxfContext.addFilter(org.apache.shiro.web.servlet.ShiroFilter.class, "/*", + EnumSet.allOf(DispatcherType.class)); + + cxfContext.addEventListener(new org.apache.shiro.web.env.EnvironmentLoaderListener()); + return cxfContext; } @@ -273,6 +280,9 @@ public Set getSingletons() { InterpreterRestApi interpreterApi = new InterpreterRestApi(replFactory); singletons.add(interpreterApi); + SecurityRestApi securityApi = new SecurityRestApi(); + singletons.add(securityApi); + return singletons; } } diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/ticket/TicketContainer.java b/zeppelin-server/src/main/java/org/apache/zeppelin/ticket/TicketContainer.java new file mode 100644 index 00000000000..513bb4a9337 --- /dev/null +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/ticket/TicketContainer.java @@ -0,0 +1,82 @@ +/* + * 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.ticket; + +import java.util.Calendar; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Very simple ticket container + * No cleanup is done, since the same user accross different devices share the same ticket + * The Map size is at most the number of different user names having access to a Zeppelin instance + */ + + +public class TicketContainer { + private static class Entry { + public final String ticket; + // lastAccessTime still unused + public final long lastAccessTime; + + Entry(String ticket) { + this.ticket = ticket; + this.lastAccessTime = Calendar.getInstance().getTimeInMillis(); + } + } + + private Map sessions = new ConcurrentHashMap<>(); + + public static final TicketContainer instance = new TicketContainer(); + + /** + * For test use + * @param principal + * @param ticket + * @return true if ticket assigned to principal. + */ + public boolean isValid(String principal, String ticket) { + if ("anonymous".equals(principal) && "anonymous".equals(ticket)) + return true; + Entry entry = sessions.get(principal); + return entry != null && entry.ticket.equals(ticket); + } + + /** + * get or create ticket for Websocket authentication assigned to authenticated shiro user + * For unathenticated user (anonymous), always return ticket value "anonymous" + * @param principal + * @return + */ + public synchronized String getTicket(String principal) { + Entry entry = sessions.get(principal); + String ticket; + if (entry == null) { + if (principal.equals("anonymous")) + ticket = "anonymous"; + else + ticket = UUID.randomUUID().toString(); + } else { + ticket = entry.ticket; + } + entry = new Entry(ticket); + sessions.put(principal, entry); + return ticket; + } +} diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java index 732c7c8b4e6..1d06e3a5ebf 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/utils/SecurityUtils.java @@ -16,6 +16,7 @@ */ package org.apache.zeppelin.utils; +import org.apache.shiro.subject.Subject; import org.apache.zeppelin.conf.ZeppelinConfiguration; import java.net.InetAddress; @@ -44,4 +45,20 @@ public static Boolean isValidOrigin(String sourceHost, ZeppelinConfiguration con "localhost".equals(sourceUriHost) || conf.getAllowedOrigins().contains(sourceHost); } + + /** + * Return the authenticated user if any otherwise returns "anonymous" + * @return shiro principal + */ + public static String getPrincipal() { + Subject subject = org.apache.shiro.SecurityUtils.getSubject(); + String principal; + if (subject.isAuthenticated()) { + principal = subject.getPrincipal().toString(); + } + else { + principal = "anonymous"; + } + return principal; + } } diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java new file mode 100644 index 00000000000..b496f99a117 --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/SecurityRestApiTest.java @@ -0,0 +1,58 @@ +/* + * 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 com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.commons.httpclient.methods.GetMethod; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; + +import static org.junit.Assert.*; + +public class SecurityRestApiTest extends AbstractTestRestApi { + Gson gson = new Gson(); + + @BeforeClass + public static void init() throws Exception { + AbstractTestRestApi.startUp(); + } + + @AfterClass + public static void destroy() throws Exception { + AbstractTestRestApi.shutDown(); + } + + @Test + public void testTicket() throws IOException { + GetMethod get = httpGet("/security/ticket"); + get.addRequestHeader("Origin", "http://localhost"); + Map resp = gson.fromJson(get.getResponseBodyAsString(), + new TypeToken>(){}.getType()); + Map body = (Map) resp.get("body"); + assertEquals("anonymous", body.get("principal")); + assertEquals("anonymous", body.get("ticket")); + get.releaseConnection(); + } + +} + diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/ticket/TicketContainerTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/ticket/TicketContainerTest.java new file mode 100644 index 00000000000..91d2cb3af20 --- /dev/null +++ b/zeppelin-server/src/test/java/org/apache/zeppelin/ticket/TicketContainerTest.java @@ -0,0 +1,62 @@ +/* + * 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.ticket; + +import org.junit.Before; +import org.junit.Test; + +import java.net.UnknownHostException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TicketContainerTest { + private TicketContainer container; + + @Before + public void setUp() throws Exception { + container = TicketContainer.instance; + } + + @Test + public void isValidAnonymous() throws UnknownHostException { + boolean ok = container.isValid("anonymous", "anonymous"); + assertTrue(ok); + } + + @Test + public void isValidExistingPrincipal() throws UnknownHostException { + String ticket = container.getTicket("someuser1"); + boolean ok = container.isValid("someuser1", ticket); + assertTrue(ok); + } + + @Test + public void isValidNonExistingPrincipal() throws UnknownHostException { + boolean ok = container.isValid("unknownuser", "someticket"); + assertFalse(ok); + } + + @Test + public void isValidunkownTicket() throws UnknownHostException { + String ticket = container.getTicket("someuser2"); + boolean ok = container.isValid("someuser2", ticket+"makeitinvalid"); + assertFalse(ok); + } +} + diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java index 87d1c20aa00..02b1a900c75 100755 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/conf/ZeppelinConfiguration.java @@ -433,7 +433,8 @@ public static enum ConfVars { ZEPPELIN_CONF_DIR("zeppelin.conf.dir", "conf"), // Allows a way to specify a ',' separated list of allowed origins for rest and websockets // i.e. http://localhost:8080 - ZEPPELIN_ALLOWED_ORIGINS("zeppelin.server.allowed.origins", "*"); + ZEPPELIN_ALLOWED_ORIGINS("zeppelin.server.allowed.origins", "*"), + ZEPPELIN_ANONYMOUS_ALLOWED("zeppelin.anonymous.allowed", true); private String varName; @SuppressWarnings("rawtypes") From f9b1952a247ea8f55d1cf8270d61c615007b63e8 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Thu, 31 Dec 2015 19:41:49 +0200 Subject: [PATCH 02/14] The Websocket channel is now as secure as the HTTP channel. --- .../org/apache/zeppelin/socket/Message.java | 2 ++ .../zeppelin/socket/NotebookServer.java | 14 ++++++++ zeppelin-web/src/app/home/home.controller.js | 8 ++++- .../components/navbar/navbar.controller.js | 32 +++++++++++++++++-- .../src/components/navbar/navbar.html | 4 +-- .../websocketEvents.factory.js | 4 ++- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java index dc657bdda06..6315f119c4d 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java @@ -102,6 +102,8 @@ public static enum OP { public OP op; public Map data = new HashMap(); + public String ticket; + public String principal; public Message(OP op) { this.op = op; 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 554f68cf073..66765c57df6 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 @@ -41,6 +41,7 @@ import org.apache.zeppelin.scheduler.JobListener; import org.apache.zeppelin.server.ZeppelinServer; import org.apache.zeppelin.socket.Message.OP; +import org.apache.zeppelin.ticket.TicketContainer; import org.apache.zeppelin.utils.SecurityUtils; import org.eclipse.jetty.websocket.WebSocket; import org.eclipse.jetty.websocket.WebSocketServlet; @@ -96,6 +97,19 @@ public void onMessage(NotebookSocket conn, String msg) { try { Message messagereceived = deserializeMessage(msg); LOG.debug("RECEIVE << " + messagereceived.op); + LOG.debug("RECEIVE PRINCIPAL << " + messagereceived.principal); + LOG.debug("RECEIVE TICKET << " + messagereceived.ticket); + String ticket = TicketContainer.instance.getTicket(messagereceived.principal); + if (ticket != null && !ticket.equals(messagereceived.ticket)) + throw new Exception("Invalid ticket " + messagereceived.ticket + " != " + ticket); + + ZeppelinConfiguration conf = ZeppelinConfiguration.create(); + boolean allowAnonymous = conf. + getBoolean(ZeppelinConfiguration.ConfVars.ZEPPELIN_ANONYMOUS_ALLOWED); + if (!allowAnonymous && messagereceived.principal.equals("anonymous")) { + throw new Exception("Anonymous access not allowed "); + } + /** Lets be elegant here */ switch (messagereceived.op) { case LIST_NOTES: diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js index 64ff8801557..4ace50dc7b4 100644 --- a/zeppelin-web/src/app/home/home.controller.js +++ b/zeppelin-web/src/app/home/home.controller.js @@ -14,7 +14,13 @@ 'use strict'; angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, notebookListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv) { - + if (!$rootScope.ticket) { + $rootScope.ticket = { + 'principal':'anonymous', + 'ticket':'anonymous' + }; + } + var vm = this; vm.notes = notebookListDataFactory; vm.websocketMsgSrv = websocketMsgSrv; diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index 30e6ac27892..480d18dae81 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -15,7 +15,14 @@ 'use strict'; angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootScope, $routeParams, - $location, notebookListDataFactory, websocketMsgSrv, arrayOrderingSrv) { + $location, notebookListDataFactory, websocketMsgSrv, arrayOrderingSrv, $http) { + if (!$rootScope.ticket) { + $rootScope.ticket = { + 'principal':'anonymous', + 'ticket':'anonymous' + }; + } + /** Current list of notes (ids) */ var vm = this; @@ -23,6 +30,7 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco vm.connected = websocketMsgSrv.isConnected(); vm.websocketMsgSrv = websocketMsgSrv; vm.arrayOrderingSrv = arrayOrderingSrv; + vm.authenticated = $rootScope.ticket.principal !== 'anonymous'; angular.element('#notebook-list').perfectScrollbar({suppressScrollX: true}); @@ -51,7 +59,27 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco websocketMsgSrv.getNotebookList(); } - function isActive(noteId) { + /** ask for a ticket for websocket access + * Shiro will require credentials here + * */ + $http.get('/api/security/ticket'). + success(function(ticket, status, headers, config) { + if (status === 401 || status === 403) { + // Dislay error message here + } + else { + $rootScope.ticket = angular.fromJson(ticket).body; + vm.loadNotes = loadNotes; + vm.isActive = isActive; + vm.loadNotes(); + vm.authenticated = $rootScope.ticket.principal !== 'anonymous'; + } + }). + error(function(data, status, headers, config) { + console.log('Could not get ticket'); + }); + + function isActive(noteId) { return ($routeParams.noteId === noteId); } diff --git a/zeppelin-web/src/components/navbar/navbar.html b/zeppelin-web/src/components/navbar/navbar.html index 86a85122add..20ee0241c90 100644 --- a/zeppelin-web/src/components/navbar/navbar.html +++ b/zeppelin-web/src/components/navbar/navbar.html @@ -73,8 +73,8 @@
  • - Connected - Disconnected + {{ticket.principal}} connected + Disconnected
  • diff --git a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js index dad2cb5545f..68b1ed40496 100644 --- a/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js +++ b/zeppelin-web/src/components/websocketEvents/websocketEvents.factory.js @@ -28,7 +28,9 @@ angular.module('zeppelinWebApp').factory('websocketEvents', function($rootScope, }); websocketCalls.sendNewEvent = function(data) { - console.log('Send >> %o, %o', data.op, data); + data.principal = $rootScope.ticket.principal; + data.ticket = $rootScope.ticket.ticket; + console.log('Send >> %o, %o, %o, %o', data.op, data.principal, data.ticket, data); websocketCalls.ws.send(JSON.stringify(data)); }; From 2017925ac5be911f9acccbfb50d841b98cba154d Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Thu, 31 Dec 2015 20:40:31 +0200 Subject: [PATCH 03/14] exclude SECURITY-README from rat check --- security-readme.md => SECURITY-README.md | 17 +++++++++++++++++ pom.xml | 1 + 2 files changed, 18 insertions(+) rename security-readme.md => SECURITY-README.md (54%) diff --git a/security-readme.md b/SECURITY-README.md similarity index 54% rename from security-readme.md rename to SECURITY-README.md index 2489329b29f..a3787050842 100644 --- a/security-readme.md +++ b/SECURITY-README.md @@ -1,3 +1,20 @@ +/* + * 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. + */ + #Shiro Authentication To connect to Zeppelin, users will be asked to enter their credentials. Once logged, a user has access to all notes including other users notes. This a a first step toward full security as implemented by this pull request (https://github.com/apache/incubator-zeppelin/pull/53). diff --git a/pom.xml b/pom.xml index 08f4b5044f4..77ef3d74425 100755 --- a/pom.xml +++ b/pom.xml @@ -468,6 +468,7 @@ DEPLOY.md CONTRIBUTING.md STYLE.md + SECURITY-README.md Roadmap.md **/licenses/** **/zeppelin-distribution/src/bin_license/** From 8eee51d5775f59379b79a88588b6ec5171512f32 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Thu, 31 Dec 2015 20:48:51 +0200 Subject: [PATCH 04/14] Remove cache optimization in shiro since it references stormpath and comes from there. --- conf/shiro.ini | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/conf/shiro.ini b/conf/shiro.ini index 8934ca764af..a592b4317dc 100644 --- a/conf/shiro.ini +++ b/conf/shiro.ini @@ -22,16 +22,6 @@ admin = password1 user1 = password2 user2 = password3 -[main] - -# Let's use some in-memory caching to reduce the number of runtime lookups against Stormpath. -# A real application might want to use a more robust caching solution (e.g. ehcache or a -# distributed cache). When using such caches, be aware of your cache TTL settings: too high -# a TTL and the cache won't reflect any potential changes in Stormpath fast enough. Too low -# and the cache could evict too often, reducing performance. -cacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager -securityManager.cacheManager = $cacheManager - [urls] From 6fd998242cda4747a9c3c928ff2a76341907244e Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Thu, 31 Dec 2015 21:24:13 +0200 Subject: [PATCH 05/14] Add minimal shiro.ini file for test phase --- zeppelin-server/src/main/resources/shiro.ini | 31 ++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 zeppelin-server/src/main/resources/shiro.ini diff --git a/zeppelin-server/src/main/resources/shiro.ini b/zeppelin-server/src/main/resources/shiro.ini new file mode 100644 index 00000000000..371a44e11e1 --- /dev/null +++ b/zeppelin-server/src/main/resources/shiro.ini @@ -0,0 +1,31 @@ +# +# 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. +# + +[users] +# List of users with their password allowed to access Zeppelin. +# To use a different strategy (LDAP / Database / ...) check the shiro doc at http://shiro.apache.org/configuration.html#Configuration-INISections +admin = password + + +[urls] + +# anon means the access is anonymous. +# authcBasic means Basic Auth Security +# To enfore security, comment the line below and uncomment the next one +/** = anon +#/** = authcBasic + From 96d1fac50a2ce546e82e21f6e72247bc9a1143ac Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Fri, 1 Jan 2016 11:53:37 +0200 Subject: [PATCH 06/14] correct comment in SECURITY-README and keep anonymous policy by default in zeppelin-site.xml.template --- SECURITY-README.md | 2 +- conf/zeppelin-site.xml.template | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SECURITY-README.md b/SECURITY-README.md index a3787050842..4ae2d86611d 100644 --- a/SECURITY-README.md +++ b/SECURITY-README.md @@ -21,7 +21,7 @@ This a a first step toward full security as implemented by this pull request (ht #Security setup 1. Secure the HTTP channel: Comment the line "/** = anon" and uncomment the line "/** = authcBasic" in the file conf/shiro.ini. Read more about he shiro.ini file format at the following URL http://shiro.apache.org/configuration.html#Configuration-INISections. -2. Secure the Websocket channel : Set to property "zeppelin.anonymous.allowed" to "true" in the file conf/zeppelin-site.xml. You can start by renaming conf/zeppelin-site.xml.template to conf/zeppelin-site.xml +2. Secure the Websocket channel : Set to property "zeppelin.anonymous.allowed" to "false" in the file conf/zeppelin-site.xml. You can start by renaming conf/zeppelin-site.xml.template to conf/zeppelin-site.xml 3. Start Zeppelin : bin/zeppelin.sh 4. point your browser to http://localhost:8080 5. Login using one of the user/password combinations defined in the conf/shiro.ini file. diff --git a/conf/zeppelin-site.xml.template b/conf/zeppelin-site.xml.template index 16a99e82632..8232be58d6c 100755 --- a/conf/zeppelin-site.xml.template +++ b/conf/zeppelin-site.xml.template @@ -182,7 +182,7 @@ zeppelin.anonymous.allowed - false + true Anonymous user allowed by default From 2a9e275638446fc0aa1619392379c01617d92d1c Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Fri, 1 Jan 2016 11:56:57 +0200 Subject: [PATCH 07/14] Add implementation notes --- SECURITY-README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/SECURITY-README.md b/SECURITY-README.md index 4ae2d86611d..73137ea0a80 100644 --- a/SECURITY-README.md +++ b/SECURITY-README.md @@ -15,16 +15,32 @@ * limitations under the License. */ -#Shiro Authentication +# Shiro Authentication To connect to Zeppelin, users will be asked to enter their credentials. Once logged, a user has access to all notes including other users notes. This a a first step toward full security as implemented by this pull request (https://github.com/apache/incubator-zeppelin/pull/53). -#Security setup +# Security setup 1. Secure the HTTP channel: Comment the line "/** = anon" and uncomment the line "/** = authcBasic" in the file conf/shiro.ini. Read more about he shiro.ini file format at the following URL http://shiro.apache.org/configuration.html#Configuration-INISections. 2. Secure the Websocket channel : Set to property "zeppelin.anonymous.allowed" to "false" in the file conf/zeppelin-site.xml. You can start by renaming conf/zeppelin-site.xml.template to conf/zeppelin-site.xml 3. Start Zeppelin : bin/zeppelin.sh 4. point your browser to http://localhost:8080 5. Login using one of the user/password combinations defined in the conf/shiro.ini file. - + +# Implementation notes +## Vocabulary +username, owner and principal are used interchangeably to designate the currently authenticated user +## What are we securing ? +Zeppelin is basically a web application that spawn remote interpreters to run commands and return HTML fragments to be displayed on the user browser. +The scope of this PR is to require credentials to access Zeppelin. To achieve this, we use Apache Shiro. +## HTTP Endpoint security +Apache Shiro sits as a servlet filter between the browser and the exposed services and handles the required authentication without any programming required. (See Apache Shiro for more info). +## Websocket security +Securing the HTTP endpoints is not enough, since Zeppelin also communicates with the browser through websockets. To secure this channel, we take the following approach: +1. The browser on startup requests a ticket through HTTP +2. The Apache Shiro Servlet filter handles the user auth +3. Once the user is authenticated, a ticket is assigned to this user and the ticket is returned to the browser + +All websockets communications require the username and ticket to be submitted by the browser. Upon receiving a websocket message, the server checks that the ticket received is the one assigned to the username through the HTTP request (step 3 above). + From 01ba5433228cdfba9ab7addc22386f28fdfd8dc8 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Sat, 2 Jan 2016 08:03:46 +0200 Subject: [PATCH 08/14] get ticket before Angular is bootstrapped --- zeppelin-web/src/app/app.js | 147 +++++++++++------- zeppelin-web/src/app/home/home.controller.js | 7 - .../components/navbar/navbar.controller.js | 31 +--- zeppelin-web/src/index.html | 2 +- 4 files changed, 91 insertions(+), 96 deletions(-) diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js index 92e7345db05..54555c899e7 100644 --- a/zeppelin-web/src/app/app.js +++ b/zeppelin-web/src/app/app.js @@ -15,65 +15,92 @@ * limitations under the License. */ 'use strict'; +(function() { + var zeppelinWebApp = angular.module('zeppelinWebApp', [ + 'ngAnimate', + 'ngCookies', + 'ngRoute', + 'ngSanitize', + 'angular-websocket', + 'ui.ace', + 'ui.bootstrap', + 'ui.sortable', + 'ngTouch', + 'ngDragDrop', + 'angular.filter', + 'monospaced.elastic', + 'puElasticInput', + 'xeditable', + 'ngToast', + 'focus-if', + 'ngResource' + ]) + .filter('breakFilter', function() { + return function (text) { + if (!!text) { + return text.replace(/\n/g, '
    '); + } + }; + }) + .config(function ($routeProvider, ngToastProvider) { + $routeProvider + .when('/', { + templateUrl: 'app/home/home.html' + }) + .when('/notebook/:noteId', { + templateUrl: 'app/notebook/notebook.html', + controller: 'NotebookCtrl' + }) + .when('/notebook/:noteId/paragraph?=:paragraphId', { + templateUrl: 'app/notebook/notebook.html', + controller: 'NotebookCtrl' + }) + .when('/notebook/:noteId/paragraph/:paragraphId?', { + templateUrl: 'app/notebook/notebook.html', + controller: 'NotebookCtrl' + }) + .when('/interpreter', { + templateUrl: 'app/interpreter/interpreter.html', + controller: 'InterpreterCtrl' + }) + .when('/search/:searchTerm', { + templateUrl: 'app/search/result-list.html', + controller: 'SearchResultCtrl' + }) + .otherwise({ + redirectTo: '/' + }); -angular.module('zeppelinWebApp', [ - 'ngAnimate', - 'ngCookies', - 'ngRoute', - 'ngSanitize', - 'angular-websocket', - 'ui.ace', - 'ui.bootstrap', - 'ui.sortable', - 'ngTouch', - 'ngDragDrop', - 'angular.filter', - 'monospaced.elastic', - 'puElasticInput', - 'xeditable', - 'ngToast', - 'focus-if', - 'ngResource' - ]) - .filter('breakFilter', function() { - return function (text) { - if (!!text) { - return text.replace(/\n/g, '
    '); - } - }; - }) - .config(function ($routeProvider, ngToastProvider) { - $routeProvider - .when('/', { - templateUrl: 'app/home/home.html' - }) - .when('/notebook/:noteId', { - templateUrl: 'app/notebook/notebook.html', - controller: 'NotebookCtrl' - }) - .when('/notebook/:noteId/paragraph?=:paragraphId', { - templateUrl: 'app/notebook/notebook.html', - controller: 'NotebookCtrl' - }) - .when('/notebook/:noteId/paragraph/:paragraphId?', { - templateUrl: 'app/notebook/notebook.html', - controller: 'NotebookCtrl' - }) - .when('/interpreter', { - templateUrl: 'app/interpreter/interpreter.html', - controller: 'InterpreterCtrl' - }) - .when('/search/:searchTerm', { - templateUrl: 'app/search/result-list.html', - controller: 'SearchResultCtrl' - }) - .otherwise({ - redirectTo: '/' - }); + ngToastProvider.configure({ + dismissButton: true, + dismissOnClick: false, + timeout: 6000 + }); + }); + + + function auth() { + var initInjector = angular.injector(['ng']); + var $http = initInjector.get('$http'); + + return $http.get('/api/security/ticket').then(function(response) { + zeppelinWebApp.run(function($rootScope) { + console.log(response); + $rootScope.ticket = angular.fromJson(response.data).body; + console.log($rootScope.ticket); + }); + }, function(errorResponse) { + // Handle error case + }); + } + + function bootstrapApplication() { + angular.element(document).ready(function() { + angular.bootstrap(document, ['zeppelinWebApp']); + }); + } + + auth().then(bootstrapApplication); + +}()); - ngToastProvider.configure({ - dismissButton: true, - dismissOnClick: false, - timeout: 6000 - }); - }); diff --git a/zeppelin-web/src/app/home/home.controller.js b/zeppelin-web/src/app/home/home.controller.js index 4ace50dc7b4..938ce7e2694 100644 --- a/zeppelin-web/src/app/home/home.controller.js +++ b/zeppelin-web/src/app/home/home.controller.js @@ -14,13 +14,6 @@ 'use strict'; angular.module('zeppelinWebApp').controller('HomeCtrl', function($scope, notebookListDataFactory, websocketMsgSrv, $rootScope, arrayOrderingSrv) { - if (!$rootScope.ticket) { - $rootScope.ticket = { - 'principal':'anonymous', - 'ticket':'anonymous' - }; - } - var vm = this; vm.notes = notebookListDataFactory; vm.websocketMsgSrv = websocketMsgSrv; diff --git a/zeppelin-web/src/components/navbar/navbar.controller.js b/zeppelin-web/src/components/navbar/navbar.controller.js index 480d18dae81..2f03e1a7b46 100644 --- a/zeppelin-web/src/components/navbar/navbar.controller.js +++ b/zeppelin-web/src/components/navbar/navbar.controller.js @@ -15,14 +15,7 @@ 'use strict'; angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootScope, $routeParams, - $location, notebookListDataFactory, websocketMsgSrv, arrayOrderingSrv, $http) { - if (!$rootScope.ticket) { - $rootScope.ticket = { - 'principal':'anonymous', - 'ticket':'anonymous' - }; - } - + $location, notebookListDataFactory, websocketMsgSrv, arrayOrderingSrv) { /** Current list of notes (ids) */ var vm = this; @@ -59,27 +52,9 @@ angular.module('zeppelinWebApp').controller('NavCtrl', function($scope, $rootSco websocketMsgSrv.getNotebookList(); } - /** ask for a ticket for websocket access - * Shiro will require credentials here - * */ - $http.get('/api/security/ticket'). - success(function(ticket, status, headers, config) { - if (status === 401 || status === 403) { - // Dislay error message here - } - else { - $rootScope.ticket = angular.fromJson(ticket).body; - vm.loadNotes = loadNotes; - vm.isActive = isActive; - vm.loadNotes(); - vm.authenticated = $rootScope.ticket.principal !== 'anonymous'; - } - }). - error(function(data, status, headers, config) { - console.log('Could not get ticket'); - }); + vm.authenticated = $rootScope.ticket.principal !== 'anonymous'; - function isActive(noteId) { + function isActive(noteId) { return ($routeParams.noteId === noteId); } diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html index 2b114654355..932b5150252 100644 --- a/zeppelin-web/src/index.html +++ b/zeppelin-web/src/index.html @@ -12,7 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. --> - + From 96ec24076164148bca70e80b0e75c18bbcd3eae9 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Sat, 2 Jan 2016 13:32:11 +0200 Subject: [PATCH 09/14] use standard HTML tags for SECURITY-README.md --- SECURITY-README.md | 29 +++++++++++++---------------- pom.xml | 1 - 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/SECURITY-README.md b/SECURITY-README.md index 73137ea0a80..2eb1fd64128 100644 --- a/SECURITY-README.md +++ b/SECURITY-README.md @@ -1,19 +1,16 @@ -/* - * 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. - */ + # Shiro Authentication To connect to Zeppelin, users will be asked to enter their credentials. Once logged, a user has access to all notes including other users notes. diff --git a/pom.xml b/pom.xml index 77ef3d74425..08f4b5044f4 100755 --- a/pom.xml +++ b/pom.xml @@ -468,7 +468,6 @@ DEPLOY.md CONTRIBUTING.md STYLE.md - SECURITY-README.md Roadmap.md **/licenses/** **/zeppelin-distribution/src/bin_license/** From 1372231743b7cdbadca3cf914b3c9492610057c9 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Mon, 4 Jan 2016 18:11:12 +0100 Subject: [PATCH 10/14] Test mode requires to user baseUrlSrv to connect to the REST API --- zeppelin-web/src/app/app.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js index 54555c899e7..d1f375345c5 100644 --- a/zeppelin-web/src/app/app.js +++ b/zeppelin-web/src/app/app.js @@ -82,12 +82,11 @@ function auth() { var initInjector = angular.injector(['ng']); var $http = initInjector.get('$http'); + var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv'); - return $http.get('/api/security/ticket').then(function(response) { + return $http.get(baseUrlSrv.getRestApiBase()+'/security/ticket').then(function(response) { zeppelinWebApp.run(function($rootScope) { - console.log(response); $rootScope.ticket = angular.fromJson(response.data).body; - console.log($rootScope.ticket); }); }, function(errorResponse) { // Handle error case @@ -95,12 +94,13 @@ } function bootstrapApplication() { - angular.element(document).ready(function() { - angular.bootstrap(document, ['zeppelinWebApp']); - }); + angular.bootstrap(document, ['zeppelinWebApp']); } - auth().then(bootstrapApplication); + + angular.element(document).ready(function() { + auth().then(bootstrapApplication); + }); }()); From 30736a05b4815e79afa782fc4cb3d1a58f236f82 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Tue, 5 Jan 2016 23:51:09 +0100 Subject: [PATCH 11/14] Add support for cross site requests with credentials --- zeppelin-web/src/app/app.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/zeppelin-web/src/app/app.js b/zeppelin-web/src/app/app.js index d1f375345c5..364ede76b51 100644 --- a/zeppelin-web/src/app/app.js +++ b/zeppelin-web/src/app/app.js @@ -42,7 +42,10 @@ } }; }) - .config(function ($routeProvider, ngToastProvider) { + .config(function ($httpProvider, $routeProvider, ngToastProvider) { + // withCredentials when running locally via grunt + $httpProvider.defaults.withCredentials = true; + $routeProvider .when('/', { templateUrl: 'app/home/home.html' @@ -80,9 +83,10 @@ function auth() { - var initInjector = angular.injector(['ng']); - var $http = initInjector.get('$http'); + var $http = angular.injector(['ng']).get('$http'); var baseUrlSrv = angular.injector(['zeppelinWebApp']).get('baseUrlSrv'); + // withCredentials when running locally via grunt + $http.defaults.withCredentials = true; return $http.get(baseUrlSrv.getRestApiBase()+'/security/ticket').then(function(response) { zeppelinWebApp.run(function($rootScope) { From 7200e773e19813da036d56c61065616f83f7eec7 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Wed, 6 Jan 2016 08:44:42 +0100 Subject: [PATCH 12/14] Default ticket / principal to anonymous in websocket message --- .../src/main/java/org/apache/zeppelin/socket/Message.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java index 6315f119c4d..c6f9abe28f7 100644 --- a/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java +++ b/zeppelin-server/src/main/java/org/apache/zeppelin/socket/Message.java @@ -102,8 +102,8 @@ public static enum OP { public OP op; public Map data = new HashMap(); - public String ticket; - public String principal; + public String ticket = "anonymous"; + public String principal = "anonymous"; public Message(OP op) { this.op = op; From 5485dcdc3da754b308f589493e2d63e727ea1066 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Fri, 8 Jan 2016 06:35:04 +0100 Subject: [PATCH 13/14] Updates licences for shiro-core and shiro-web introduced in this PR --- zeppelin-distribution/src/bin_license/LICENSE | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE index 52ce3ebc7f2..86600b23fe0 100644 --- a/zeppelin-distribution/src/bin_license/LICENSE +++ b/zeppelin-distribution/src/bin_license/LICENSE @@ -91,6 +91,8 @@ The following components are provided under Apache License. (Apache 2.0) Lucene Suggest (org.apache.lucene:lucene-suggest:5.3.1 - http://lucene.apache.org/lucene-parent/lucene-suggest) (Apache 2.0) Elasticsearch: Core (org.elasticsearch:elasticsearch:2.1.0 - http://nexus.sonatype.org/oss-repository-hosting.html/parent/elasticsearch) (Apache 2.0) Joda convert (org.joda:joda-convert:1.2 - http://joda-convert.sourceforge.net) + (Apache 2.0) Shiro Core (org.apache.shiro:shiro-core:1.2.3 - https://shiro.apache.org) + (Apache 2.0) Shiro Web (org.apache.shiro:shiro-web:1.2.3 - https://shiro.apache.org) (Apache 2.0) SnakeYAML (org.yaml:snakeyaml:1.15 - http://www.snakeyaml.org) From 47421b88922c000ca85c39bb68df9bf80b8eae36 Mon Sep 17 00:00:00 2001 From: Hayssam Saleh Date: Fri, 8 Jan 2016 08:37:24 +0100 Subject: [PATCH 14/14] Rollback classpath change since zeppelin conf dir already in classpath --- bin/zeppelin.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/zeppelin.sh b/bin/zeppelin.sh index bf620f156ee..17950e8bbdd 100755 --- a/bin/zeppelin.sh +++ b/bin/zeppelin.sh @@ -85,4 +85,4 @@ if [[ ! -d "${ZEPPELIN_NOTEBOOK_DIR}" ]]; then $(mkdir -p "${ZEPPELIN_NOTEBOOK_DIR}") fi -$(exec $ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CONF_DIR/shiro.ini:$ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_SERVER "$@") +$(exec $ZEPPELIN_RUNNER $JAVA_OPTS -cp $ZEPPELIN_CLASSPATH_OVERRIDES:$CLASSPATH $ZEPPELIN_SERVER "$@")