From 243e286b8a1350cd4e8393f4f56b0327fe460694 Mon Sep 17 00:00:00 2001 From: Hector Sanjuan Date: Thu, 21 Jul 2011 14:58:10 +0200 Subject: [PATCH] Feature #738: Added client and server support to list, create and delete ACLs. --- src/sunstone/models/OpenNebulaJSON.rb | 1 + src/sunstone/models/OpenNebulaJSON/AclJSON.rb | 51 ++ .../models/OpenNebulaJSON/PoolJSON.rb | 1 + src/sunstone/models/SunstoneServer.rb | 5 +- src/sunstone/public/js/opennebula.js | 90 ++++ src/sunstone/public/js/plugins/acls-tab.js | 474 ++++++++++++++++++ src/sunstone/public/js/sunstone-util.js | 2 +- 7 files changed, 622 insertions(+), 2 deletions(-) create mode 100644 src/sunstone/models/OpenNebulaJSON/AclJSON.rb create mode 100644 src/sunstone/public/js/plugins/acls-tab.js diff --git a/src/sunstone/models/OpenNebulaJSON.rb b/src/sunstone/models/OpenNebulaJSON.rb index 44685c68f9..0eb98bd37c 100644 --- a/src/sunstone/models/OpenNebulaJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON.rb @@ -26,6 +26,7 @@ require 'OpenNebulaJSON/PoolJSON' require 'OpenNebulaJSON/UserJSON' require 'OpenNebulaJSON/VirtualMachineJSON' require 'OpenNebulaJSON/VirtualNetworkJSON' +require 'OpenNebulaJSON/AclJSON' module OpenNebula class Error diff --git a/src/sunstone/models/OpenNebulaJSON/AclJSON.rb b/src/sunstone/models/OpenNebulaJSON/AclJSON.rb new file mode 100644 index 0000000000..0db8a68ac1 --- /dev/null +++ b/src/sunstone/models/OpenNebulaJSON/AclJSON.rb @@ -0,0 +1,51 @@ +# -------------------------------------------------------------------------- # +# Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) # +# # +# 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. # +#--------------------------------------------------------------------------- # + +require 'OpenNebulaJSON/JSONUtils' + +module OpenNebulaJSON + class AclJSON < OpenNebula::Acl + include JSONUtils + + def create(template_json) + acl_string = parse_json(template_json, 'acl') + acl_rule = Acl.parse_rule(acl_string) + if OpenNebula.is_error?(acl_rule) + return acl_rule + end + self.allocate(acl_rule[0],acl_rule[1],acl_rule[2]) + end + + def perform_action(template_json) + action_hash = parse_json(template_json, 'action') + if OpenNebula.is_error?(action_hash) + return action_hash + end + + error_msg = "#{action_hash['perform']} action not " << + " available for this resource" + OpenNebula::Error.new(error_msg) + + # rc = case action_hash['perform'] + # #no actions! + # else + # error_msg = "#{action_hash['perform']} action not " << + # " available for this resource" + # OpenNebula::Error.new(error_msg) + # end + end + end +end diff --git a/src/sunstone/models/OpenNebulaJSON/PoolJSON.rb b/src/sunstone/models/OpenNebulaJSON/PoolJSON.rb index 150956bdf6..a59db50c73 100644 --- a/src/sunstone/models/OpenNebulaJSON/PoolJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/PoolJSON.rb @@ -24,4 +24,5 @@ module OpenNebulaJSON class TemplatePoolJSON < OpenNebula::TemplatePool; include JSONUtils; end class GroupPoolJSON < OpenNebula::GroupPool; include JSONUtils; end class UserPoolJSON < OpenNebula::UserPool; include JSONUtils; end + class AclPoolJSON < OpenNebula::AclPool; include JSONUtils; end end diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index f1cec51f60..d387b77acc 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -65,13 +65,14 @@ class SunstoneServer when "vm" then VirtualMachinePoolJSON.new(@client) when "vnet" then VirtualNetworkPoolJSON.new(@client) when "user" then UserPoolJSON.new(@client) + when "acl" then AclPoolJSON.new(@client) else error = Error.new("Error: #{kind} resource not supported") return [404, error.to_json] end rc = case kind - when "group","host","user" then pool.info + when "group","host","user","acl" then pool.info else gid != "0" ? pool.info_group : pool.info_all end @@ -120,6 +121,7 @@ class SunstoneServer when "vm" then VirtualMachineJSON.new(VirtualMachine.build_xml,@client) when "vnet" then VirtualNetworkJSON.new(VirtualNetwork.build_xml, @client) when "user" then UserJSON.new(User.build_xml, @client) + when "acl" then AclJSON.new(Acl.build_xml, @client) else error = Error.new("Error: #{kind} resource not supported") return [404, error.to_json] @@ -339,6 +341,7 @@ class SunstoneServer when "vm" then VirtualMachineJSON.new_with_id(id, @client) when "vnet" then VirtualNetworkJSON.new_with_id(id, @client) when "user" then UserJSON.new_with_id(id, @client) + when "acl" then AclJSON.new_with_id(id, @client) else error = Error.new("Error: #{kind} resource not supported") return error diff --git a/src/sunstone/public/js/opennebula.js b/src/sunstone/public/js/opennebula.js index 769bffab40..222faa20ca 100644 --- a/src/sunstone/public/js/opennebula.js +++ b/src/sunstone/public/js/opennebula.js @@ -2589,5 +2589,95 @@ var OpenNebula = { "chgrp" : function(params){ OpenNebula.Helper.chgrp(params,OpenNebula.Template.resource,"template"); } + }, + + "Acl" : { + "resource" : "ACL", + "create" : function(params){ + var callback = params.success; + var callback_error = params.error; + var data = params.data; + var resource = OpenNebula.Acl.resource; + + var request = OpenNebula.Helper.request(resource,"create",data); + + $.ajax({ + url: "acl", + type: "POST", + dataType: "json", + data: JSON.stringify(data), + success: function(response) + { + if (callback) + { + callback(request, response); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + }, + "list" : function(params){ + var callback = params.success; + var callback_error = params.error; + var timeout = params.timeout || false; + + var resource = OpenNebula.Acl.resource; + var request = OpenNebula.Helper.request(resource,"list"); + + $.ajax({ + url: "acl", + type: "GET", + dataType: "json", + data: {timeout: timeout}, + success: function(response) + { + if (callback) + { + var acl_pool = OpenNebula.Helper.pool(resource,response); + callback(request, acl_pool); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + }, + "delete" : function(params){ + var callback = params.success; + var callback_error = params.error; + var id = params.data.id; + var resource = OpenNebula.Acl.resource; + + var request = OpenNebula.Helper.request(resource,"delete", id); + + $.ajax({ + url: "acl/" + id, + type: "DELETE", + success: function() + { + if (callback) + { + callback(request); + } + }, + error: function(response) + { + if (callback_error) + { + callback_error(request, OpenNebula.Error(response)); + } + } + }); + } } } diff --git a/src/sunstone/public/js/plugins/acls-tab.js b/src/sunstone/public/js/plugins/acls-tab.js new file mode 100644 index 0000000000..8f2a4ab7b0 --- /dev/null +++ b/src/sunstone/public/js/plugins/acls-tab.js @@ -0,0 +1,474 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2011, OpenNebula Project Leads (OpenNebula.org) */ +/* */ +/* 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. */ +/* -------------------------------------------------------------------------- */ + +/*ACLs tab plugin*/ +var dataTable_acls; + +var acls_tab_content = +'
\ +
\ +
\ +\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +
AllIDApplies toAffected resourcesResource ID / Owned byAllowed operations
\ +
'; + +var create_acl_tmpl = +'
\ +
\ +
\ + \ + \ +
\ + \ + Hosts
\ + Virtual Machines
\ + Virtual Networks
\ + Images
\ + Templates
\ + Users
\ + Groups
\ +
\ + \ + All
\ + Specific ID
\ + Owned by group
\ +
\ + \ + \ +
\ + \ + \ +
\ + \ + Create
\ + Delete
\ + Use
\ + Manage
\ + Get Information
\ + Get Pool of resources
\ + Get Pool of my/group\'s resources
\ + Change owner
\ +
\ + \ + \ +
\ +
\ +
\ +
\ + \ + \ +
\ +
\ +
'; + +var acl_actions = { + "Acl.create" : { + type: "create", + call: OpenNebula.Acl.create, + callback: function(){ + Sunstone.runAction("Acl.list"); + }, + error: onError, + notify: true + }, + + "Acl.create_dialog" : { + type: "custom", + call: popUpCreateAclDialog + }, + + "Acl.list" : { + type: "list", + call: OpenNebula.Acl.list, + callback: updateAclsView, + error: onError + }, + + "Acl.refresh" : { + type: "custom", + call: function () { + waitingNodes(dataTable_acls); + Sunstone.runAction("Acl.list"); + }, + }, + + "Acl.autorefresh" : { + type: "custom", + call: function(){ + OpenNebula.Acl.list({ + timeout: true, + success: updateAclsView, + error: onError + }); + }, + condition: True, + notify: false + }, + + "Acl.delete" : { + type: "multiple", + call: OpenNebula.Acl.delete, + callback: deleteAclElement, + elements: function() { return getSelectedNodes(dataTable_acls); }, + error: onError, + notify: true + }, +} + +var acl_buttons = { + "Acl.refresh" : { + type: "image", + text: "Refresh list", + img: "images/Refresh-icon.png", + condition: True + }, + "Acl.create_dialog" : { + type: "create_dialog", + text: "+ New", + condition: True + }, + "Acl.delete" : { + type: "action", + text: "Delete", + condition: True + } +} + +var acls_tab = { + title: "ACLs", + content: acls_tab_content, + buttons: acl_buttons, + condition: True +} + +Sunstone.addActions(acl_actions); +Sunstone.addMainTab('acls_tab',acls_tab); + +function parseUserAcl(user){ + var user_str=""; + if (user[0] == '*'){ + user_str = "All"; + } else { + if (user[0] == '#'){ + user_str="User "; + user_str+= getUserName(user.substring(1)); + } + else if (user[0] == '@'){ + user_str="Group "; + user_str+= getGroupName(user.substring(1)); + }; + }; + return user_str; +} + +function parseResourceAcl(user){ + var user_str=""; + if (user[0] == '*'){ + user_str = "All"; + } else { + if (user[0] == '#'){ + user_str="ID "; + user_str+= user.substring(1); + } + else if (user[0] == '@'){ + user_str="Group "; + user_str+= getGroupName(user.substring(1)); + }; + }; + return user_str; +} + +//Parses the string, returns a legible array +function parseAclString(string) { + var space_split = string.split(' '); + var user = space_split[0]; + var resources = space_split[1]; + var rights = space_split[2]; + + //User + var user_str=parseUserAcl(user); + + + //Resources + var resources_str=""; + var resources_array = resources.split('/'); + var belonging_to = parseResourceAcl(resources_array[1]); + resources_array = resources_array[0].split('+'); + for (var i=0; i', + acl.ID, + acl_array[0], + acl_array[1], + acl_array[2], + acl_array[3] + ] +} + + +// Callback to delete a single element from the dataTable +function deleteAclElement(req){ + deleteElement(dataTable_acls,'#acl_'+req.request.data); +} + +function updateAclsView(request,list){ + var list_array = []; + + $.each(list,function(){ + list_array.push(aclElementArray(this)); + }); + updateView(list_array,dataTable_acls); +} + +function setupCreateAclDialog(){ + $('div#dialogs').append('
'); + $('#create_acl_dialog').html(create_acl_tmpl); + + //Prepare jquery dialog + $('#create_acl_dialog').dialog({ + autoOpen: false, + modal:true, + width: 600 + }); + + $('#create_acl_dialog #res_subgroup_all').attr("checked","checked"); + $('#create_acl_dialog #res_id').attr("disabled","disabled"); + $('#create_acl_dialog #belonging_to').attr("disabled","disabled"); + + $('#create_acl_dialog button').button(); + + $('.res_subgroup').click(function(){ + var value = $(this).val(); + var context = $(this).parent(); + switch (value) { + case "*": + $('#res_id',context).attr("disabled","disabled"); + $('#belonging_to',context).attr("disabled","disabled"); + break; + case "res_id": + $('#res_id',context).removeAttr("disabled"); + $('#belonging_to').attr("disabled","disabled"); + break; + case "belonging_to": + $('#res_id',context).attr("disabled","disabled"); + $('#belonging_to',context).removeAttr("disabled"); + break; + }; + }); + + $('input,select',$('#create_acl_form')).live("change",function(){ + var context = $('#create_acl_form'); + var user = $('#applies',context).val(); + + if ($('#applies :selected',context).hasClass("user")){ + user='#'+user; + } else if ($('#applies :selected',context).hasClass("group")){ + user = '@'+user; + }; + + var resources = ""; + $('.resource_cb:checked',context).each(function(){ + resources+=$(this).val()+'+'; + }); + if (resources.length) { resources = resources.substring(0,resources.length-1) }; + + var belonging=""; + var mode = $('.res_subgroup:checked').val(); + switch (mode) { + case "*": + belonging="*"; + break; + case "res_id": + belonging="#"+$('#res_id',context).val(); + break; + case "belonging_to": + belonging="@"+$('#belonging_to',context).val(); + break; + } + + + var rights = ""; + $('.right_cb:checked',context).each(function(){ + rights+=$(this).val()+'+'; + }); + if (rights.length) { rights = rights.substring(0,rights.length-1) }; + + var acl_string = user + ' ' + resources + '/' + belonging + ' ' + rights; + $('#acl_preview',context).val(acl_string); + + }); + + $('#create_acl_form').submit(function(){ + var user = $('#applies',this).val(); + if (!user.length) { + notifyError("Please specify to who this ACL applies"); + return false; + }; + + var resources = $('.resource_cb:checked',this).length; + if (!resources) { + notifyError("Please select at least one resource"); + return false; + } + + var mode = $('.res_subgroup:checked',this).val(); + switch (mode) { + case "res_id": + var l=$('#res_id',this).val().length; + if (!l){ + notifyError("Please provide a resource ID for the resource(s) in this rule"); + return false; + } + break; + case "belonging_to": + var l=$('#belonging_to',this).val().length; + if (!l){ + notifyError("Please select a group to which the selected resources belong to"); + return false; + } + break; + } + + var rights = $('.right_cb:checked',this).length; + if (!rights) { + notifyError("Please select at least one operation"); + return false; + } + + var acl_string = $('#acl_preview',this).val(); + + var acl_json = { "acl" : acl_string }; + Sunstone.runAction("Acl.create",acl_json); + $('#create_acl_dialog').dialog('close'); + return false; + }); +} + +function popUpCreateAclDialog(){ + var users = $(''); + $('.empty_value',users).remove(); + $('option',users).addClass("user"); + users.prepend(''); + + var groups = $(''); + $('.empty_value',groups).remove(); + $('option',groups).addClass("group"); + groups.prepend(''); + + + $('#create_acl_dialog #applies').html(''+ + users.html()+groups.html()); + $('#create_acl_dialog #belonging_to').html(groups_select); + + $('#create_acl_dialog').dialog('open'); +} + +// Prepare the autorefresh of the list +function setAclAutorefresh(){ + setInterval(function(){ + var checked = $('input:checked',dataTable_acls.fnGetNodes()); + var filter = $("#datatable_acls_filter input").attr("value"); + if (!checked.length && !filter.length){ + Sunstone.runAction("Acl.autorefresh"); + } + },INTERVAL+someTime()); +} + +$(document).ready(function(){ + //if we are not oneadmin, our tab will not even be in the DOM. + dataTable_acls = $("#datatable_acls").dataTable({ + "bJQueryUI": true, + "bSortClasses": false, + "sPaginationType": "full_numbers", + "bAutoWidth":false, + "aoColumnDefs": [ + { "bSortable": false, "aTargets": ["check"] }, + { "sWidth": "60px", "aTargets": [0] }, + { "sWidth": "35px", "aTargets": [1] } + ] + }); + dataTable_acls.fnClearTable(); + addElement([ + spinner, + '','','','',''],dataTable_acls); + + Sunstone.runAction("Acl.list"); + + setupCreateAclDialog(); + setAclAutorefresh(); + + initCheckAllBoxes(dataTable_acls); + tableCheckboxesListener(dataTable_acls); + //shortenedInfoFields('#datatable_acls'); + +}) diff --git a/src/sunstone/public/js/sunstone-util.js b/src/sunstone/public/js/sunstone-util.js index dadc422efb..b46610e0c9 100644 --- a/src/sunstone/public/js/sunstone-util.js +++ b/src/sunstone/public/js/sunstone-util.js @@ -477,7 +477,7 @@ function makeSelectOptions(dataTable, status_bad, user_col){ var nodes = dataTable.fnGetData(); - var select = ""; + var select = ''; var array; $.each(nodes,function(){ var id = this[id_col];