zeppelin-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From b..@apache.org
Subject zeppelin git commit: [Zeppelin-945] Interpreter authorization
Date Tue, 16 Aug 2016 07:02:04 GMT
Repository: zeppelin
Updated Branches:
  refs/heads/master 6deb79218 -> 43dc32bd3


[Zeppelin-945] Interpreter authorization

### What is this PR for?
This PR is derived from https://github.com/apache/zeppelin/pull/1223

### What type of PR is it?
Improvement

### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-945

### How should this be tested?
You can change user permission of interpreter at interpreter setting page.

### Screenshots (if appropriate)
![result](https://cloud.githubusercontent.com/assets/3348133/17457867/904e2754-5c3e-11e6-948c-b2969c4b89c7.gif)

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

Author: astroshim <hsshim@nflabs.com>

Closes #1257 from astroshim/ZEPPELIN-945_1 and squashes the following commits:

83097ab [astroshim] fix naming.
2c48ded [astroshim] code refactor.
7ed8ad6 [astroshim] fix js code style
3e25159 [astroshim] move directive to componets
f07542a [astroshim] remove comment
febce0c [astroshim] fix js
e42cb9e [astroshim] fix initialize user value
e1e7a35 [astroshim] add interpreter create.
e72c097 [astroshim] Merge branch 'master' into ZEPPELIN-945_1
e1679b3 [astroshim] wip
e56e5b1 [astroshim] wip
635d523 [astroshim] Merge branch 'master' of https://github.com/apache/zeppelin into ZEPPELIN-945_1
1ae1c6a [astroshim] fix for @AhyoungRyu pointed.
b34fdf7 [astroshim] fix for checking intpname
29d8f43 [astroshim] fix js
407d260 [astroshim] fit UX


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/43dc32bd
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/43dc32bd
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/43dc32bd

Branch: refs/heads/master
Commit: 43dc32bd396df49d682bb768e972ef6c11f950da
Parents: 6deb792
Author: astroshim <hsshim@nflabs.com>
Authored: Tue Aug 16 14:39:51 2016 +0900
Committer: Alexander Bezzubov <bzz@apache.org>
Committed: Tue Aug 16 16:01:55 2016 +0900

----------------------------------------------------------------------
 .../interpreter-create/interpreter-create.html  | 29 ++++++++
 .../app/interpreter/interpreter.controller.js   | 76 +++++++++++++++++++-
 .../src/app/interpreter/interpreter.css         |  8 +++
 .../src/app/interpreter/interpreter.html        | 29 +++++++-
 .../interpreter/interpreter.directive.js        | 28 ++++++++
 zeppelin-web/src/index.html                     |  1 +
 .../zeppelin/interpreter/InterpreterOption.java | 16 ++++-
 .../org/apache/zeppelin/notebook/Paragraph.java | 52 ++++++++++++++
 8 files changed, 235 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html b/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
index ea5d98f..1550126 100644
--- a/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
+++ b/zeppelin-web/src/app/interpreter/interpreter-create/interpreter-create.html
@@ -88,6 +88,35 @@ limitations under the License.
             pu-elastic-input-minwidth="180px" ng-model="newInterpreterSetting.option.port"
/>
         </div>
 
+        <div class="col-md-12">
+          <div class="checkbox">
+          <span class="input-group" style="line-height:30px;">
+            <label><input type="checkbox" style="width:18px !important" id="idShowPermission"
ng-click="togglePermissions('newInterpreter')" ng-model="newInterpreterSetting.option.setPermission"/>
+            Set permission </label>
+          </span>
+          </div>
+        </div>
+
+        <div class="col-md-12">
+          <!-- permissions -->
+          <div ng-show="newInterpreterSetting.option.setPermission" class="permissionsForm">
+            <div>
+              <p>
+                Enter comma separated users in the fields. <br />
+                Empty field (*) implies anyone can run this interpreter.
+              </p>
+              <div>
+
+                <span class="owners">Owners </span>
+                <select id="newInterpreterUsers"  class="form-control" multiple="multiple">
+                  <option ng-repeat="user in newInterpreterSetting.option.users" selected="selected">{{user}}</option>
+                </select>
+              </div>
+            </div>
+          </div>
+        </div>
+
+
         <b>Properties</b>
         <table class="table table-striped properties">
           <tr>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/app/interpreter/interpreter.controller.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter.controller.js b/zeppelin-web/src/app/interpreter/interpreter.controller.js
index a04b376..4be35ef 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.controller.js
+++ b/zeppelin-web/src/app/interpreter/interpreter.controller.js
@@ -22,8 +22,69 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
     $scope.showRepositoryInfo = false;
     $scope._ = _;
 
+    $scope.openPermissions = function() {
+      $scope.showInterpreterAuth = true;
+    };
+
+    $scope.closePermissions = function() {
+      $scope.showInterpreterAuth = false;
+    };
+
+    var getSelectJson = function() {
+      var selectJson = {
+        tags: false,
+        multiple: true,
+        tokenSeparators: [',', ' '],
+        minimumInputLength: 2,
+        ajax: {
+          url: function(params) {
+            if (!params.term) {
+              return false;
+            }
+            return baseUrlSrv.getRestApiBase() + '/security/userlist/' + params.term;
+          },
+          delay: 250,
+          processResults: function(data, params) {
+            var users = [];
+            if (data.body.users.length !== 0) {
+              for (var i = 0; i < data.body.users.length; i++) {
+                users.push({
+                  'id': data.body.users[i],
+                  'text': data.body.users[i]
+                });
+              }
+            }
+            return {
+              results: users,
+              pagination: {
+                more: false
+              }
+            };
+          },
+          cache: false
+        }
+      };
+      return selectJson;
+    };
+
+    $scope.togglePermissions = function(intpName) {
+      angular.element('#' + intpName + 'Users').select2(getSelectJson());
+      if ($scope.showInterpreterAuth) {
+        $scope.closePermissions();
+      } else {
+        $scope.openPermissions();
+      }
+    };
+
+    $scope.$on('ngRenderFinished', function(event, data) {
+      for (var setting = 0; setting < $scope.interpreterSettings.length; setting++) {
+        angular.element('#' + $scope.interpreterSettings[setting].name + 'Users').select2(getSelectJson());
+      }
+    });
+
     var getInterpreterSettings = function() {
-      $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting').success(function(data,
status, headers, config) {
+      $http.get(baseUrlSrv.getRestApiBase() + '/interpreter/setting')
+      .success(function(data, status, headers, config) {
         $scope.interpreterSettings = data.body;
         checkDownloadingDependencies();
       }).error(function(data, status, headers, config) {
@@ -114,7 +175,6 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
         var setting = $scope.interpreterSettings[index];
         option = setting.option;
       }
-
       if (option.perNoteSession) {
         return 'scoped';
       } else if (option.perNoteProcess) {
@@ -148,10 +208,15 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
             if (setting.option.isExistingProcess === undefined) {
               setting.option.isExistingProcess = false;
             }
+            if (setting.option.setPermission === undefined) {
+              setting.option.setPermission = false;
+            }
             if (setting.option.remote === undefined) {
               // remote always true for now
               setting.option.remote = true;
             }
+            setting.option.users = angular.element('#' + setting.name + 'Users').val();
+
             var request = {
               option: angular.copy(setting.option),
               properties: angular.copy(setting.properties),
@@ -168,6 +233,7 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
                 removeTMPSettings(index);
                 thisConfirm.close();
                 checkDownloadingDependencies();
+                $route.reload();
               })
               .error(function(data, status, headers, config) {
                 console.log('Error %o %o', status, data.message);
@@ -275,6 +341,10 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
       if (newSetting.depArtifact !== '' || newSetting.depArtifact) {
         $scope.addNewInterpreterDependency();
       }
+      if (newSetting.option.setPermission === undefined) {
+        newSetting.option.setPermission = false;
+      }
+      newSetting.option.users = angular.element('#newInterpreterUsers').val();
 
       var request = angular.copy($scope.newInterpreterSetting);
 
@@ -311,6 +381,7 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
         option: {
           remote: true,
           isExistingProcess: false,
+          setPermission: false,
           perNoteSession: false,
           perNoteProcess: false
 
@@ -487,6 +558,7 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
     var init = function() {
       $scope.resetNewInterpreterSetting();
       $scope.resetNewRepositorySetting();
+
       getInterpreterSettings();
       getAvailableInterpreters();
       getRepositories();

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/app/interpreter/interpreter.css
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter.css b/zeppelin-web/src/app/interpreter/interpreter.css
index 9843636..ee4f81d 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.css
+++ b/zeppelin-web/src/app/interpreter/interpreter.css
@@ -74,6 +74,14 @@
   overflow-y: auto;
 }
 
+.permissionsForm {
+  list-style-type: none;
+  background: #EFEFEF;
+  padding: 10px 10px 10px 10px;
+  box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15);
+  border: 1px solid #E5E5E5;
+}
+
 .interpreterSettingAdd {
   margin : 5px 5px 5px 5px;
   padding : 10px 10px 10px 10px;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/app/interpreter/interpreter.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/app/interpreter/interpreter.html b/zeppelin-web/src/app/interpreter/interpreter.html
index fd88204..7ac1872 100644
--- a/zeppelin-web/src/app/interpreter/interpreter.html
+++ b/zeppelin-web/src/app/interpreter/interpreter.html
@@ -84,9 +84,10 @@ limitations under the License.
 </div>
 
 <div class="box width-full"
-     ng-repeat="setting in interpreterSettings | orderBy: 'name' | filter: searchInterpreter">
+     ng-repeat="setting in interpreterSettings | orderBy: 'name' | filter: searchInterpreter"
interpreter-directive>
   <div id="{{setting.name | lowercase}}">
     <div class="row interpreter">
+
       <div class="col-md-12">
         <h3 class="interpreter-title">{{setting.name}}
           <small>
@@ -191,7 +192,33 @@ limitations under the License.
             pu-elastic-input-minwidth="180px" ng-model="setting.option.port"  ng-disabled="!valueform.$visible"
/>
       </div>
 
+      <div class="col-md-12">
+        <div class="checkbox">
+          <span class="input-group" style="line-height:30px;">
+            <label><input type="checkbox" style="width:18px !important" id="idShowPermission"
ng-click="togglePermissions(setting.name)" ng-model="setting.option.setPermission" ng-disabled="!valueform.$visible"/>
+            Set permission </label>
+          </span>
+        </div>
+      </div>
+
+      <div class="col-md-12">
+        <!-- permissions -->
+        <div ng-show="setting.option.setPermission" class="permissionsForm">
+          <div>
+            <p>
+              Enter comma separated users in the fields. <br />
+              Empty field (*) implies anyone can run this interpreter.
+            </p>
+            <div>
 
+            <span class="owners">Owners </span>
+            <select id="{{setting.name}}Users" class="form-control" multiple="multiple"
ng-disabled="!valueform.$visible">
+              <option ng-repeat="user in setting.option.users" selected="selected">{{user}}</option>
+            </select>
+            </div>
+          </div>
+        </div>
+      </div>
 
       <div ng-show="_.isEmpty(setting.properties) && _.isEmpty(setting.dependencies)
|| valueform.$hidden" class="col-md-12 gray40-message">
         <em>Currently there are no properties and dependencies set for this interpreter</em>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/components/interpreter/interpreter.directive.js
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/components/interpreter/interpreter.directive.js b/zeppelin-web/src/components/interpreter/interpreter.directive.js
new file mode 100644
index 0000000..1cf1ab2
--- /dev/null
+++ b/zeppelin-web/src/components/interpreter/interpreter.directive.js
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+'use strict';
+
+angular.module('zeppelinWebApp').directive('interpreterDirective', function($timeout) {
+  return {
+    restrict: 'A',
+    link: function(scope, element, attr) {
+      if (scope.$last === true) {
+        $timeout(function() {
+          var id = 'ngRenderFinished';
+          scope.$emit(id);
+        });
+      }
+    }
+  };
+});

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-web/src/index.html
----------------------------------------------------------------------
diff --git a/zeppelin-web/src/index.html b/zeppelin-web/src/index.html
index b23db7a..0697dca 100644
--- a/zeppelin-web/src/index.html
+++ b/zeppelin-web/src/index.html
@@ -164,6 +164,7 @@ limitations under the License.
     <script src="components/arrayOrderingSrv/arrayOrdering.service.js"></script>
     <script src="components/navbar/navbar.controller.js"></script>
     <script src="components/ngescape/ngescape.directive.js"></script>
+    <script src="components/interpreter/interpreter.directive.js"></script>
     <script src="components/expandCollapse/expandCollapse.directive.js"></script>
     <script src="components/noteName-create/notename.controller.js"></script>
     <script src="components/noteName-import/notenameImport.controller.js"></script>

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterOption.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterOption.java
b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterOption.java
index 7aac781..2bcc4c6 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterOption.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/interpreter/InterpreterOption.java
@@ -17,6 +17,8 @@
 
 package org.apache.zeppelin.interpreter;
 
+import java.util.List;
+
 /**
  *
  */
@@ -28,7 +30,8 @@ public class InterpreterOption {
   boolean perNoteProcess;
   
   boolean isExistingProcess;
-
+  boolean setPermission;
+  List<String> users;
 
   public boolean isExistingProcess() {
     return isExistingProcess;
@@ -46,6 +49,17 @@ public class InterpreterOption {
     this.host = host;
   }
 
+  public boolean permissionIsSet() {
+    return setPermission;
+  }
+
+  public void setUserPermission(boolean setPermission) {
+    this.setPermission = setPermission;
+  }
+
+  public List<String> getUsers() {
+    return users;
+  }
 
   public InterpreterOption() {
     remote = false;

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/43dc32bd/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
----------------------------------------------------------------------
diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
index 308cbb0..079216c 100644
--- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
+++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Paragraph.java
@@ -275,6 +275,19 @@ public class Paragraph extends Job implements Serializable, Cloneable
{
     return null;
   }
 
+  private boolean hasPermission(String user, List<String> intpUsers) {
+    if (1 > intpUsers.size()) {
+      return true;
+    }
+
+    for (String u: intpUsers) {
+      if (user.trim().equals(u.trim())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   @Override
   protected Object jobRun() throws Throwable {
     String replName = getRequiredReplName();
@@ -285,6 +298,17 @@ public class Paragraph extends Job implements Serializable, Cloneable
{
       throw new RuntimeException("Can not find interpreter for " + getRequiredReplName());
     }
 
+    if (this.noteHasUser() && this.noteHasInterpreters()) {
+      InterpreterSetting intp = getInterpreterSettingById(repl.getInterpreterGroup().getId());
+      if (intp != null &&
+        interpreterHasUser(intp) &&
+        isUserAuthorizedToAccessInterpreter(intp.getOption()) == false) {
+        logger.error("{} has no permission for {} ", authenticationInfo.getUser(), repl);
+        return new InterpreterResult(Code.ERROR, authenticationInfo.getUser() +
+          " has no permission for " + getRequiredReplName());
+      }
+    }
+
     String script = getScriptBody();
     // inject form
     if (repl.getFormType() == FormType.NATIVE) {
@@ -339,6 +363,34 @@ public class Paragraph extends Job implements Serializable, Cloneable
{
     }
   }
 
+  private boolean noteHasUser() {
+    return this.user != null;
+  }
+
+  private boolean noteHasInterpreters() {
+    return !factory.getInterpreterSettings(note.getId()).isEmpty();
+  }
+
+  private boolean interpreterHasUser(InterpreterSetting intp) {
+    return intp.getOption().permissionIsSet() && intp.getOption().getUsers() != null;
+  }
+
+  private boolean isUserAuthorizedToAccessInterpreter(InterpreterOption intpOpt){
+    return intpOpt.permissionIsSet() &&
+      hasPermission(authenticationInfo.getUser(), intpOpt.getUsers());
+  }
+
+  private InterpreterSetting getInterpreterSettingById(String id) {
+    InterpreterSetting setting = null;
+    for (InterpreterSetting i: factory.getInterpreterSettings(note.getId())) {
+      if (id.startsWith(i.getId())) {
+        setting = i;
+        break;
+      }
+    }
+    return setting;
+  }
+
   @Override
   protected boolean jobAbort() {
     Interpreter repl = getRepl(getRequiredReplName());


Mime
View raw message