| /* | |
| * 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. | |
| */ | |
| // fix for jQuery tree to work with this version of jQuery | |
| jQuery.curCSS = jQuery.css; | |
| var userTree = false; | |
| var selectedRole = false; | |
| var selectedParent = false; | |
| var newDialogRole = false; | |
| var roleDetails = false; | |
| var roleDetailsHelp = false; | |
| var roleDetailsTable = false; | |
| var roleDetailsBody = false; | |
| var roleDetailsTemplate = false; | |
| var roleDetailsTemplateP = false; | |
| var roleDetailsTemplateC = false; | |
| function roleObj(node) { | |
| node = node && node.attr ? node.attr('role') : false; | |
| return node ? JSON.parse(node) : false; | |
| } | |
| var treeSettings = { | |
| core : { | |
| data : [], // will be set on load | |
| multiple : false, | |
| themes : { stripes : true }, | |
| check_callback : function (operation, node, node_parent, node_position, more) { | |
| // disable copy to root node | |
| if ('#' === node_parent.id) return false; | |
| if (operation === 'copy_node' && 'root' === node_parent.id) return false; | |
| if (operation === 'move_node' || operation === 'copy_node') { | |
| // don't copy/move things around the same/root level | |
| if (node.parent === node_parent.id) return false; | |
| // don't copy/move if target alreay contains the same member | |
| var target = node_parent.original.role; | |
| if (target && isMember(node.original.role, target)) return false; | |
| } | |
| return true; | |
| } | |
| }, | |
| plugins : [ 'dnd', 'types', 'sort' ], | |
| types : { | |
| root : { valid_children: ['t0', 't1', 't2'], icon : pluginRoot + '/res/book-2.png' }, | |
| t2 : { valid_children: ['t0', 't1', 't2'], icon : pluginRoot + '/res/group.png' }, | |
| t1 : { valid_children: [], icon : pluginRoot + '/res/user.png' }, | |
| t0 : { valid_children: [], icon : pluginRoot + '/res/role.png' } | |
| } | |
| } | |
| function initTree(data) { | |
| // show help message | |
| roleDetailsHelp.removeClass('ui-helper-hidden'); | |
| roleDetailsTable.addClass('ui-helper-hidden'); | |
| var openNodes = []; | |
| // recreate tree, because reload doesn't work | |
| var userTreeRef = $.jstree.reference('#userTree'); | |
| if (userTreeRef) { | |
| userTreeRef.destroy(); | |
| // save state | |
| $.each(userTreeRef.get_node('root').children_d, function(idx, child) { | |
| var node = userTreeRef.get_node(child); | |
| if ( node.state.opened ) openNodes.push(node.text); | |
| }); | |
| } | |
| // prepare data | |
| var sortedGroups = sortGroups(data); | |
| treeSettings.core['data'] = buildTree(sortedGroups); | |
| // build tree | |
| userTree = $('#userTree') | |
| .on('select_node.jstree', function(e, data) { | |
| var role = data.node.original.role; | |
| if (role) { | |
| var parent = data.node.parent; | |
| var parent_name = parent === '#' || parent === 'root' ? false : data.instance.get_node(parent).text; | |
| onSelectNode(role.name, parent_name); | |
| } else { | |
| roleDetailsHelp.removeClass('ui-helper-hidden'); | |
| roleDetailsTable.addClass('ui-helper-hidden'); | |
| } | |
| }) | |
| .on('move_node.jstree', function(e, data) { | |
| var role = data.node.original.role; | |
| var parent = data.parent; | |
| var parent_name = parent === '#' || parent === 'root' ? false : data.instance.get_node(parent).text; | |
| var old_parent = data.old_parent; | |
| var old_parent_name = old_parent === '#' || old_parent === 'root' ? false : data.instance.get_node(old_parent).text; | |
| if (parent_name) { | |
| //console.log('move: adding role', role, 'to group', parent); | |
| $.post(pluginRoot, {'action': 'addMember', 'role' : role.name, 'group' : parent_name}); | |
| } | |
| if (old_parent_name) { | |
| //console.log('move: removed role', role, 'to group', old_parent); | |
| $.post(pluginRoot, {'action': 'removeMember', 'role' : role.name, 'group' : old_parent_name}); | |
| } | |
| $('#reload').click(); | |
| }) | |
| .on('copy_node.jstree', function(e, data) { | |
| var role = data.original.original.role; | |
| var parent_name = data.instance.get_node(data.parent).text; | |
| if (parent_name) { | |
| //console.log('copy: copying role', role, 'to group', parent); | |
| $.post(pluginRoot, {'action': 'addMember', 'role' : role.name, 'group' : parent_name}); | |
| } | |
| $('#reload').click(); | |
| }) | |
| .on('ready.jstree', function(e, data) { // restore state | |
| var _ = data.instance; | |
| if (openNodes.length) $.each(_.get_node('root').children_d, function(idx, child) { | |
| var node = _.get_node(child); | |
| if ($.inArray(node.text, openNodes) > -1) _.open_node(node, false, false); | |
| }); | |
| }).jstree(treeSettings); | |
| } | |
| $(function() { | |
| // read the available digest algorithms | |
| $.ajax({ | |
| type : "POST", | |
| url : pluginRoot, | |
| async : false, | |
| data : {'action': 'getDigestAlgorithms' }, | |
| dataType: 'json', | |
| success : function(data) { | |
| var _select = $('select.propertyType'); | |
| $.each(data, function(id, alg) { | |
| _select.append('<option value="password-{0}">{1} {2}</option>'.msgFormat(alg, i18n.paswd, alg)); | |
| }); | |
| } | |
| }); | |
| roleDetails = $('#roleDetails'); | |
| roleDetailsTable = roleDetails.find('table'); | |
| roleDetailsHelp = roleDetails.find('#roleDetailsHelp'); | |
| roleDetailsBody = roleDetailsTable.find('tbody'); | |
| roleDetailsTemplateP = roleDetailsBody.find('tr:eq(0)'); | |
| roleDetailsTemplateC = roleDetailsBody.find('tr:eq(1)'); | |
| roleDetailsTemplate = roleDetailsBody.find('tr:eq(2)').clone(); | |
| roleDetailsBody.find('tr').not('.header').remove(); | |
| // add new property/credential code | |
| $('tr.header span.ui-icon-plus').click(function() { | |
| $(this).parent().parent().parent().parent().after(newProp()); | |
| }); | |
| // new role dialog | |
| var _buttons = {}; | |
| _buttons[i18n.close] = function() { | |
| $(this).dialog('close'); | |
| } | |
| _buttons[i18n.add] = function() { | |
| var _ = newDialogRole; | |
| var n = _.find('input'); | |
| if (!n.val()) { | |
| n.addClass('ui-state-error'); | |
| return false; | |
| } else n.removeClass('ui-state-error'); | |
| var t = _.find('select').val(); | |
| $.post(pluginRoot, {'action': 'set', 'data' : JSON.stringify({'name': n.val(), 'type': new Number(t)})} , function(data) { | |
| _.dialog('close'); | |
| $('#reload').click(); | |
| }, 'json'); | |
| } | |
| newDialogRole = $("#newDialogRole").dialog({ | |
| autoOpen : false, | |
| modal : true, | |
| open : function() { $(this).find('input').val('').removeClass('ui-state-error') }, | |
| closeText: i18n.abort, | |
| buttons : _buttons | |
| }); | |
| // role info buttons | |
| $('#delRole').click( function() { | |
| if (selectedRole) $.post(pluginRoot, {'action': 'del', 'role' : selectedRole}, function() { | |
| $('#reload').click(); | |
| }); | |
| }); | |
| $('#savRole').click( doSaveRole ); | |
| $('#toggleRequiredRole').click( function() { | |
| if (selectedRole && selectedParent) | |
| $.post(pluginRoot, { action: 'toggleMembership', role: selectedRole, group: selectedParent }, function() { | |
| $('#reload').click() | |
| }); | |
| }); | |
| // top-frame buttons | |
| $('#newRole').click( function() { | |
| newDialogRole.dialog('open'); | |
| return false; | |
| }); | |
| $('#reload').click( function() { | |
| $.post(pluginRoot, {'action': 'list'} , initTree, 'json'); | |
| return false; | |
| }).click(); | |
| }); | |
| function digest(val, alg) { | |
| var _ret = false; | |
| $.ajax({ | |
| type : "POST", | |
| url : pluginRoot, | |
| async : false, | |
| data : { | |
| 'action': 'digest', | |
| 'data' : val, | |
| 'algorithm' : alg | |
| }, | |
| dataType: 'json', | |
| success : function(data) { | |
| _ret = data['encoded']; | |
| } | |
| }); | |
| return _ret; | |
| } | |
| function newProp() { | |
| var tr = roleDetailsTemplate.clone() | |
| .find('li').click( function() { | |
| tr.remove(); | |
| }).end() | |
| .find('select').change( function(evt) { | |
| tr.find('.v').replaceWith('<input class="v" '+ ($(this).val().indexOf('password') == 0 ? 'type="password"' : '') + '/>'); | |
| initStaticWidgets(tr); | |
| }).end() | |
| initStaticWidgets(tr); | |
| return tr; | |
| } | |
| function hashToArray(s) { | |
| var r = []; | |
| while(s.length > 0) { | |
| r.push(parseInt(s.substring(0, 2), 16)); | |
| s = s.substring(2); | |
| } | |
| return r; | |
| } | |
| function strToArray(s) { | |
| var r = []; | |
| var el = s.split(','); | |
| for(var i=0;i<el.length;i++) r.push( parseInt(el[i], 10) ); | |
| return r; | |
| } | |
| function doSaveRole() { | |
| if (!selectedRole) return; | |
| var doProps = true; | |
| var data = { | |
| name : selectedRole, | |
| properties : {}, | |
| credentials : {} | |
| }; | |
| roleDetailsBody.find('tr').each( function() { | |
| var _ = $(this); | |
| if (_.hasClass('header-props')) doProps = true; | |
| else if (_.hasClass('header-cred')) doProps = false; | |
| else { | |
| var k = _.find('.k').val(); | |
| var v = _.find('.v').val(); | |
| var t = _.find('select').val(); | |
| if (t.indexOf('password-') == 0) { | |
| var hash = digest(v, t.substring(9)); | |
| v = hash; | |
| } else if (t == 'byte[]') { | |
| v = strToArray(v); | |
| } | |
| if (doProps) data.properties[k] = v; | |
| else data.credentials[k] = v; | |
| } | |
| }); | |
| $.post(pluginRoot, {'action': 'set', 'data' : JSON.stringify(data)}); | |
| } | |
| function isInMemberArray(role, g) { | |
| if(g) for(var i in g) if (g[i].name == role.name) return true; | |
| return false; | |
| } | |
| function isMember(role, group) { | |
| if (!role) return false; | |
| if (!group) return false; | |
| if (isInMemberArray(role, group.members)) return true; | |
| if (isInMemberArray(role, group.rmembers)) return true; | |
| } | |
| function buildTree(sortedGroups) { | |
| var treeRoot = { | |
| text : i18n.root, | |
| id : 'root', | |
| type : 'root', | |
| state: { opened : true }, | |
| children: [] | |
| }; | |
| var treeNode = function(name, role, parent, req) { | |
| if (!role) return; | |
| if (!parent) parent = treeRoot.children; | |
| var node = { | |
| 'text' : role.name, | |
| 'type' : 't' + role.type, | |
| 'role' : role, | |
| } | |
| if (req) node.li_attr = { 'class' : 'required' }; | |
| parent.push(node); | |
| if (role.type == 2) { | |
| node['children'] = []; | |
| node = node['children']; | |
| if (role.members) $.each(role.members, function(idx, xrole) { | |
| treeNode(xrole.name, xrole, node, 0); | |
| }); | |
| if (role.rmembers) $.each(role.rmembers, function(idx, xrole) { | |
| treeNode(xrole.name, xrole, node, 1); | |
| }); | |
| } | |
| } | |
| $.each(sortedGroups, treeNode); | |
| return treeRoot; | |
| } | |
| function sortGroups(data) { | |
| var rootGroups = {}; // only root groups - without parents | |
| var unassigned = {}; // non-groups, not assigned to any group | |
| var processed = {}; // all processed entries | |
| var u = 0; | |
| var g = 0; | |
| var r = 0; | |
| var _st = function(map, n, role) { | |
| if (typeof map[n] == 'undefined') { // not added - add | |
| map[n] = role; | |
| } else if (map[n] != false) { // already added | |
| map[n] = false; // mark for removal | |
| } | |
| } | |
| var groupF = function(i1, role) { | |
| var n = role.name; | |
| var t = role.type; | |
| if (t == 2) { // group | |
| // don't process twice | |
| if (processed[n]) { | |
| rootGroups[n] = false; | |
| return true; | |
| } | |
| processed[n]=role; | |
| _st(rootGroups, n, role); | |
| if (role.members) $.each(role.members, groupF); | |
| if (role.rmembers) $.each(role.rmembers, groupF); | |
| g++; | |
| } else { // role or user | |
| if (t == 1) u++; else r++; | |
| _st(unassigned, n, role); | |
| } | |
| } | |
| $.each(data, groupF); | |
| $('.statline').text( i18n.status.msgFormat(g,u,r) ); | |
| return $.extend(rootGroups, unassigned); | |
| } | |
| function onSelectNode(role, parent) { | |
| if (parent) { | |
| $('#toggleRequiredRole').removeClass('ui-state-disabled'); | |
| } else { | |
| $('#toggleRequiredRole').addClass('ui-state-disabled') | |
| } | |
| $.post(pluginRoot, {'action': 'get', 'role' : role} , function(data) { | |
| selectedRole = role; | |
| selectedParent = parent; | |
| roleDetailsHelp.addClass('ui-helper-hidden'); | |
| roleDetailsTable.removeClass('ui-helper-hidden'); | |
| roleDetailsBody.find('tr').not('.header').remove(); | |
| var target = false; | |
| var _append = function(k,v) { | |
| var t = $.isArray(v) ? 'byte[]' : 'string'; | |
| target.after(newProp() | |
| .data('k', k) | |
| .find('input.k').val(k).end() | |
| .find('input.v').val(v).end() | |
| .find('select').val(t).end() | |
| ); | |
| } | |
| var x = data.properties; | |
| if (x) { | |
| target = roleDetailsTemplateP; | |
| $.each(x, _append); | |
| } | |
| x = data.credentials; | |
| if (x) { | |
| target = roleDetailsTemplateC; | |
| $.each(x, _append); | |
| } | |
| // show/user credentials view if user/or not (respectively) | |
| x = roleDetailsBody.find('.header-cred'); | |
| if (data.type != 1) x.addClass('ui-helper-hidden'); | |
| else x.removeClass('ui-helper-hidden'); | |
| }, 'json') | |
| return false; | |
| } | |