Author: chug
Date: Fri Apr 27 19:26:18 2012
New Revision: 1331549

URL: http://svn.apache.org/viewvc?rev=1331549&view=rev
Log:
QPID-2616 Count and limit client connections
Limit client connections by name and by host address.


Added:
    qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
    qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
Modified:
    qpid/trunk/qpid/cpp/src/CMakeLists.txt
    qpid/trunk/qpid/cpp/src/acl.mk
    qpid/trunk/qpid/cpp/src/qpid/acl/Acl.cpp
    qpid/trunk/qpid/cpp/src/qpid/acl/Acl.h
    qpid/trunk/qpid/cpp/src/qpid/acl/AclPlugin.cpp

Modified: qpid/trunk/qpid/cpp/src/CMakeLists.txt
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/CMakeLists.txt?rev=1331549&r1=1331548&r2=1331549&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/CMakeLists.txt (original)
+++ qpid/trunk/qpid/cpp/src/CMakeLists.txt Fri Apr 27 19:26:18 2012
@@ -594,6 +594,8 @@ if (BUILD_ACL)
   set (acl_SOURCES
        qpid/acl/Acl.cpp
        qpid/acl/Acl.h
+       qpid/acl/AclConnectionCounter.cpp
+       qpid/acl/AclConnectionCounter.h
        qpid/acl/AclData.cpp
        qpid/acl/AclData.h
        qpid/acl/AclPlugin.cpp

Modified: qpid/trunk/qpid/cpp/src/acl.mk
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/acl.mk?rev=1331549&r1=1331548&r2=1331549&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/acl.mk (original)
+++ qpid/trunk/qpid/cpp/src/acl.mk Fri Apr 27 19:26:18 2012
@@ -24,6 +24,8 @@ dmoduleexec_LTLIBRARIES += acl.la
 acl_la_SOURCES = \
   qpid/acl/Acl.cpp \
   qpid/acl/Acl.h \
+  qpid/acl/AclConnectionCounter.cpp \
+  qpid/acl/AclConnectionCounter.h \
   qpid/acl/AclData.cpp \
   qpid/acl/AclData.h \
   qpid/acl/AclPlugin.cpp \

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/Acl.cpp
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/Acl.cpp?rev=1331549&r1=1331548&r2=1331549&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/Acl.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/Acl.cpp Fri Apr 27 19:26:18 2012
@@ -17,6 +17,7 @@
  */
 
 #include "qpid/acl/Acl.h"
+#include "qpid/acl/AclConnectionCounter.h"
 #include "qpid/acl/AclData.h"
 #include "qpid/acl/AclValidator.h"
 #include "qpid/sys/Mutex.h"
@@ -48,7 +49,8 @@ using qpid::management::Manageable;
 using qpid::management::Args;
 namespace _qmf = qmf::org::apache::qpid::acl;
 
-Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), 
transferAcl(false), mgmtObject(0)
+Acl::Acl (AclValues& av, Broker& b): aclValues(av), broker(&b), 
transferAcl(false), mgmtObject(0),
+    connectionCounter(new ConnectionCounter(aclValues.aclMaxConnectPerUser, 
aclValues.aclMaxConnectPerIp))
 {
 
     agent = broker->getManagementAgent();
@@ -63,6 +65,7 @@ Acl::Acl (AclValues& av, Broker& b): acl
         throw Exception("Could not read ACL file " + errorString);
         if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0);
     }
+    broker->getConnectionObservers().add(connectionCounter);
     QPID_LOG(info, "ACL Plugin loaded");
     if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1);
 }
@@ -267,7 +270,9 @@ Manageable::status_t Acl::lookupPublish(
 }
 
 
-Acl::~Acl(){}
+Acl::~Acl(){
+    broker->getConnectionObservers().remove(connectionCounter);
+}
 
 ManagementObject* Acl::GetManagementObject(void) const
 {

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/Acl.h
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/Acl.h?rev=1331549&r1=1331548&r2=1331549&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/Acl.h (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/Acl.h Fri Apr 27 19:26:18 2012
@@ -30,6 +30,7 @@
 #include "qmf/org/apache/qpid/acl/Acl.h"
 #include "qpid/sys/Mutex.h"
 
+#include <boost/shared_ptr.hpp>
 #include <map>
 #include <string>
 
@@ -40,9 +41,12 @@ class Broker;
 }
 
 namespace acl {
+class ConnectionCounter;
 
 struct AclValues {
-       std::string aclFile;
+    std::string aclFile;
+    uint32_t    aclMaxConnectPerUser;
+    uint32_t    aclMaxConnectPerIp;
 };
 
 
@@ -50,55 +54,56 @@ class Acl : public broker::AclModule, pu
 {
 
 private:
-   acl::AclValues aclValues;
-   broker::Broker* broker;
-   bool transferAcl;
-   boost::shared_ptr<AclData> data;
-   qmf::org::apache::qpid::acl::Acl* mgmtObject; // mgnt owns lifecycle
-   qpid::management::ManagementAgent* agent;
-   mutable qpid::sys::Mutex dataLock;
+    acl::AclValues                       aclValues;
+    broker::Broker*                      broker;
+    bool                                 transferAcl;
+    boost::shared_ptr<AclData>           data;
+    qmf::org::apache::qpid::acl::Acl*    mgmtObject; // mgnt owns lifecycle
+    qpid::management::ManagementAgent*   agent;
+    mutable qpid::sys::Mutex             dataLock;
+    boost::shared_ptr<ConnectionCounter> connectionCounter;
 
 public:
-   Acl (AclValues& av, broker::Broker& b);
+    Acl (AclValues& av, broker::Broker& b);
 
-   void initialize();
+    void initialize();
 
-   inline virtual bool doTransferAcl() {return transferAcl;};
+    inline virtual bool doTransferAcl() {
+        return transferAcl;
+    };
+
+// create specilied authorise methods for cases that need faster matching as 
needed.
+    virtual bool authorise(
+        const std::string&               id,
+        const Action&                    action,
+        const ObjectType&                objType,
+        const std::string&               name,
+        std::map<Property, std::string>* params=0);
+
+    virtual bool authorise(
+        const std::string&               id,
+        const Action&                    action,
+        const ObjectType&                objType,
+        const std::string&               ExchangeName,
+        const std::string&               RoutingKey);
 
-   // create specilied authorise methods for cases that need faster matching 
as needed.
-   virtual bool authorise(
-       const std::string& id,
-       const Action& action,
-       const ObjectType& objType,
-       const std::string& name,
-       std::map<Property, std::string>* params=0);
-
-   virtual bool authorise(
-       const std::string& id,
-       const Action& action,
-       const ObjectType& objType,
-       const std::string& ExchangeName,
-       const std::string& RoutingKey);
-
-   virtual ~Acl();
+    virtual ~Acl();
 private:
-   bool result(
-       const AclResult& aclreslt,
-       const std::string& id,
-       const Action& action,
-       const ObjectType& objType,
-       const std::string& name);
-   bool readAclFile(std::string& errorText);
-   bool readAclFile(std::string& aclFile, std::string& errorText);
-   Manageable::status_t lookup       (management::Args& args, std::string& 
text);
-   Manageable::status_t lookupPublish(management::Args& args, std::string& 
text);
-   virtual qpid::management::ManagementObject* GetManagementObject(void) const;
-   virtual management::Manageable::status_t ManagementMethod (uint32_t 
methodId, management::Args& args, std::string& text);
+    bool result(
+        const AclResult&   aclreslt,
+        const std::string& id,
+        const Action&      action,
+        const ObjectType&  objType,
+        const std::string& name);
+    bool readAclFile(std::string& errorText);
+    bool readAclFile(std::string& aclFile, std::string& errorText);
+    Manageable::status_t lookup       (management::Args& args, std::string& 
text);
+    Manageable::status_t lookupPublish(management::Args& args, std::string& 
text);
+    virtual qpid::management::ManagementObject* GetManagementObject(void) 
const;
+    virtual management::Manageable::status_t ManagementMethod (uint32_t 
methodId, management::Args& args, std::string& text);
 
 };
 
-
-
 }} // namespace qpid::acl
 
 #endif // QPID_ACL_ACL_H

Added: qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp?rev=1331549&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp (added)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp Fri Apr 27 
19:26:18 2012
@@ -0,0 +1,211 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "AclConnectionCounter.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include <assert.h>
+#include <sstream>
+
+using namespace qpid::sys;
+
+namespace qpid {
+namespace acl {
+
+//
+// This module instantiates a broker::ConnectionObserver and limits client
+// connections by counting connections per user name and per client IP address.
+//
+
+
+//
+//
+//
+ConnectionCounter::ConnectionCounter(uint32_t nl, uint32_t hl) :
+    nameLimit(nl), hostLimit(hl) {}
+
+ConnectionCounter::~ConnectionCounter() {}
+
+
+//
+// limitCheckLH
+//
+// Increment the name's count in map and return a comparison against the limit.
+// called with dataLock already taken
+//
+bool ConnectionCounter::limitCheckLH(
+    connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) 
{
+
+    bool result(true);
+    if (theLimit > 0) {
+        connectCountsMap_t::iterator eRef = theMap.find(theName);
+        if (eRef != theMap.end()) {
+            uint32_t count = (uint32_t)(*eRef).second + 1;
+            (*eRef).second = count;
+            result = count <= theLimit;
+        } else {
+            theMap[theName] = 1;
+        }
+    }
+    return result;
+}
+
+
+//
+// releaseLH
+//
+// Decrement the name's count in map.
+// called with dataLock already taken
+//
+void ConnectionCounter::releaseLH(
+    connectCountsMap_t& theMap, const std::string& theName, uint32_t theLimit) 
{
+
+    if (theLimit > 0) {
+        connectCountsMap_t::iterator eRef = theMap.find(theName);
+        if (eRef != theMap.end()) {
+            uint32_t count = (uint32_t) (*eRef).second;
+            assert (count > 0);
+            if (1 == count) {
+                theMap.erase (eRef);
+            } else {
+                (*eRef).second = count - 1;
+            }
+        } else {
+            // User had no connections.
+            QPID_LOG(notice, "ACL ConnectionCounter Connection for '" << 
theName
+                << "' not found in connection count pool");
+        }
+    }
+}
+
+
+//
+// connection - called during Connection's constructor
+//
+void ConnectionCounter::connection(broker::Connection& connection) {
+    QPID_LOG(trace, "ACL ConnectionCounter connection IP:" << 
connection.getMgmtId()
+        << ", user:" << connection.getUsername());
+
+    Mutex::ScopedLock locker(dataLock);
+
+    connectProgressMap[connection.getMgmtId()] = C_CREATED;
+}
+
+
+//
+// opened - called when first AMQP frame is received over Connection
+//
+void ConnectionCounter::opened(broker::Connection& connection) {
+    QPID_LOG(trace, "ACL ConnectionCounter Opened IP:" << 
connection.getMgmtId()
+        << ", user:" << connection.getUsername());
+
+    Mutex::ScopedLock locker(dataLock);
+
+    const std::string& userName(              connection.getUsername());
+    const std::string& hostName(getClientHost(connection.getMgmtId()));
+
+    // Bump state from CREATED to OPENED
+    (void) limitCheckLH(connectProgressMap, connection.getMgmtId(), C_OPENED);
+
+    bool nameOk = limitCheckLH(connectByNameMap, userName, nameLimit);
+    bool hostOk = limitCheckLH(connectByHostMap, hostName, hostLimit);
+
+    if (!nameOk) {
+        // User has too many
+        QPID_LOG(info, "ACL ConnectionCounter User '" << userName
+            << "' exceeded maximum allowed connections");
+        throw Exception(
+            QPID_MSG("User '" << userName
+                << "' exceeded maximum allowed connections"));
+    }
+
+    if (!hostOk) {
+        // Host has too many
+        QPID_LOG(info, "ACL ConnectionCounter Client host '" << hostName
+            << "' exceeded maximum allowed connections");
+        throw Exception(
+            QPID_MSG("Client host '" << hostName
+                << "' exceeded maximum allowed connections"));
+    }
+}
+
+
+//
+// closed - called during Connection's destructor
+//
+void ConnectionCounter::closed(broker::Connection& connection) {
+    QPID_LOG(trace, "ACL ConnectionCounter Closed IP:" << 
connection.getMgmtId()
+        << ", user:" << connection.getUsername());
+
+    Mutex::ScopedLock locker(dataLock);
+
+    connectCountsMap_t::iterator eRef = 
connectProgressMap.find(connection.getMgmtId());
+    if (eRef != connectProgressMap.end()) {
+        if ((*eRef).second == C_OPENED){
+            // Normal case: connection was created and opened.
+            // Decrement in-use counts
+            releaseLH(connectByNameMap,
+                      connection.getUsername(),
+                      nameLimit);
+
+            releaseLH(connectByHostMap,
+                      getClientHost(connection.getMgmtId()),
+                      hostLimit);
+        } else {
+            // Connection was created but not opened.
+            // Don't decrement any connection counts.
+        }
+        connectProgressMap.erase(eRef);
+
+    } else {
+        // connection not found in progress map
+        QPID_LOG(notice, "ACL ConnectionCounter info for '" << 
connection.getMgmtId()
+            << "' not found in connection state pool");
+    }
+}
+
+
+//
+// getClientIp - given a connection's mgmtId return the client host part.
+//
+// TODO: Ideally this would be a method of the connection itself.
+//
+std::string ConnectionCounter::getClientHost(const std::string mgmtId)
+{
+    size_t hyphen = mgmtId.find('-');
+    if (std::string::npos != hyphen) {
+        size_t colon = mgmtId.find_last_of(':');
+        if (std::string::npos != colon) {
+            // trailing colon found
+            return mgmtId.substr(hyphen+1, colon - hyphen - 1);
+        } else {
+            // colon not found - use everything after hyphen
+            return mgmtId.substr(hyphen+1);
+        }
+    }
+
+    // no hyphen found - use whole string
+    assert(false);
+    return mgmtId;
+}
+
+}} // namespace qpid::ha

Added: qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.h?rev=1331549&view=auto
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.h (added)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclConnectionCounter.h Fri Apr 27 19:26:18 
2012
@@ -0,0 +1,79 @@
+#ifndef QPID_ACL_CONNECTIONCOUNTER_H
+#define QPID_ACL_CONNECTIONCOUNTER_H
+
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/ConnectionObserver.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/iterator/iterator_concepts.hpp>
+
+#include <map>
+
+namespace qpid {
+
+namespace broker {
+class Connection;
+}
+
+namespace acl {
+
+ /**
+ * Terminate client connections when a user tries to create 'too many'.
+ * Terminate hostIp connections when an IP host tries to create 'too many'.
+ */
+class ConnectionCounter : public broker::ConnectionObserver
+{
+private:
+    typedef std::map<std::string, uint32_t> connectCountsMap_t;
+    enum CONNECTION_PROGRESS { C_CREATED=1, C_OPENED=2 };
+
+    uint32_t         nameLimit;
+    uint32_t         hostLimit;
+    qpid::sys::Mutex dataLock;
+
+    connectCountsMap_t connectProgressMap;
+    connectCountsMap_t connectByNameMap;
+    connectCountsMap_t connectByHostMap;
+
+    std::string getClientHost(const std::string mgmtId);
+
+    bool limitCheckLH(connectCountsMap_t& theMap,
+                      const std::string& theName,
+                      uint32_t theLimit);
+
+    void releaseLH(connectCountsMap_t& theMap,
+                   const std::string& theName,
+                   uint32_t theLimit);
+
+public:
+    ConnectionCounter(uint32_t nl, uint32_t hl);
+    ~ConnectionCounter();
+
+    void connection(broker::Connection& connection);
+    void     opened(broker::Connection& connection);
+    void     closed(broker::Connection& connection);
+
+};
+
+}} // namespace qpid::ha
+
+#endif  /*!QPID_ACL_CONNECTIONCOUNTER_H*/

Modified: qpid/trunk/qpid/cpp/src/qpid/acl/AclPlugin.cpp
URL: 
http://svn.apache.org/viewvc/qpid/trunk/qpid/cpp/src/qpid/acl/AclPlugin.cpp?rev=1331549&r1=1331548&r2=1331549&view=diff
==============================================================================
--- qpid/trunk/qpid/cpp/src/qpid/acl/AclPlugin.cpp (original)
+++ qpid/trunk/qpid/cpp/src/qpid/acl/AclPlugin.cpp Fri Apr 27 19:26:18 2012
@@ -40,7 +40,9 @@ struct AclOptions : public Options {
 
     AclOptions(AclValues& v) : Options("ACL Options"), values(v) {
         addOptions()
-            ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to 
load from, loaded from data dir");
+            ("acl-file",           optValue(values.aclFile, "FILE"), "The 
policy file to load from, loaded from data dir")
+            ("acl-max-connect-per-user", optValue(values.aclMaxConnectPerUser, 
"N"), "The maximum number of connections allowed per user")
+            ("acl-max-connect-per-ip"  , optValue(values.aclMaxConnectPerIp, 
"N"),   "The maximum number of connections allowed per host IP address");
     }
 };
 



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to