Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions conf/shiro.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
# 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
user1 = password2, role1, role2
user2 = password3, role3
user3 = password4, role2

# Sample LDAP configuration, for user Authentication, currently tested for single Realm
[main]
Expand Down
6 changes: 6 additions & 0 deletions docs/_includes/themes/zeppelin/_navigation.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@
<li><a href="{{BASE_PATH}}/rest-api/rest-notebook.html">Notebook API</a></li>
<li><a href="{{BASE_PATH}}/rest-api/rest-configuration.html">Configuration API</a></li>
<li role="separator" class="divider"></li>
<!-- li><span><b>Security</b><span></li -->
<li><a href="{{BASE_PATH}}/security/overview.html">Security Overview</a></li>
<li><a href="{{BASE_PATH}}/security/authentication.html">Authentication</a></li>
<li><a href="{{BASE_PATH}}/security/notebook_authorization.html">Notebook Authorization</a></li>
<li><a href="{{BASE_PATH}}/security/interpreter_authorization.html">Interpreter Authorization</a></li>
<li role="separator" class="divider"></li>
<!-- li><span><b>Development</b><span></li -->
<li><a href="{{BASE_PATH}}/development/writingzeppelininterpreter.html">Writing Zeppelin Interpreter</a></li>
<li><a href="{{BASE_PATH}}/development/howtocontribute.html">How to contribute (code)</a></li>
Expand Down
31 changes: 31 additions & 0 deletions docs/security/authentication.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
layout: page
title: "Authentication"
description: "Authentication"
group: security
---
<!--
Licensed 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.
-->
# Authentication

Authentication is company-specific.

One option is to use [Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication)

Another option is to have an authentication server that can verify user credentials in an LDAP server.
If an incoming request to the Zeppelin server does not have a cookie with user information encrypted with the authentication server public key, the user
is redirected to the authentication server. Once the user is verified, the authentication server redirects the browser to a specific
URL in the Zeppelin server which sets the authentication cookie in the browser.
The end result is that all requests to the Zeppelin
web server have the authentication cookie which contains user and groups information.
34 changes: 34 additions & 0 deletions docs/security/interpreter_authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
layout: page
title: "Notebook Authorization"
description: "Notebook Authorization"
group: security
---
<!--
Licensed 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.
-->
# Interpreter and Data Source Authorization

## Interpreter Authorization

Interpreter authorization involves permissions like creating an interpreter and execution queries using it.

## Data Source Authorization

Data source authorization involves authenticating to the data source like a Mysql database and letting it determine user permissions.

For the Hive interpreter, we need to maintain per-user connection pools.
The interpret method takes the user string as parameter and executes the jdbc call using a connection in the user's connection pool.

In case of Presto, we don't need password if the Presto DB server runs backend code using HDFS authorization for the user.
For databases like Vertica and Mysql we have to store password information for users.
37 changes: 37 additions & 0 deletions docs/security/notebook_authorization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
layout: page
title: "Notebook Authorization"
description: "Notebook Authorization"
group: security
---
<!--
Licensed 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.
-->
# Notebook Authorization

We assume that there is an authentication component that associates a user string and a set of group strings with every NotebookSocket.

Each note has the following:
* set of owner entities (users or groups)
* set of reader entities (users or groups)
* set of writer entities (users or groups)

If a set is empty, it means that any user can perform that operation.

The NotebookServer classifies every Note operation into three categories: read, write, manage.
Before executing a Note operation, it checks if the user and the groups associated with the NotebookSocket have permissions. For example, before executing an read
operation, it checks if the user and the groups have at least one entity that belongs to the reader entities.

To initialize and modify note permissions, we provide UI like "Interpreter binding". The user inputs comma separated entities for owners, readers and writers.
We execute a rest api call with this information. In the backend we get the user information for the connection and allow the operation if the user and groups
associated with the current user have at least one entity that belongs to owner entities for the note.
28 changes: 28 additions & 0 deletions docs/security/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
layout: page
title: "Security Overview"
description: "Security Overview"
group: security
---
<!--
Licensed 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.
-->
{% include JB/setup %}

# Security Overview

There are three aspects to Zeppelin security:

* Authentication: is the user who they say they are? [More](authentication.html)
* Notebook authorization: does the user have permissions to read or write to a note? [More](notebook_authorization.html)
* Interpreter and data source authorization: does the user have permissions to perform interpreter operations or access data source objects? [More](interpreter_authorization.html)
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package org.apache.zeppelin.rest;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -46,6 +48,7 @@
import org.apache.zeppelin.search.SearchService;
import org.apache.zeppelin.server.JsonResponse;
import org.apache.zeppelin.socket.NotebookServer;
import org.apache.zeppelin.utils.SecurityUtils;
import org.quartz.CronExpression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -75,6 +78,66 @@ public NotebookRestApi(Notebook notebook, NotebookServer notebookServer, SearchS
this.notebookIndex = search;
}

/**
* list note owners
*/
@GET
@Path("{noteId}/permissions")
public Response getNotePermissions(@PathParam("noteId") String noteId) {
Note note = notebook.getNote(noteId);
HashMap<String, HashSet> permissionsMap = new HashMap<String, HashSet>();
permissionsMap.put("owners", note.getOwners());
permissionsMap.put("readers", note.getReaders());
permissionsMap.put("writers", note.getWriters());
return new JsonResponse<>(Status.OK, "", permissionsMap).build();
}

String ownerPermissionError(HashSet<String> current,
HashSet<String> allowed) throws IOException {
LOG.info("Cannot change permissions. Connection owners {}. Allowed owners {}",
current.toString(), allowed.toString());
return "Insufficient privileges to change permissions.\n\n" +
"Allowed owners: " + allowed.toString() + "\n\n" +
"User belongs to: " + current.toString();
}

/**
* Set note owners
*/
@PUT
@Path("{noteId}/permissions")
public Response putNotePermissions(@PathParam("noteId") String noteId, String req)
throws IOException {
HashMap<String, HashSet> permMap = gson.fromJson(req,
new TypeToken<HashMap<String, HashSet>>(){}.getType());
Note note = notebook.getNote(noteId);
String principal = SecurityUtils.getPrincipal();
HashSet<String> roles = SecurityUtils.getRoles();
LOG.info("Set permissions {} {} {} {} {}",
noteId,
principal,
permMap.get("owners"),
permMap.get("readers"),
permMap.get("writers")
);

HashSet<String> userAndRoles = new HashSet<String>();
userAndRoles.add(principal);
userAndRoles.addAll(roles);
if (!note.isOwner(userAndRoles)) {
return new JsonResponse<>(Status.FORBIDDEN, ownerPermissionError(userAndRoles,
note.getOwners())).build();
}
note.setOwners(permMap.get("owners"));
note.setReaders(permMap.get("readers"));
note.setWriters(permMap.get("writers"));
LOG.debug("After set permissions {} {} {}", note.getOwners(), note.getReaders(),
note.getWriters());
note.persist();
notebookServer.broadcastNote(note);
return new JsonResponse<>(Status.OK).build();
}

/**
* bind a setting to note
* @throws IOException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@
import org.apache.zeppelin.server.JsonResponse;
import org.apache.zeppelin.ticket.TicketContainer;
import org.apache.zeppelin.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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.HashSet;
import java.util.Map;

/**
Expand All @@ -36,6 +39,8 @@
@Path("/security")
@Produces("application/json")
public class SecurityRestApi {
private static final Logger LOG = LoggerFactory.getLogger(SecurityRestApi.class);

/**
* Required by Swagger.
*/
Expand All @@ -56,6 +61,7 @@ public SecurityRestApi() {
public Response ticket() {
ZeppelinConfiguration conf = ZeppelinConfiguration.create();
String principal = SecurityUtils.getPrincipal();
HashSet<String> roles = SecurityUtils.getRoles();
JsonResponse response;
// ticket set to anonymous for anonymous user. Simplify testing.
String ticket;
Expand All @@ -66,9 +72,11 @@ public Response ticket() {

Map<String, String> data = new HashMap<>();
data.put("principal", principal);
data.put("roles", roles.toString());
data.put("ticket", ticket);

response = new JsonResponse(Response.Status.OK, "", data);
LOG.warn(response.toString());
return response.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public static enum OP {
PARAGRAPH_APPEND_OUTPUT, // [s-c] append output
PARAGRAPH_UPDATE_OUTPUT, // [s-c] update (replace) output
PING,
AUTH_INFO,

ANGULAR_OBJECT_UPDATE, // [s-c] add/update angular object
ANGULAR_OBJECT_REMOVE, // [s-c] add angular object del
Expand All @@ -116,6 +117,7 @@ public static enum OP {
public Map<String, Object> data = new HashMap<String, Object>();
public String ticket = "anonymous";
public String principal = "anonymous";
public String roles = "";

public Message(OP op) {
this.op = op;
Expand Down
Loading