From ec48ba7081387a9676716197805fc037561a03eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn?= Date: Thu, 7 Jul 2011 19:22:30 +0200 Subject: [PATCH] Java OCA: New ACL classes, and tests. TODO: Create a new Exception for rule parsing errors --- .../src/org/opennebula/client/acl/Acl.java | 355 ++++++++++++++++++ .../org/opennebula/client/acl/AclPool.java | 98 +++++ src/oca/java/test/AclTest.java | 213 +++++++++++ src/oca/java/test/all_tests.sh | 19 + src/oca/java/test/test.sh | 20 + 5 files changed, 705 insertions(+) create mode 100644 src/oca/java/src/org/opennebula/client/acl/Acl.java create mode 100644 src/oca/java/src/org/opennebula/client/acl/AclPool.java create mode 100644 src/oca/java/test/AclTest.java diff --git a/src/oca/java/src/org/opennebula/client/acl/Acl.java b/src/oca/java/src/org/opennebula/client/acl/Acl.java new file mode 100644 index 0000000000..62d2f1d560 --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/acl/Acl.java @@ -0,0 +1,355 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ +package org.opennebula.client.acl; + + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.opennebula.client.Client; +import org.opennebula.client.OneResponse; +import org.opennebula.client.PoolElement; +import org.w3c.dom.Node; + +/** + * This class represents an OpenNebula ACL rule. + * It also offers static XML-RPC call wrappers. + *
+ * There is not a public constructor, because the information for an individual + * ACL rule cannot be retrieved from OpenNebula. + *
+ * Instead, Acl objects should be obtained using AclPool.getById, after the + * info method has been called. + * + * @see AclPool#getById + */ +public class Acl extends PoolElement{ + + private static final String METHOD_PREFIX = "acl."; + private static final String ADDRULE = METHOD_PREFIX + "addrule"; + private static final String DELRULE = METHOD_PREFIX + "delrule"; + + private static final Map USERS; + private static final Map RESOURCES; + private static final Map RIGHTS; + + static { + HashMap tmpUsers = new HashMap(); + tmpUsers.put("#", 0x0000000100000000L); + tmpUsers.put("@", 0x0000000200000000L); + tmpUsers.put("*", 0x0000000400000000L); + + USERS = Collections.unmodifiableMap(tmpUsers); + + HashMap tmpResources = new HashMap(); + + tmpResources.put("VM" , 0x0000001000000000L); + tmpResources.put("HOST" , 0x0000002000000000L); + tmpResources.put("NET" , 0x0000004000000000L); + tmpResources.put("IMAGE" , 0x0000008000000000L); + tmpResources.put("USER" , 0x0000010000000000L); + tmpResources.put("TEMPLATE" , 0x0000020000000000L); + tmpResources.put("GROUP" , 0x0000040000000000L); + + RESOURCES = Collections.unmodifiableMap(tmpResources); + + HashMap tmpRights = new HashMap(); + + tmpRights.put("CREATE" , 0x1L); + tmpRights.put("DELETE" , 0x2L); + tmpRights.put("USE" , 0x4L); + tmpRights.put("MANAGE" , 0x8L); + tmpRights.put("INFO" , 0x10L); + tmpRights.put("INFO_POOL" , 0x20L); + tmpRights.put("INFO_POOL_MINE", 0x40L); + tmpRights.put("INSTANTIATE" , 0x80L); + tmpRights.put("CHOWN" , 0x100L); + + RIGHTS = Collections.unmodifiableMap(tmpRights); + } + + /** + * @see PoolElement + */ + protected Acl(Node xmlElement, Client client) + { + super(xmlElement, client); + } + + // ================================= + // Static XML-RPC methods + // ================================= + + /** + * Allocates a new ACl rule in OpenNebula + * + * @param client XML-RPC Client. + * @param user A string containing a hex number, e.g. 0x100000001 + * @param resource A string containing a hex number, e.g. 0x2100000001 + * @param rights A string containing a hex number, e.g. 0x10 + * @return If successful the message contains the associated + * id generated for this rule. + */ + public static OneResponse allocate(Client client, String user, + String resource, String rights) + { + return client.call(ADDRULE, user, resource, rights); + } + + /** + * Allocates a new ACl rule in OpenNebula + * + * @param client XML-RPC Client. + * @param user 64b encoded user + * @param resource 64b encoded user + * @param rights 64b encoded user + * @return If successful the message contains the associated + * id generated for this rule. + */ + public static OneResponse allocate(Client client, long user, long resource, + long rights) + { + return allocate(client, + Long.toHexString(user), + Long.toHexString(resource), + Long.toHexString(rights)); + } + + /** + * Allocates a new ACl rule in OpenNebula + * + * @param client XML-RPC Client. + * @param rule a rule string, e.g. "#5 HOST+VM/@12 INFO+CREATE+DELETE" + * @return If successful the message contains the associated + * id generated for this rule. + */ + public static OneResponse allocate(Client client, String rule) + { + String[] components = parseRule(rule); + return allocate(client, components[0], components[1], components[2]); + } + + /** + * Deletes an ACL rule from OpenNebula. + * + * @param client XML-RPC Client. + * @param id The ACL rule id. + * @return A encapsulated response. + */ + public static OneResponse delete(Client client, int id) + { + return client.call(DELRULE, id); + } + + // ================================= + // Instanced object XML-RPC methods + // ================================= + + /** + * Deletes the ACL rule from OpenNebula. + * + * @see Acl#delete(Client, int) + */ + public OneResponse delete() + { + return delete(client, id); + } + + // ================================= + // Helpers + // ================================= + + public long user() + { + long ret = 0; + + try + { + ret = Long.parseLong( xpath("USER"), 16 ); + } + catch (NumberFormatException e) + {} + + return ret; + } + + public long resource() + { + long ret = 0; + + try + { + ret = Long.parseLong( xpath("RESOURCE"), 16 ); + } + catch (NumberFormatException e) + {} + + return ret; + } + + public long rights() + { + long ret = 0; + + try + { + ret = Long.parseLong( xpath("RIGHTS"), 16 ); + } + catch (NumberFormatException e) + {} + + return ret; + } + + public String toString() + { + String st = xpath("STRING"); + + if( st == null ) + { + st = ""; + } + + return st; + } + + // ================================= + // Rule parsing + // ================================= + + /** + * Parses a rule string, e.g. "#5 HOST+VM/@12 INFO+CREATE+DELETE" + * + * @param rule an ACL rule in string format + * @return an Array containing 3 Strings (hex 64b numbers) + */ + public static String[] parseRule(String rule) + { + String [] ret = new String[3]; + + String [] components = rule.split(" "); + + if( components.length != 3 ) + { + // TODO: throw "String needs three components: User, Resource, Rights" + return ret; + } + + ret[0] = parseUsers(components[0]); + ret[1] = parseResources(components[1]); + ret[2] = parseRights(components[2]); + + return ret; + } + + /** + * Converts a string in the form [#, @, *] to a hex. number + * + * @param users Users component string + * @return A string containing a hex number + */ + private static String parseUsers(String users) + { + return Long.toHexString( calculateIds(users) ); + } + + /** + * Converts a resources string to a hex. number + * + * @param resources Resources component string + * @return A string containing a hex number + */ + private static String parseResources(String resources) + { + long ret = 0; + String[] resourcesComponents = resources.split("/"); + + if( resourcesComponents.length != 2 ) + { + // TODO: throw "Resource '#{resources}' malformed" + return ""; + } + + for( String resource : resourcesComponents[0].split("\\+") ) + { + resource = resource.toUpperCase(); + + if( !RESOURCES.containsKey(resource) ) + { + // TODO: throw "Resource '#{resource}' does not exist" + } + + ret += RESOURCES.get(resource); + } + + ret += calculateIds(resourcesComponents[1]); + + return Long.toHexString(ret); + } + + /** + * Converts a rights string to a hex. number + * + * @param rights Rights component string + * @return A string containing a hex number + */ + private static String parseRights(String rights) + { + long ret = 0; + + + for( String right : rights.split("\\+") ) + { + right = right.toUpperCase(); + + if( !RIGHTS.containsKey(right) ) + { + // TODO throw "Right '#{right}' does not exist" + return ""; + } + + ret += RIGHTS.get(right); + } + + return Long.toHexString(ret); + } + + /** + * Calculates the numeric value for a String containing an individual + * (#id), group (@id) or all (*) ID component + * + * @param id Rule Id string + * @return the numeric value for the given id_str + */ + private static long calculateIds(String id) + { + if( !id.matches("^([#@]\\d+|\\*)$") ) + { + // TODO: throw "ID string '#{id_str}' malformed" + return 0; + } + + long value = USERS.get( "" + id.charAt(0) ); + + if( id.charAt(0) != '*' ) + { + value += Long.parseLong( id.substring(1) ); + } + + return value; + } +} diff --git a/src/oca/java/src/org/opennebula/client/acl/AclPool.java b/src/oca/java/src/org/opennebula/client/acl/AclPool.java new file mode 100644 index 0000000000..22c6ddab1a --- /dev/null +++ b/src/oca/java/src/org/opennebula/client/acl/AclPool.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ +package org.opennebula.client.acl; + +import java.util.AbstractList; +import java.util.Iterator; + + +import org.opennebula.client.Client; +import org.opennebula.client.OneResponse; +import org.opennebula.client.Pool; +import org.opennebula.client.PoolElement; +import org.w3c.dom.Node; + +/** + * This class represents an OpenNebula ACL rule pool. + * It also offers static XML-RPC call wrappers. + */ +public class AclPool extends Pool implements Iterable{ + + private static final String ELEMENT_NAME = "ACL"; + private static final String INFO_METHOD = "acl.info"; + + /** + * Creates a new ACL rule pool + * @param client XML-RPC Client. + */ + public AclPool(Client client) + { + super(ELEMENT_NAME, client); + } + + @Override + public PoolElement factory(Node node) + { + return new Acl(node, client); + } + + /** + * Retrieves all the hosts in the pool. + * + * @param client XML-RPC Client. + * @return If successful the message contains the string + * with the information returned by OpenNebula. + */ + public static OneResponse info(Client client) + { + return client.call(INFO_METHOD); + } + + /** + * Loads the xml representation of the ACL rule pool. + * + * @see AclPool#info(Client) + */ + public OneResponse info() + { + OneResponse response = info(client); + super.processInfo(response); + return response; + } + + public Iterator iterator() + { + AbstractList ab = new AbstractList() + { + public int size() + { + return getLength(); + } + + public Acl get(int index) + { + return (Acl) item(index); + } + }; + + return ab.iterator(); + } + + public Acl getById(int id) + { + return (Acl) super.getById(id); + } +} diff --git a/src/oca/java/test/AclTest.java b/src/oca/java/test/AclTest.java new file mode 100644 index 0000000000..9b08558e64 --- /dev/null +++ b/src/oca/java/test/AclTest.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * 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. + ******************************************************************************/ +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.opennebula.client.Client; +import org.opennebula.client.OneResponse; +import org.opennebula.client.acl.*; + +public class AclTest +{ + + private static Acl acl; + private static AclPool aclPool; + + private static Client client; + + private static OneResponse res; + + /** + * @throws java.lang.Exception + */ + @BeforeClass + public static void setUpBeforeClass() throws Exception + { + client = new Client(); + aclPool = new AclPool(client); + } + + /** + * @throws java.lang.Exception + */ + @AfterClass + public static void tearDownAfterClass() throws Exception + { + } + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception + { + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception + { + for(Acl rule : aclPool) + { + if( rule.id() != 0 ) + { + rule.delete(); + } + } + } + + @Test + public void defaultRules() + { + res = aclPool.info(); + assertTrue( !res.isError() ); + + assertEquals(1, aclPool.getLength()); + } + + @Test + public void hexAllocate() + { + // Allocate rule "#1 VM+HOST/@1 INFO+CREATE" + res = Acl.allocate(client, "0x100000001", "0x3200000001", "0x11"); + assertTrue( !res.isError() ); + + aclPool.info(); + acl = aclPool.getById( res.getIntMessage() ); + + assertNotNull(acl); + + assertEquals(res.getIntMessage(), acl.id()); + assertEquals(0x100000001L, acl.user()); + assertEquals(0x3200000001L, acl.resource()); + assertEquals(0x11L, acl.rights()); + assertEquals("#1 VM+HOST/@1 CREATE+INFO", acl.toString()); + } + + @Test + public void numericAllocate() + { + // Allocate rule "#1 VM+HOST/@1 INFO+CREATE" + res = Acl.allocate(client, 0x100000001L, 214748364801L, 0x11L); + assertTrue( !res.isError() ); + + aclPool.info(); + acl = aclPool.getById( res.getIntMessage() ); + + assertNotNull(acl); + + assertEquals(res.getIntMessage(), acl.id()); + assertEquals(0x100000001L, acl.user()); + assertEquals(0x3200000001L, acl.resource()); + assertEquals(0x11L, acl.rights()); + assertEquals("#1 VM+HOST/@1 CREATE+INFO", acl.toString()); + } + + @Test + public void ruleAllocate() + { + res = Acl.allocate(client, "@507 IMAGE/#456 CREATE"); + assertTrue( !res.isError() ); + + aclPool.info(); + acl = aclPool.getById( res.getIntMessage() ); + + assertNotNull(acl); + + assertEquals(res.getIntMessage(), acl.id()); + assertEquals(0x2000001fbL, acl.user()); + assertEquals(0x81000001c8L, acl.resource()); + assertEquals(0x1L, acl.rights()); + assertEquals("@507 IMAGE/#456 CREATE", acl.toString()); + } + + @Test + public void parseRules() + { + String[] rules = { + "#3 TEMPLATE/#0 INFO", + "#2 IMAGE/#0 INFO", + "@107 IMAGE+TEMPLATE/@100 INFO", + "* VM+IMAGE+TEMPLATE/@100 CREATE+INFO+INFO_POOL", + "#2345 VM+IMAGE+TEMPLATE/* CREATE+INFO+INFO_POOL+INFO_POOL_MINE+INSTANTIATE" + }; + + long[] users = { + 0x100000003L, + 0x100000002L, + 0x20000006bL, + 0x400000000L, + 0x100000929L + }; + + long[] resources = { + 0x20100000000L, + 0x8100000000L, + 0x28200000064L, + 0x29200000064L, + 0x29400000000L + }; + + long[] rights = { + 0x10L, + 0x10L, + 0x10L, + 0x31L, + 0xf1L + }; + + for( int i = 0; i < rules.length; i++ ) + { + res = Acl.allocate(client, rules[i]); + assertTrue( !res.isError() ); + + aclPool.info(); + acl = aclPool.getById( res.getIntMessage() ); + + assertNotNull(acl); + + assertEquals(res.getIntMessage(), acl.id()); + assertEquals(users[i], acl.user()); + assertEquals(resources[i], acl.resource()); + assertEquals(rights[i], acl.rights()); + } + + assertTrue( true ); + } + + @Test + public void delete() + { + res = Acl.allocate(client, "#1 HOST/@2 INFO_POOL"); + assertTrue( !res.isError() ); + + aclPool.info(); + assertTrue( aclPool.getLength() == 2 ); + + res = Acl.delete(client, res.getIntMessage()); + assertTrue( !res.isError() ); + + aclPool.info(); + assertTrue( aclPool.getLength() == 1 ); + } + +} diff --git a/src/oca/java/test/all_tests.sh b/src/oca/java/test/all_tests.sh index b6dfb55c7e..27b17915c7 100755 --- a/src/oca/java/test/all_tests.sh +++ b/src/oca/java/test/all_tests.sh @@ -1,5 +1,21 @@ #!/bin/bash +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + if [ -z $ONE_LOCATION ]; then echo "ONE_LOCATION not defined." exit -1 @@ -42,4 +58,7 @@ let RC=RC+$? ./test.sh GroupTest let RC=RC+$? +./test.sh AclTest +let RC=RC+$? + exit $RC \ No newline at end of file diff --git a/src/oca/java/test/test.sh b/src/oca/java/test/test.sh index 77754dd27f..1a2c3ba4fc 100755 --- a/src/oca/java/test/test.sh +++ b/src/oca/java/test/test.sh @@ -1,4 +1,21 @@ #!/bin/bash + +# -------------------------------------------------------------------------- # +# 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. # +#--------------------------------------------------------------------------- # + # Usage: test.sh # For instance: test.sh ImageTest @@ -16,6 +33,9 @@ if [ -f $VAR_LOCATION/one.db ]; then exit -1 fi +echo "=========================================================================" +echo "Doing $1" +echo "=========================================================================" PID=$$