You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by bz...@apache.org on 2016/06/30 08:19:28 UTC
zeppelin git commit: [ZEPPELIN-1023] Add more credential apis.
Repository: zeppelin
Updated Branches:
refs/heads/master 3a338e01e -> c348161df
[ZEPPELIN-1023] Add more credential apis.
### What is this PR for?
This PR is for supporting various Credential APIs for users.
### What type of PR is it?
Improvement
### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-1023
### How should this be tested?
- to create credential information.
```
curl -XPUT -H "Content-Type: application/json" "http://localhost:8080/api/credential" -d '{"entity" : "e1", "username" : "user1", "password" : "testpass"}'
```
- to get credential information.
```
curl -XGET -H "Content-Type: application/json" "http://localhost:8080/api/credential"
```
- to remove credential entity information.
```
curl -XDELETE -H "Content-Type: application/json" "http://localhost:8080/api/credential/e1"
```
- to remove all credential information.
```
curl -XDELETE -H "Content-Type: application/json" "http://localhost:8080/api/credential"
```
### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no
Author: astroshim <hs...@nflabs.com>
Author: AhyoungRyu <fb...@hanmail.net>
Author: HyungSung <hs...@nflabs.com>
Closes #1030 from astroshim/ZEPPELIN-1023 and squashes the following commits:
1b94ae1 [astroshim] Merge branch 'master' into ZEPPELIN-1023
a580476 [astroshim] erase comment code
0609931 [astroshim] update credentialsMap
0f503fd [astroshim] to fix ci
d0c2bac [astroshim] changed syncronizedmap to concurrenthashmap
7b2a7c5 [HyungSung] Merge pull request #7 from AhyoungRyu/ZEPPELIN-1023-docs
80d8a30 [AhyoungRyu] Add rest-credential.md to index.md & dropdown menu
f546532 [astroshim] add rest-credential document
26433f2 [astroshim] change using syncronizedMap.
9b2c1c9 [astroshim] add checking null and blank values in the putCredentials method.
0371701 [astroshim] add more credential apis.
Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/c348161d
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/c348161d
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/c348161d
Branch: refs/heads/master
Commit: c348161df00d5db72943f96527ac52d0e5419751
Parents: 3a338e0
Author: astroshim <hs...@nflabs.com>
Authored: Mon Jun 27 22:38:47 2016 +0900
Committer: Alexander Bezzubov <bz...@apache.org>
Committed: Thu Jun 30 17:19:20 2016 +0900
----------------------------------------------------------------------
docs/_includes/themes/zeppelin/_navigation.html | 1 +
docs/index.md | 1 +
docs/rest-api/rest-credential.md | 181 +++++++++++++++++++
.../org/apache/zeppelin/user/Credentials.java | 25 ++-
.../apache/zeppelin/user/UserCredentials.java | 16 +-
.../apache/zeppelin/rest/CredentialRestApi.java | 66 ++++++-
.../zeppelin/rest/CredentialsRestApiTest.java | 60 +++++-
7 files changed, 334 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/docs/_includes/themes/zeppelin/_navigation.html
----------------------------------------------------------------------
diff --git a/docs/_includes/themes/zeppelin/_navigation.html b/docs/_includes/themes/zeppelin/_navigation.html
index b5703af..fce8cee 100644
--- a/docs/_includes/themes/zeppelin/_navigation.html
+++ b/docs/_includes/themes/zeppelin/_navigation.html
@@ -94,6 +94,7 @@
<li><a href="{{BASE_PATH}}/rest-api/rest-interpreter.html">Interpreter API</a></li>
<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><a href="{{BASE_PATH}}/rest-api/rest-credential.html">Credential API</a></li>
<li role="separator" class="divider"></li>
<li class="title"><span><b>Security</b><span></li>
<li><a href="{{BASE_PATH}}/security/authentication.html">Authentication for NGINX</a></li>
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/docs/index.md
----------------------------------------------------------------------
diff --git a/docs/index.md b/docs/index.md
index 9847aa4..10fc7e1 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -162,6 +162,7 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor
* [Interpreter API](./rest-api/rest-interpreter.html)
* [Notebook API](./rest-api/rest-notebook.html)
* [Configuration API](./rest-api/rest-configuration.html)
+ * [Credential API](./rest-api/rest-credential.html)
* Security: available security support in Apache Zeppelin
* [Authentication for NGINX](./security/authentication.html)
* [Shiro Authentication](./security/shiroauthentication.html)
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/docs/rest-api/rest-credential.md
----------------------------------------------------------------------
diff --git a/docs/rest-api/rest-credential.md b/docs/rest-api/rest-credential.md
new file mode 100644
index 0000000..a01f335
--- /dev/null
+++ b/docs/rest-api/rest-credential.md
@@ -0,0 +1,181 @@
+---
+layout: page
+title: "Credentials REST API"
+description: ""
+group: rest-api
+---
+<!--
+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 %}
+
+## Zeppelin REST API
+ Zeppelin provides several REST APIs for interaction and remote activation of zeppelin functionality.
+
+ All REST APIs are available starting with the following endpoint `http://[zeppelin-server]:[zeppelin-port]/api`. Note that zeppelin REST APIs receive or return JSON objects, it is recommended for you to install some JSON viewers such as [JSONView](https://chrome.google.com/webstore/detail/jsonview/chklaanhfefbnpoihckbnefhakgolnmc).
+
+ If you work with Zeppelin and find a need for an additional REST API, please [file an issue or send us mail](http://zeppelin.apache.org/community.html).
+
+ <br />
+## Credential REST API List
+
+### List Credential information
+ <table class="table-credential">
+ <col width="200">
+ <tr>
+ <td>Description</td>
+ <td>This ```GET``` method returns all key/value pairs of credential information on the server.</td>
+ </tr>
+ <tr>
+ <td>URL</td>
+ <td>```http://[zeppelin-server]:[zeppelin-port]/api/credential```</td>
+ </tr>
+ <tr>
+ <td>Success code</td>
+ <td>200</td>
+ </tr>
+ <tr>
+ <td> Fail code</td>
+ <td> 500 </td>
+ </tr>
+ <tr>
+ <td> sample JSON response
+ </td>
+ <td>
+ <pre>
+{
+ "status": "OK",
+ "message": "",
+ "body": {
+ "userCredentials":{
+ "entity1":{
+ "username":"user1",
+ "password":"password1"
+ },
+ "entity2":{
+ "username":"user2",
+ "password":"password2"
+ }
+ }
+ }
+}</pre></td>
+ </tr>
+ </table>
+
+<br/>
+### Create an Credential Information
+ <table class="table-credential">
+ <col width="200">
+ <tr>
+ <td>Description</td>
+ <td>This ```PUT``` method creates an credential information with new properties.</td>
+ </tr>
+ <tr>
+ <td>URL</td>
+ <td>```http://[zeppelin-server]:[zeppelin-port]/api/credential/```</td>
+ </tr>
+ <tr>
+ <td>Success code</td>
+ <td>200</td>
+ </tr>
+ <tr>
+ <td>Fail code</td>
+ <td> 500 </td>
+ </tr>
+ <tr>
+ <td>Sample JSON input</td>
+ <td>
+ <pre>
+{
+ "entity": "e1",
+ "username": "user",
+ "password": "password"
+}
+ </pre>
+ </td>
+ </tr>
+ <tr>
+ <td>Sample JSON response</td>
+ <td>
+ <pre>
+{
+ "status": "OK"
+}
+ </pre>
+ </td>
+ </tr>
+ </table>
+
+
+<br/>
+### Delete all Credential Information
+
+ <table class="table-credential">
+ <col width="200">
+ <tr>
+ <td>Description</td>
+ <td>This ```DELETE``` method deletes credential information.</td>
+ </tr>
+ <tr>
+ <td>URL</td>
+ <td>```http://[zeppelin-server]:[zeppelin-port]/api/credential```</td>
+ </tr>
+ <tr>
+ <td>Success code</td>
+ <td>200</td>
+ </tr>
+ <tr>
+ <td> Fail code</td>
+ <td> 500 </td>
+ </tr>
+ <tr>
+ <td>Sample JSON response</td>
+ <td>
+ <code>{"status":"OK"}</code>
+ </td>
+ </tr>
+ </table>
+
+
+<br/>
+### Delete an Credential entity
+
+ <table class="table-credential">
+ <col width="200">
+ <tr>
+ <td>Description</td>
+ <td>This ```DELETE``` method deletes an given credential entity.</td>
+ </tr>
+ <tr>
+ <td>URL</td>
+ <td>```http://[zeppelin-server]:[zeppelin-port]/api/credential/[entity]```</td>
+ </tr>
+ <tr>
+ <td>Success code</td>
+ <td>200</td>
+ </tr>
+ <tr>
+ <td> Fail code</td>
+ <td> 500 </td>
+ </tr>
+ <tr>
+ <td>Sample JSON response</td>
+ <td>
+ <code>{"status":"OK"}</code>
+ </td>
+ </tr>
+ </table>
+
+
+<br/>
+
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java
index b3a3726..72d44e9 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java
@@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* Class defining credentials for data source authorization
@@ -44,6 +45,7 @@ public class Credentials {
credentialsFile = new File(credentialsPath);
}
credentialsMap = new HashMap<>();
+
if (credentialsPersist) {
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
@@ -62,6 +64,28 @@ public class Credentials {
public void putUserCredentials(String username, UserCredentials uc) throws IOException {
credentialsMap.put(username, uc);
+ saveCredentials();
+ }
+
+ public UserCredentials removeUserCredentials(String username) throws IOException {
+ UserCredentials uc;
+ uc = credentialsMap.remove(username);
+ saveCredentials();
+ return uc;
+ }
+
+ public boolean removeCredentialEntity(String username, String entity) throws IOException {
+ UserCredentials uc = credentialsMap.get(username);
+ if (uc != null && uc.existUsernamePassword(entity) == false) {
+ return false;
+ }
+
+ uc.removeUsernamePassword(entity);
+ saveCredentials();
+ return true;
+ }
+
+ public void saveCredentials() throws IOException {
if (credentialsPersist) {
saveToFile();
}
@@ -118,5 +142,4 @@ public class Credentials {
LOG.error("Error saving credentials file", e);
}
}
-
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java
index 166840a..f952866 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java
@@ -17,18 +17,14 @@
package org.apache.zeppelin.user;
-import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
/**
* User Credentials POJO
*/
public class UserCredentials {
- private Map<String, UsernamePassword> userCredentials;
-
- public UserCredentials() {
- this.userCredentials = new HashMap<>();
- }
+ private Map<String, UsernamePassword> userCredentials = new ConcurrentHashMap<>();
public UsernamePassword getUsernamePassword(String entity) {
return userCredentials.get(entity);
@@ -38,6 +34,14 @@ public class UserCredentials {
userCredentials.put(entity, up);
}
+ public void removeUsernamePassword(String entity) {
+ userCredentials.remove(entity);
+ }
+
+ public boolean existUsernamePassword(String entity) {
+ return userCredentials.containsKey(entity);
+ }
+
@Override
public String toString() {
return "UserCredentials{" +
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java
old mode 100644
new mode 100755
index 6904a32..74412c4
--- a/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java
@@ -17,6 +17,7 @@
package org.apache.zeppelin.rest;
+import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.zeppelin.user.Credentials;
@@ -50,7 +51,6 @@ public class CredentialRestApi {
private HttpServletRequest servReq;
public CredentialRestApi() {
-
}
public CredentialRestApi(Credentials credentials) {
@@ -58,18 +58,22 @@ public class CredentialRestApi {
}
/**
- * Update credentials for current user
+ * Put User Credentials REST API
+ * @param message - JSON with entity, username, password.
+ * @return JSON with status.OK
+ * @throws IOException, IllegalArgumentException
*/
@PUT
- public Response putCredentials(String message) throws IOException {
+ public Response putCredentials(String message) throws IOException, IllegalArgumentException {
Map<String, String> messageMap = gson.fromJson(message,
new TypeToken<Map<String, String>>(){}.getType());
String entity = messageMap.get("entity");
String username = messageMap.get("username");
String password = messageMap.get("password");
- if (entity == null || username == null || password == null) {
- return new JsonResponse(Status.BAD_REQUEST, "", "").build();
+ if (Strings.isNullOrEmpty(entity)
+ || Strings.isNullOrEmpty(username) || Strings.isNullOrEmpty(password) ) {
+ return new JsonResponse(Status.BAD_REQUEST).build();
}
String user = SecurityUtils.getPrincipal();
@@ -77,7 +81,57 @@ public class CredentialRestApi {
UserCredentials uc = credentials.getUserCredentials(user);
uc.putUsernamePassword(entity, new UsernamePassword(username, password));
credentials.putUserCredentials(user, uc);
- return new JsonResponse(Status.OK, "", "").build();
+ return new JsonResponse(Status.OK).build();
+ }
+
+ /**
+ * Get User Credentials list REST API
+ * @param
+ * @return JSON with status.OK
+ * @throws IOException, IllegalArgumentException
+ */
+ @GET
+ public Response getCredentials(String message) throws
+ IOException, IllegalArgumentException {
+ String user = SecurityUtils.getPrincipal();
+ logger.info("getCredentials credentials for user {} ", user);
+ UserCredentials uc = credentials.getUserCredentials(user);
+ return new JsonResponse(Status.OK, uc).build();
+ }
+
+ /**
+ * Remove User Credentials REST API
+ * @param
+ * @return JSON with status.OK
+ * @throws IOException, IllegalArgumentException
+ */
+ @DELETE
+ public Response removeCredentials(String message) throws
+ IOException, IllegalArgumentException {
+ String user = SecurityUtils.getPrincipal();
+ logger.info("removeCredentials credentials for user {} ", user);
+ UserCredentials uc = credentials.removeUserCredentials(user);
+ if (uc == null) {
+ return new JsonResponse(Status.NOT_FOUND).build();
+ }
+ return new JsonResponse(Status.OK).build();
}
+ /**
+ * Remove Entity of User Credential entity REST API
+ * @param
+ * @return JSON with status.OK
+ * @throws IOException, IllegalArgumentException
+ */
+ @DELETE
+ @Path("{entity}")
+ public Response removeCredentialEntity(@PathParam("entity") String entity) throws
+ IOException, IllegalArgumentException {
+ String user = SecurityUtils.getPrincipal();
+ logger.info("removeCredentialEntity for user {} entity {}", user, entity);
+ if (credentials.removeCredentialEntity(user, entity) == false) {
+ return new JsonResponse(Status.NOT_FOUND).build();
+ }
+ return new JsonResponse(Status.OK).build();
+ }
}
http://git-wip-us.apache.org/repos/asf/zeppelin/blob/c348161d/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java
----------------------------------------------------------------------
diff --git a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java
index 674c47e..29c2914 100644
--- a/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java
+++ b/zeppelin-server/src/test/java/org/apache/zeppelin/rest/CredentialsRestApiTest.java
@@ -19,19 +19,26 @@ package org.apache.zeppelin.rest;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
+import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.zeppelin.notebook.Note;
+import org.apache.zeppelin.server.ZeppelinServer;
+import org.apache.zeppelin.user.UserCredentials;
+import org.apache.zeppelin.utils.SecurityUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.*;
public class CredentialsRestApiTest extends AbstractTestRestApi {
+ protected static final Logger LOG = LoggerFactory.getLogger(CredentialsRestApiTest.class);
Gson gson = new Gson();
@BeforeClass
@@ -72,5 +79,52 @@ public class CredentialsRestApiTest extends AbstractTestRestApi {
allNullPut.releaseConnection();
}
-}
+ public Map<String, UserCredentials> testGetUserCredentials() throws IOException {
+ GetMethod getMethod = httpGet("/credential");
+ getMethod.addRequestHeader("Origin", "http://localhost");
+ Map<String, Object> resp = gson.fromJson(getMethod.getResponseBodyAsString(),
+ new TypeToken<Map<String, Object>>(){}.getType());
+ Map<String, Object> body = (Map<String, Object>) resp.get("body");
+ Map<String, UserCredentials> credentialMap = (Map<String, UserCredentials>)body.get("userCredentials");
+ getMethod.releaseConnection();
+ return credentialMap;
+ }
+
+ public void testPutUserCredentials(String requestData) throws IOException {
+ PutMethod putMethod = httpPut("/credential", requestData);
+ putMethod.addRequestHeader("Origin", "http://localhost");
+ assertThat(putMethod, isAllowed());
+ putMethod.releaseConnection();
+ }
+
+ public void testRemoveUserCredentials() throws IOException {
+ DeleteMethod deleteMethod = httpDelete("/credential/");
+ assertThat("Test delete method:", deleteMethod, isAllowed());
+ deleteMethod.releaseConnection();
+ }
+
+ public void testRemoveCredentialEntity(String entity) throws IOException {
+ DeleteMethod deleteMethod = httpDelete("/credential/" + entity);
+ assertThat("Test delete method:", deleteMethod, isAllowed());
+ deleteMethod.releaseConnection();
+ }
+
+ @Test
+ public void testCredentialsAPIs() throws IOException {
+ String requestData1 = "{\"entity\" : \"entityname\", \"username\" : \"myuser\", \"password\" : \"mypass\"}";
+ String entity = "entityname";
+ Map<String, UserCredentials> credentialMap;
+
+ testPutUserCredentials(requestData1);
+ credentialMap = testGetUserCredentials();
+ assertNotNull("CredentialMap should be null", credentialMap);
+ testRemoveCredentialEntity(entity);
+ credentialMap = testGetUserCredentials();
+ assertNull("CredentialMap should be null", credentialMap.get("entity1"));
+
+ testRemoveUserCredentials();
+ credentialMap = testGetUserCredentials();
+ assertEquals("Compare CredentialMap", credentialMap.toString(), "{}");
+ }
+}