diff --git a/docs/_includes/themes/zeppelin/_navigation.html b/docs/_includes/themes/zeppelin/_navigation.html index c0f32b6a282..9feda62f31c 100644 --- a/docs/_includes/themes/zeppelin/_navigation.html +++ b/docs/_includes/themes/zeppelin/_navigation.html @@ -100,7 +100,7 @@
  • Authentication for NGINX
  • Shiro Authentication
  • Notebook Authorization
  • -
  • Interpreter & Data Resource Authorization
  • +
  • Data Source Authorization
  • Contibute
  • Writing Zeppelin Interpreter
  • diff --git a/docs/assets/themes/zeppelin/img/docs-img/add_credential.png b/docs/assets/themes/zeppelin/img/docs-img/add_credential.png new file mode 100644 index 00000000000..dcf446074d0 Binary files /dev/null and b/docs/assets/themes/zeppelin/img/docs-img/add_credential.png differ diff --git a/docs/assets/themes/zeppelin/img/docs-img/credential_tab.png b/docs/assets/themes/zeppelin/img/docs-img/credential_tab.png new file mode 100644 index 00000000000..66a1fbed56e Binary files /dev/null and b/docs/assets/themes/zeppelin/img/docs-img/credential_tab.png differ diff --git a/docs/index.md b/docs/index.md index e120e7ae1ee..141e7f6aeef 100644 --- a/docs/index.md +++ b/docs/index.md @@ -167,7 +167,7 @@ Join to our [Mailing list](https://zeppelin.apache.org/community.html) and repor * [Authentication for NGINX](./security/authentication.html) * [Shiro Authentication](./security/shiroauthentication.html) * [Notebook Authorization](./security/notebook_authorization.html) - * [Interpreter & Data Resource Authorization](./security/interpreter_authorization.html) + * [Data Source Authorization](./security/datasource_authorization.html) * Contribute * [Writing Zeppelin Interpreter](./development/writingzeppelininterpreter.html) * [Writing Zeppelin Application (Experimental)](./development/writingzeppelinapplication.html) diff --git a/docs/security/datasource_authorization.md b/docs/security/datasource_authorization.md new file mode 100644 index 00000000000..3f86b8ab00d --- /dev/null +++ b/docs/security/datasource_authorization.md @@ -0,0 +1,59 @@ +--- +layout: page +title: "Data Source Authorization" +description: "Data Source Authorization" +group: security +--- + +# Data Source Authorization in Apache Zeppelin + +
    + +## Overview + +Data source authorization involves authenticating to the data source like a Mysql database and letting it determine user permissions. +Apache Zeppelin allows users to use their own credentials to authenticate with **Data Sources**. + +For example, let's assume you have an account in the Vertica databases with credentials. +You might want to use this account to create a JDBC connection instead of a shared account with all users who are defined in `conf/shiro.ini`. +In this case, you can add your credential information to Apache Zeppelin and use them with below simple steps. + +## How to save the credential information? +You can add new credentials in the dropdown menu for your data source which can be passed to interpreters. + + + +**Entity** can be the key that distinguishes each credential sets. Type **Username & Password** for your own credentials. ex) user & password of Mysql + + + +The credentials saved as per users defined in `conf/shiro.ini`. +If you didn't activate [shiro authentication in Apache Zeppelin](./shiroauthentication.html), your credential information will be saved as `anonymous`. +All credential information also can be found in `conf/credentials.json`. + +#### JDBC interpreter +You need to maintain per-user connection pools. +The interpret method takes the user string as a parameter and executes the jdbc call using a connection in the user's connection pool. + +#### Presto +You don't need a password if the Presto DB server runs backend code using HDFS authorization for the user. + +#### Vertica and Mysql +You have to store the password information for users. + +## Please note +As a first step of data source authentication feature, [ZEPPELIN-828](https://issues.apache.org/jira/browse/ZEPPELIN-828) was proposed and implemented in Pull Request [#860](https://github.com/apache/zeppelin/pull/860). +Currently, only customized 3rd party interpreters can use this feature. We are planning to apply this mechanism to [the community interpreters](../manual/interpreterinstallation.md#available-community-managed-interpreters) in the near future. +Please keep track [ZEPPELIN-1070](https://issues.apache.org/jira/browse/ZEPPELIN-1070). diff --git a/docs/security/interpreter_authorization.md b/docs/security/interpreter_authorization.md deleted file mode 100644 index 6e59e0718a9..00000000000 --- a/docs/security/interpreter_authorization.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: page -title: "Notebook Authorization" -description: "Notebook Authorization" -group: security ---- - -# 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 JDBC 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. - -The Credentials tab in the navbar allows users to save credentials for data sources which are passed to interpreters. diff --git a/zeppelin-distribution/src/bin_license/LICENSE b/zeppelin-distribution/src/bin_license/LICENSE index 292b8d38b9b..d7cc1ccd60d 100644 --- a/zeppelin-distribution/src/bin_license/LICENSE +++ b/zeppelin-distribution/src/bin_license/LICENSE @@ -124,7 +124,7 @@ The text of each license is also included at licenses/LICENSE-[project]-[version (The MIT License) angular-elastic v2.4.2 (https://github.com/monospaced/angular-elastic) - https://github.com/monospaced/angular-elastic/blob/v2.4.2/LICENCE.txt (The MIT License) angular-elastic-input v2.2.0 (https://github.com/jacek-pulit/angular-elastic-input) - https://github.com/jacek-pulit/angular-elastic-input/blob/v2.2.0/LICENSE (The MIT License) ng-focus-if v1.0.2 (https://github.com/hiebj/ng-focus-if) - https://github.com/hiebj/ng-focus-if/blob/v1.0.2/LICENSE - (The MIT License) angular-xeditable v0.1.8 (http://vitalets.github.io/angular-xeditable/) - https://github.com/vitalets/angular-xeditable/tree/0.1.8 + (The MIT License) angular-xeditable v0.1.12 (http://vitalets.github.io/angular-xeditable/) - https://github.com/vitalets/angular-xeditable/tree/0.1.12 (The MIT License) lodash v3.9.3 (https://lodash.com/) - https://github.com/lodash/lodash/blob/3.9.3/LICENSE.txt (The MIT License) angular-filter v0.5.4 (https://github.com/a8m/angular-filter) - https://github.com/a8m/angular-filter/blob/v0.5.4/license.md (The MIT License) ngToast v2.0.0 (http://tamerayd.in/ngToast/) - http://tameraydin.mit-license.org/ diff --git a/zeppelin-web/bower.json b/zeppelin-web/bower.json index a414653cbbf..94d228a0eaa 100644 --- a/zeppelin-web/bower.json +++ b/zeppelin-web/bower.json @@ -23,7 +23,7 @@ "ng-sortable": "~1.3.3", "angular-elastic": "~2.4.2", "angular-elastic-input": "~2.2.0", - "angular-xeditable": "0.1.8", + "angular-xeditable": "0.1.12", "highlightjs": "^9.2.0", "lodash": "~3.9.3", "angular-filter": "~0.5.4", diff --git a/zeppelin-web/src/app/credential/credential.controller.js b/zeppelin-web/src/app/credential/credential.controller.js index df231635b40..621e499ce7f 100644 --- a/zeppelin-web/src/app/credential/credential.controller.js +++ b/zeppelin-web/src/app/credential/credential.controller.js @@ -15,42 +15,134 @@ 'use strict'; angular.module('zeppelinWebApp').controller('CredentialCtrl', function($scope, $route, $routeParams, $location, - $rootScope, $http, baseUrlSrv) { + $rootScope, $http, baseUrlSrv, ngToast) { $scope._ = _; - $scope.credentialEntity = ''; - $scope.credentialUsername = ''; - $scope.credentialPassword = ''; + $scope.credentialInfo = []; + $scope.showAddNewCredentialInfo = false; - $scope.updateCredentials = function() { - if (_.isEmpty($scope.credentialEntity.trim()) || - _.isEmpty($scope.credentialUsername.trim())) { - BootstrapDialog.alert({ - closable: true, - message: 'Username \\ Entity can not be empty.' + var getCredentialInfo = function() { + $http.get(baseUrlSrv.getRestApiBase() + '/credential'). + success(function(data, status, headers, config) { + $scope.credentialInfo = _.map(data.body.userCredentials, function(value, prop) { + return {entity: prop, password: value.password, username: value.username}; + }); + console.log('Success %o %o', status, $scope.credentialInfo); + }). + error(function(data, status, headers, config) { + console.log('Error %o %o', status, data.message); + }); + }; + + $scope.addNewCredentialInfo = function() { + if ($scope.entity && _.isEmpty($scope.entity.trim()) && + $scope.username && _.isEmpty($scope.username.trim())) { + ngToast.danger({ + content: 'Username \\ Entity can not be empty.', + verticalPosition: 'bottom', + timeout: '3000' }); return; } - $http.put(baseUrlSrv.getRestApiBase() + '/credential', - {'entity': $scope.credentialEntity, - 'username': $scope.credentialUsername, - 'password': $scope.credentialPassword - }). + var newCredential = { + 'entity': $scope.entity, + 'username': $scope.username, + 'password': $scope.password + }; + + $http.put(baseUrlSrv.getRestApiBase() + '/credential', newCredential). success(function(data, status, headers, config) { - BootstrapDialog.alert({ - closable: true, - message: 'Successfully saved credentials.' + ngToast.success({ + content: 'Successfully saved credentials.', + verticalPosition: 'bottom', + timeout: '3000' }); - $scope.credentialEntity = ''; - $scope.credentialUsername = ''; - $scope.credentialPassword = ''; + $scope.credentialInfo.push(newCredential); + resetCredentialInfo(); + $scope.showAddNewCredentialInfo = false; console.log('Success %o %o', status, data.message); }). error(function(data, status, headers, config) { - alert('Error saving credentials'); + ngToast.danger({ + content: 'Error saving credentials', + verticalPosition: 'bottom', + timeout: '3000' + }); console.log('Error %o %o', status, data.message); }); }; + $scope.cancelCredentialInfo = function() { + $scope.showAddNewCredentialInfo = false; + resetCredentialInfo(); + }; + + var resetCredentialInfo = function() { + $scope.entity = ''; + $scope.username = ''; + $scope.password = ''; + }; + + $scope.copyOriginCredentialsInfo = function() { + ngToast.info({ + content: 'Since entity is a unique key, you can edit only username & password', + verticalPosition: 'bottom', + timeout: '3000' + }); + }; + + $scope.updateCredentialInfo = function(form, data, entity) { + var request = { + entity: entity, + username: data.username, + password: data.password + }; + + $http.put(baseUrlSrv.getRestApiBase() + '/credential/', request). + success(function(data, status, headers, config) { + var index = _.findIndex($scope.credentialInfo, {'entity': entity}); + $scope.credentialInfo[index] = request; + return true; + }). + error(function(data, status, headers, config) { + console.log('Error %o %o', status, data.message); + ngToast.danger({ + content: 'We couldn\'t save the credential', + verticalPosition: 'bottom', + timeout: '3000' + }); + form.$show(); + }); + return false; + }; + + $scope.removeCredentialInfo = function(entity) { + BootstrapDialog.confirm({ + closable: false, + closeByBackdrop: false, + closeByKeyboard: false, + title: '', + message: 'Do you want to delete this credential information?', + callback: function(result) { + if (result) { + $http.delete(baseUrlSrv.getRestApiBase() + '/credential/' + entity). + success(function(data, status, headers, config) { + var index = _.findIndex($scope.credentialInfo, {'entity': entity}); + $scope.credentialInfo.splice(index, 1); + console.log('Success %o %o', status, data.message); + }). + error(function(data, status, headers, config) { + console.log('Error %o %o', status, data.message); + }); + } + } + }); + }; + + var init = function() { + getCredentialInfo(); + }; + + init(); }); diff --git a/zeppelin-web/src/app/credential/credential.html b/zeppelin-web/src/app/credential/credential.html index 46d83d90757..d4131b19be6 100644 --- a/zeppelin-web/src/app/credential/credential.html +++ b/zeppelin-web/src/app/credential/credential.html @@ -18,53 +18,134 @@

    Credentials

    +
    + + + + +
    - Add credentials for entities one at a time.
    + Manage your credentials. You can add new credential information. +
    +
    + + + +
    +
    +
    +
    +

    Add new credential

    +
    +
    +
    + + + + + + + + + + + + + +
    EntityUsernamePassword
    + + + + + +
    + + Save + + + Cancel + +
    +
    +
    -
    -
    -
    - - - - - - - - - - - - - - -
    EntityUsernamePasswordaction
    - - - - - -
    -
    - - -
    -
    +
    +
    + Currently there is no credential information +
    +
    + + + + + + + + + + + + + + + +
    EntityUsernamePassword
    + + {{credential.entity}} + + + + {{credential.username}} + + + + ********** + + + + + + + + + +
    + + +
    +
    +