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
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,35 @@ <h4>Create new interpreter</h4>
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>
Expand Down
76 changes: 74 additions & 2 deletions zeppelin-web/src/app/interpreter/interpreter.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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),
Expand All @@ -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);
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -311,6 +381,7 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
option: {
remote: true,
isExistingProcess: false,
setPermission: false,
perNoteSession: false,
perNoteProcess: false

Expand Down Expand Up @@ -487,6 +558,7 @@ angular.module('zeppelinWebApp').controller('InterpreterCtrl',
var init = function() {
$scope.resetNewInterpreterSetting();
$scope.resetNewRepositorySetting();

getInterpreterSettings();
getAvailableInterpreters();
getRepositories();
Expand Down
8 changes: 8 additions & 0 deletions zeppelin-web/src/app/interpreter/interpreter.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
29 changes: 28 additions & 1 deletion zeppelin-web/src/app/interpreter/interpreter.html
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ <h4>Repositories</h4>
</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>
Expand Down Expand Up @@ -191,7 +192,33 @@ <h5>Option</h5>
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>
Expand Down
28 changes: 28 additions & 0 deletions zeppelin-web/src/components/interpreter/interpreter.directive.js
Original file line number Diff line number Diff line change
@@ -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);
});
}
}
};
});
1 change: 1 addition & 0 deletions zeppelin-web/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@
<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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

package org.apache.zeppelin.interpreter;

import java.util.List;

/**
*
*/
Expand All @@ -28,7 +30,8 @@ public class InterpreterOption {
boolean perNoteProcess;

boolean isExistingProcess;

boolean setPermission;
List<String> users;

public boolean isExistingProcess() {
return isExistingProcess;
Expand All @@ -46,6 +49,17 @@ public void setHost(String host) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,19 @@ public Map<String, Object> info() {
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();
Expand All @@ -285,6 +298,17 @@ protected Object jobRun() throws Throwable {
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) {
Expand Down Expand Up @@ -339,6 +363,34 @@ protected Object jobRun() throws Throwable {
}
}

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());
Expand Down