You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by gs...@apache.org on 2011/02/23 13:59:50 UTC

svn commit: r1073726 - in /qpid/trunk/qpid/cpp/src: qpid/broker/Broker.cpp qpid/broker/Broker.h qpid/management/ManagementAgent.cpp tests/acl.py

Author: gsim
Date: Wed Feb 23 12:59:50 2011
New Revision: 1073726

URL: http://svn.apache.org/viewvc?rev=1073726&view=rev
Log:
QPID-3015: cleanup and additional test of acl enforcement

[Also modified acl unit test module to allow the policy file used to be configurable, making it easier to run standalone. Reset policy to allow all on tear down to allow repeated runs.]


Modified:
    qpid/trunk/qpid/cpp/src/qpid/broker/Broker.cpp
    qpid/trunk/qpid/cpp/src/qpid/broker/Broker.h
    qpid/trunk/qpid/cpp/src/qpid/management/ManagementAgent.cpp
    qpid/trunk/qpid/cpp/src/tests/acl.py

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Broker.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Broker.cpp?rev=1073726&r1=1073725&r2=1073726&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Broker.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Broker.cpp Wed Feb 23 12:59:50 2011
@@ -593,7 +593,7 @@ void Broker::createObject(const std::str
         }
     } else if (type == TYPE_EXCHANGE || type == TYPE_TOPIC) {
         bool durable(false);
-        std::string exchangeType;
+        std::string exchangeType("topic");
         std::string alternateExchange;
         Variant::Map extensions;
         for (Variant::Map::const_iterator i = properties.begin(); i != properties.end(); ++i) {
@@ -787,7 +787,7 @@ std::pair<boost::shared_ptr<Queue>, bool
     Exchange::shared_ptr alternate;
     if (!alternateExchange.empty()) {
         alternate = exchanges.get(alternateExchange);
-        if (!alternate) framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+        if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
     }
 
     std::pair<Queue::shared_ptr, bool> result = queues.declare(name, durable, autodelete, owner);
@@ -859,7 +859,7 @@ std::pair<Exchange::shared_ptr, bool> Br
     Exchange::shared_ptr alternate;
     if (!alternateExchange.empty()) {
         alternate = exchanges.get(alternateExchange);
-        if (!alternate) framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
+        if (!alternate) throw framing::NotFoundException(QPID_MSG("Alternate exchange does not exist: " << alternateExchange));
     }
 
     std::pair<Exchange::shared_ptr, bool> result;

Modified: qpid/trunk/qpid/cpp/src/qpid/broker/Broker.h
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/broker/Broker.h?rev=1073726&r1=1073725&r2=1073726&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/broker/Broker.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/broker/Broker.h Wed Feb 23 12:59:50 2011
@@ -153,7 +153,7 @@ public:
     void setLogLevel(const std::string& level);
     std::string getLogLevel();
     void createObject(const std::string& type, const std::string& name,
-                      const qpid::types::Variant::Map& properties, bool lenient, const ConnectionState* context);
+                      const qpid::types::Variant::Map& properties, bool strict, const ConnectionState* context);
     void deleteObject(const std::string& type, const std::string& name,
                       const qpid::types::Variant::Map& options, const ConnectionState* context);
 

Modified: qpid/trunk/qpid/cpp/src/qpid/management/ManagementAgent.cpp
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/management/ManagementAgent.cpp?rev=1073726&r1=1073725&r2=1073726&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/management/ManagementAgent.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/management/ManagementAgent.cpp Wed Feb 23 12:59:50 2011
@@ -75,6 +75,18 @@ namespace {
         }
         return n2;
     }
+
+struct ScopedManagementContext
+{
+    ScopedManagementContext(const qpid::broker::ConnectionState* context)
+    {
+        setManagementExecutionContext(context);
+    }
+    ~ScopedManagementContext()
+    {
+        setManagementExecutionContext(0);
+    }
+};
 }
 
 
@@ -2238,7 +2250,7 @@ void ManagementAgent::dispatchAgentComma
     uint32_t bufferLen = inBuffer.getPosition();
     inBuffer.reset();
 
-    setManagementExecutionContext((const qpid::broker::ConnectionState*) msg.getPublisher());
+    ScopedManagementContext context((const qpid::broker::ConnectionState*) msg.getPublisher());
     const framing::FieldTable *headers = msg.getApplicationHeaders();
     if (headers && msg.getAppId() == "qmf2")
     {

Modified: qpid/trunk/qpid/cpp/src/tests/acl.py
URL: http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/tests/acl.py?rev=1073726&r1=1073725&r2=1073726&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/tests/acl.py (original)
+++ qpid/trunk/qpid/cpp/src/tests/acl.py Wed Feb 23 12:59:50 2011
@@ -26,10 +26,11 @@ from qpid.datatypes import uuid4
 from qpid.testlib import TestBase010
 from qmf.console import Session
 from qpid.datatypes import Message
+import qpid.messaging
 
 class ACLFile:
-    def __init__(self):
-        self.f = open('data_dir/policy.acl','w');
+    def __init__(self, policy='data_dir/policy.acl'):
+        self.f = open(policy,'w')
    
     def write(self,line):
         self.f.write(line)
@@ -50,14 +51,24 @@ class ACLTests(TestBase010):
         acl = self.qmf.getObjects(_class="acl")[0]    
         return acl.reloadACLFile()
 
+    def get_acl_file(self):
+        return ACLFile(self.config.defines.get("policy-file", "data_dir/policy.acl"))
+
     def setUp(self):
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow all all\n')
         aclf.close()
         TestBase010.setUp(self)
         self.startQmf()
         self.reload_acl()
-        
+
+    def tearDown(self):
+        aclf = self.get_acl_file()
+        aclf.write('acl allow all all\n')
+        aclf.close()
+        self.reload_acl()
+        TestBase010.tearDown(self)
+
    #=====================================
    # ACL general tests
    #=====================================     
@@ -66,7 +77,7 @@ class ACLTests(TestBase010):
         """
         Test the deny all mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow anonymous all all\n')
         aclf.write('acl allow bob@QPID create queue\n')
         aclf.write('acl deny all all')
@@ -94,7 +105,7 @@ class ACLTests(TestBase010):
         """
         Test the allow all mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID bind exchange\n')
         aclf.write('acl allow all all')
         aclf.close()        
@@ -126,7 +137,7 @@ class ACLTests(TestBase010):
         """
         Test empty groups
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl group\n')
         aclf.write('acl group admins bob@QPID joe@QPID\n')
         aclf.write('acl allow all all')
@@ -140,7 +151,7 @@ class ACLTests(TestBase010):
         """
         Test illegal acl formats
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl group admins bob@QPID joe@QPID\n')
         aclf.write('acl allow all all')
         aclf.close()
@@ -154,7 +165,7 @@ class ACLTests(TestBase010):
         Test illegal extension lines
         """
          
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('group admins bob@QPID \n')
         aclf.write('          \ \n')
         aclf.write('joe@QPID \n')
@@ -172,7 +183,7 @@ class ACLTests(TestBase010):
         """
         Test proper extention lines
         """
-        aclf = ACLFile()        
+        aclf = self.get_acl_file()
         aclf.write('group test1 joe@EXAMPLE.com \\ \n') # should be allowed
         aclf.write('            jack@EXAMPLE.com \\ \n') # should be allowed
         aclf.write('jill@TEST.COM \\ \n') # should be allowed
@@ -189,7 +200,7 @@ class ACLTests(TestBase010):
         Test a user defined without a realm
         Ex. group admin rajith
         """
-        aclf = ACLFile()        
+        aclf = self.get_acl_file()
         aclf.write('group admin bob\n') # shouldn't be allowed
         aclf.write('acl deny admin bind exchange\n')
         aclf.write('acl allow all all')
@@ -204,7 +215,7 @@ class ACLTests(TestBase010):
         Test a user defined without a realm
         Ex. group admin rajith
         """
-        aclf = ACLFile()        
+        aclf = self.get_acl_file()
         aclf.write('group test1 joe@EXAMPLE.com\n') # should be allowed
         aclf.write('group test2 jack_123-jill@EXAMPLE.com\n') # should be allowed
         aclf.write('group test4 host/somemachine.example.com@EXAMPLE.COM\n') # should be allowed
@@ -215,7 +226,7 @@ class ACLTests(TestBase010):
         if (result.text.find("ACL format error",0,len(result.text)) != -1):
             self.fail(result)
 
-        aclf = ACLFile()        
+        aclf = self.get_acl_file()
         aclf.write('group test1 joe$H@EXAMPLE.com\n') # shouldn't be allowed
         aclf.write('acl allow all all')
         aclf.close() 
@@ -233,7 +244,7 @@ class ACLTests(TestBase010):
         Test illegal queue policy
         """
          
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ding\n')
         aclf.write('acl allow all all')
         aclf.close()        
@@ -249,7 +260,7 @@ class ACLTests(TestBase010):
         Test illegal queue policy
         """
          
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=-1\n')
         aclf.write('acl allow all all')
         aclf.close()        
@@ -260,7 +271,7 @@ class ACLTests(TestBase010):
         if (result.text != expected): 
             self.fail(result) 
 
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q2 maxqueuesize=9223372036854775808\n')
         aclf.write('acl allow all all')                                 
         aclf.close()        
@@ -277,7 +288,7 @@ class ACLTests(TestBase010):
         Test illegal queue policy
         """
          
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=-1\n')
         aclf.write('acl allow all all')
         aclf.close()        
@@ -288,7 +299,7 @@ class ACLTests(TestBase010):
         if (result.text != expected): 
             self.fail(result) 
 
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q2 maxqueuecount=9223372036854775808\n')
         aclf.write('acl allow all all')                                 
         aclf.close()        
@@ -308,7 +319,7 @@ class ACLTests(TestBase010):
         """
         Test cases for queue acl in allow mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create queue name=q1 durable=true passive=true\n')
         aclf.write('acl deny bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
         aclf.write('acl deny bob@QPID access queue name=q3\n')
@@ -411,7 +422,7 @@ class ACLTests(TestBase010):
         """
         Test cases for queue acl in deny mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow bob@QPID create queue name=q1 durable=true passive=true\n')
         aclf.write('acl allow bob@QPID create queue name=q2 exclusive=true policytype=ring\n')
         aclf.write('acl allow bob@QPID access queue name=q3\n')
@@ -534,7 +545,7 @@ class ACLTests(TestBase010):
         """
         Test cases for exchange acl in allow mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID create exchange name=testEx durable=true passive=true\n')
         aclf.write('acl deny bob@QPID create exchange name=ex1 type=direct\n')
         aclf.write('acl deny bob@QPID access exchange name=myEx queuename=q1 routingkey=rk1.*\n')
@@ -665,7 +676,7 @@ class ACLTests(TestBase010):
         """
         Test cases for exchange acl in deny mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow bob@QPID create exchange name=myEx durable=true passive=false\n')
         aclf.write('acl allow bob@QPID bind exchange name=amq.topic queuename=bar routingkey=foo.*\n') 
         aclf.write('acl allow bob@QPID unbind exchange name=amq.topic queuename=bar routingkey=foo.*\n')
@@ -772,6 +783,52 @@ class ACLTests(TestBase010):
             if (403 == e.args[0].error_code):
                 self.fail("ACL should allow exchange delete request for myEx");
 
+    def test_create_and_delete_exchange_via_qmf(self):
+        """
+        Test acl is enforced when creating/deleting via QMF
+        methods. Note that in order to be able to send the QMF methods
+        and receive the responses a significant amount of permissions
+        need to be enabled (TODO: can the set below be narrowed down
+        at all?)
+        """
+        aclf = self.get_acl_file()
+        aclf.write('acl allow bob@QPID create exchange\n')
+        aclf.write('acl allow admin@QPID delete exchange\n')
+        aclf.write('acl allow all access exchange\n')
+        aclf.write('acl allow all bind exchange\n')
+        aclf.write('acl allow all create queue\n')
+        aclf.write('acl allow all access queue\n')
+        aclf.write('acl allow all delete queue\n')
+        aclf.write('acl allow all consume queue\n')
+        aclf.write('acl allow all access method\n')
+        aclf.write('acl deny all all')
+        aclf.close()
+
+        result = self.reload_acl()
+        if (result.text.find("format error",0,len(result.text)) != -1):
+            self.fail(result)
+
+        bob = BrokerAdmin(self.config.broker, "bob", "bob")
+        bob.create_exchange("my-exchange") #should pass
+        #cleanup by deleting exchange
+        try:
+            bob.delete_exchange("my-exchange") #should fail
+            self.fail("ACL should deny exchange delete request for my-exchange");
+        except Exception, e:
+            self.assertEqual(7,e.args[0]["error_code"])
+            self.assertTrue(e.args[0]["error_text"].find("unauthorized-access") == 0)
+        admin = BrokerAdmin(self.config.broker, "admin", "admin")
+        admin.delete_exchange("my-exchange") #should pass
+
+        anonymous = BrokerAdmin(self.config.broker)
+        try:
+            anonymous.create_exchange("another-exchange") #should fail
+            self.fail("ACL should deny exchange create request for another-exchange");
+        except Exception, e:
+            self.assertEqual(7,e.args[0]["error_code"])
+            self.assertTrue(e.args[0]["error_text"].find("unauthorized-access") == 0)
+
+
    #=====================================
    # ACL consume tests
    #=====================================
@@ -780,7 +837,7 @@ class ACLTests(TestBase010):
         """
         Test cases for consume in allow mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID consume queue name=q1\n')
         aclf.write('acl deny bob@QPID consume queue name=q2\n')                
         aclf.write('acl allow all all')
@@ -826,7 +883,7 @@ class ACLTests(TestBase010):
         """
         Test cases for consume in allow mode
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow bob@QPID consume queue name=q1\n')
         aclf.write('acl allow bob@QPID consume queue name=q2\n')
         aclf.write('acl allow bob@QPID create queue\n')                                
@@ -872,7 +929,7 @@ class ACLTests(TestBase010):
         """
         Test various publish acl
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl deny bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
         aclf.write('acl deny bob@QPID publish exchange name=amq.topic\n')
         aclf.write('acl deny bob@QPID publish exchange name=myEx routingkey=rk2\n')                
@@ -921,7 +978,7 @@ class ACLTests(TestBase010):
         """
         Test various publish acl
         """
-        aclf = ACLFile()
+        aclf = self.get_acl_file()
         aclf.write('acl allow bob@QPID publish exchange name=amq.direct routingkey=rk1\n')
         aclf.write('acl allow bob@QPID publish exchange name=amq.topic\n')
         aclf.write('acl allow bob@QPID publish exchange name=myEx routingkey=rk2\n')
@@ -972,3 +1029,49 @@ class ACLTests(TestBase010):
         except qpid.session.SessionException, e:
             if (403 == e.args[0].error_code):
                 self.fail("ACL should allow message transfer to exchange amq.direct with routing key rk1");
+
+class BrokerAdmin:
+    def __init__(self, broker, username=None, password=None):
+        self.connection = qpid.messaging.Connection(broker)
+        if username:
+            self.connection.username = username
+            self.connection.password = password
+            self.connection.sasl_mechanisms = "PLAIN"
+        self.connection.open()
+        self.session = self.connection.session()
+        self.sender = self.session.sender("qmf.default.direct/broker")
+        self.reply_to = "responses-#; {create:always}"
+        self.receiver = self.session.receiver(self.reply_to)
+
+    def invoke(self, method, arguments):
+        content = {
+            "_object_id": {"_object_name": "org.apache.qpid.broker:broker:amqp-broker"},
+            "_method_name": method,
+            "_arguments": arguments
+            }
+        request = qpid.messaging.Message(reply_to=self.reply_to, content=content)
+        request.properties["x-amqp-0-10.app-id"] = "qmf2"
+        request.properties["qmf.opcode"] = "_method_request"
+        self.sender.send(request)
+        response = self.receiver.fetch()
+        self.session.acknowledge()
+        if response.properties['x-amqp-0-10.app-id'] == 'qmf2':
+            if response.properties['qmf.opcode'] == '_method_response':
+                return response.content['_arguments']
+            elif response.properties['qmf.opcode'] == '_exception':
+                raise Exception(response.content['_values'])
+            else: raise Exception("Invalid response received, unexpected opcode: %s" % response.properties['qmf.opcode'])
+        else: raise Exception("Invalid response received, not a qmfv2 method: %s" % response.properties['x-amqp-0-10.app-id'])
+    def create_exchange(self, name, exchange_type=None, options={}):
+        properties = options
+        if exchange_type: properties["exchange_type"] = exchange_type
+        self.invoke("create", {"type": "exchange", "name":name, "properties":properties})
+
+    def create_queue(self, name, properties={}):
+        self.invoke("create", {"type": "queue", "name":name, "properties":properties})
+
+    def delete_exchange(self, name):
+        self.invoke("delete", {"type": "exchange", "name":name})
+
+    def delete_queue(self, name):
+        self.invoke("delete", {"type": "queue", "name":name})



---------------------------------------------------------------------
Apache Qpid - AMQP Messaging Implementation
Project:      http://qpid.apache.org
Use/Interact: mailto:commits-subscribe@qpid.apache.org