diff --git a/.gitignore b/.gitignore
index 532891481d9..e13f0e94d47 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@ conf/truststore
conf/interpreter.json
conf/notebook-authorization.json
conf/shiro.ini
+conf/credentials.json
# other generated files
spark/dependency-reduced-pom.xml
diff --git a/docs/security/interpreter_authorization.md b/docs/security/interpreter_authorization.md
index d1c792d113f..862ef9b828b 100644
--- a/docs/security/interpreter_authorization.md
+++ b/docs/security/interpreter_authorization.md
@@ -32,3 +32,5 @@ The interpret method takes the user string as parameter and executes the jdbc ca
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.
+
+The Credentials tab in the navbar allows users to save credentials for data sources which are passed to interpreters.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 833f219dc37..6dba9d760d3 100755
--- a/pom.xml
+++ b/pom.xml
@@ -471,6 +471,7 @@
**/zeppelin-distribution/src/bin_license/**conf/interpreter.jsonconf/notebook-authorization.json
+ conf/credentials.jsonconf/zeppelin-env.shspark-*-bin*/**.spark-dist/**
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java
index 5d54342c531..c4cd441497a 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/AuthenticationInfo.java
@@ -24,6 +24,7 @@
public class AuthenticationInfo {
String user;
String ticket;
+ UserCredentials userCredentials;
public AuthenticationInfo() {}
@@ -52,4 +53,13 @@ public String getTicket() {
public void setTicket(String ticket) {
this.ticket = ticket;
}
+
+ public UserCredentials getUserCredentials() {
+ return userCredentials;
+ }
+
+ public void setUserCredentials(UserCredentials userCredentials) {
+ this.userCredentials = userCredentials;
+ }
+
}
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
new file mode 100644
index 00000000000..b3a3726f845
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/Credentials.java
@@ -0,0 +1,122 @@
+/*
+* 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.user;
+
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.*;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Class defining credentials for data source authorization
+ */
+public class Credentials {
+ private static final Logger LOG = LoggerFactory.getLogger(Credentials.class);
+
+ private Map credentialsMap;
+ private Gson gson;
+ private Boolean credentialsPersist = true;
+ File credentialsFile;
+
+ public Credentials(Boolean credentialsPersist, String credentialsPath) {
+ this.credentialsPersist = credentialsPersist;
+ if (credentialsPath != null) {
+ credentialsFile = new File(credentialsPath);
+ }
+ credentialsMap = new HashMap<>();
+ if (credentialsPersist) {
+ GsonBuilder builder = new GsonBuilder();
+ builder.setPrettyPrinting();
+ gson = builder.create();
+ loadFromFile();
+ }
+ }
+
+ public UserCredentials getUserCredentials(String username) {
+ UserCredentials uc = credentialsMap.get(username);
+ if (uc == null) {
+ uc = new UserCredentials();
+ }
+ return uc;
+ }
+
+ public void putUserCredentials(String username, UserCredentials uc) throws IOException {
+ credentialsMap.put(username, uc);
+ if (credentialsPersist) {
+ saveToFile();
+ }
+ }
+
+ private void loadFromFile() {
+ LOG.info(credentialsFile.getAbsolutePath());
+ if (!credentialsFile.exists()) {
+ // nothing to read
+ return;
+ }
+
+ try {
+ FileInputStream fis = new FileInputStream(credentialsFile);
+ InputStreamReader isr = new InputStreamReader(fis);
+ BufferedReader bufferedReader = new BufferedReader(isr);
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ sb.append(line);
+ }
+ isr.close();
+ fis.close();
+
+ String json = sb.toString();
+ CredentialsInfoSaving info = gson.fromJson(json, CredentialsInfoSaving.class);
+ this.credentialsMap = info.credentialsMap;
+ } catch (IOException e) {
+ LOG.error("Error loading credentials file", e);
+ e.printStackTrace();
+ }
+ }
+
+ private void saveToFile() throws IOException {
+ String jsonString;
+
+ synchronized (credentialsMap) {
+ CredentialsInfoSaving info = new CredentialsInfoSaving();
+ info.credentialsMap = credentialsMap;
+ jsonString = gson.toJson(info);
+ }
+
+ try {
+ if (!credentialsFile.exists()) {
+ credentialsFile.createNewFile();
+ }
+
+ FileOutputStream fos = new FileOutputStream(credentialsFile, false);
+ OutputStreamWriter out = new OutputStreamWriter(fos);
+ out.append(jsonString);
+ out.close();
+ fos.close();
+ } catch (IOException e) {
+ LOG.error("Error saving credentials file", e);
+ }
+ }
+
+}
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/CredentialsInfoSaving.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/CredentialsInfoSaving.java
new file mode 100644
index 00000000000..20b586cc4e2
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/CredentialsInfoSaving.java
@@ -0,0 +1,27 @@
+/*
+ * 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.user;
+
+import java.util.Map;
+
+/**
+ * Helper class to save credentials
+ */
+public class CredentialsInfoSaving {
+ public Map credentialsMap;
+}
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
new file mode 100644
index 00000000000..166840a68ef
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UserCredentials.java
@@ -0,0 +1,47 @@
+/*
+ * 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.user;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * User Credentials POJO
+ */
+public class UserCredentials {
+ private Map userCredentials;
+
+ public UserCredentials() {
+ this.userCredentials = new HashMap<>();
+ }
+
+ public UsernamePassword getUsernamePassword(String entity) {
+ return userCredentials.get(entity);
+ }
+
+ public void putUsernamePassword(String entity, UsernamePassword up) {
+ userCredentials.put(entity, up);
+ }
+
+ @Override
+ public String toString() {
+ return "UserCredentials{" +
+ "userCredentials=" + userCredentials +
+ '}';
+ }
+}
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UsernamePassword.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UsernamePassword.java
new file mode 100644
index 00000000000..00116a990ad
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/user/UsernamePassword.java
@@ -0,0 +1,55 @@
+/*
+* 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.user;
+
+/**
+ * Username and Password POJO
+ */
+public class UsernamePassword {
+ private String username;
+ private String password;
+
+ public UsernamePassword(String username, String password) {
+ this.username = username;
+ this.password = password;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ @Override
+ public String toString() {
+ return "UsernamePassword{" +
+ "username='" + username + '\'' +
+ ", password='" + password + '\'' +
+ '}';
+ }
+}
diff --git a/zeppelin-interpreter/src/test/java/org/apache/zeppelin/user/CredentialsTest.java b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/user/CredentialsTest.java
new file mode 100644
index 00000000000..259516f9766
--- /dev/null
+++ b/zeppelin-interpreter/src/test/java/org/apache/zeppelin/user/CredentialsTest.java
@@ -0,0 +1,40 @@
+/*
+ * 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.user;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class CredentialsTest {
+
+ @Test
+ public void testDefaultProperty() throws IOException {
+ Credentials credentials = new Credentials(false, null);
+ UserCredentials userCredentials = new UserCredentials();
+ UsernamePassword up1 = new UsernamePassword("user2", "password");
+ userCredentials.putUsernamePassword("hive(vertica)", up1);
+ credentials.putUserCredentials("user1", userCredentials);
+ UserCredentials uc2 = credentials.getUserCredentials("user1");
+ UsernamePassword up2 = uc2.getUsernamePassword("hive(vertica)");
+ assertEquals(up1.getUsername(), up2.getUsername());
+ assertEquals(up1.getPassword(), up2.getPassword());
+ }
+}
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
new file mode 100644
index 00000000000..b1a4d17a837
--- /dev/null
+++ b/zeppelin-server/src/main/java/org/apache/zeppelin/rest/CredentialRestApi.java
@@ -0,0 +1,78 @@
+/*
+ * 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.zeppelin.user.Credentials;
+import org.apache.zeppelin.user.UserCredentials;
+import org.apache.zeppelin.user.UsernamePassword;
+import org.apache.zeppelin.server.JsonResponse;
+import org.apache.zeppelin.utils.SecurityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.*;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Credential Rest API
+ *
+ */
+@Path("/credential")
+@Produces("application/json")
+public class CredentialRestApi {
+ Logger logger = LoggerFactory.getLogger(CredentialRestApi.class);
+ private Credentials credentials;
+ private Gson gson = new Gson();
+
+ @Context
+ private HttpServletRequest servReq;
+
+ public CredentialRestApi() {
+
+ }
+
+ public CredentialRestApi(Credentials credentials) {
+ this.credentials = credentials;
+ }
+
+ /**
+ * Update credentials for current user
+ */
+ @PUT
+ public Response putCredentials(String message) throws IOException {
+ Map messageMap = gson.fromJson(message,
+ new TypeToken