summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/qpid/acl
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/qpid/acl')
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.cpp450
-rw-r--r--qpid/cpp/src/qpid/acl/Acl.h134
-rw-r--r--qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp320
-rw-r--r--qpid/cpp/src/qpid/acl/AclConnectionCounter.h106
-rw-r--r--qpid/cpp/src/qpid/acl/AclData.cpp890
-rw-r--r--qpid/cpp/src/qpid/acl/AclData.h323
-rw-r--r--qpid/cpp/src/qpid/acl/AclLexer.cpp141
-rw-r--r--qpid/cpp/src/qpid/acl/AclLexer.h200
-rw-r--r--qpid/cpp/src/qpid/acl/AclPlugin.cpp98
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.cpp827
-rw-r--r--qpid/cpp/src/qpid/acl/AclReader.h150
-rw-r--r--qpid/cpp/src/qpid/acl/AclResourceCounter.cpp174
-rw-r--r--qpid/cpp/src/qpid/acl/AclResourceCounter.h78
-rw-r--r--qpid/cpp/src/qpid/acl/AclTopicMatch.h89
-rw-r--r--qpid/cpp/src/qpid/acl/AclValidator.cpp536
-rw-r--r--qpid/cpp/src/qpid/acl/AclValidator.h106
-rw-r--r--qpid/cpp/src/qpid/acl/management-schema.xml85
17 files changed, 4707 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/acl/Acl.cpp b/qpid/cpp/src/qpid/acl/Acl.cpp
new file mode 100644
index 0000000000..bd9482ef41
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/Acl.cpp
@@ -0,0 +1,450 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/Acl.h"
+#include "qpid/acl/AclConnectionCounter.h"
+#include "qpid/acl/AclResourceCounter.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/acl/AclValidator.h"
+#include "qpid/sys/Mutex.h"
+
+#include "qpid/broker/Broker.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/log/Logger.h"
+#include "qpid/types/Variant.h"
+#include "qmf/org/apache/qpid/acl/ArgsAclLookup.h"
+#include "qmf/org/apache/qpid/acl/ArgsAclLookupPublish.h"
+#include "qmf/org/apache/qpid/acl/Package.h"
+#include "qmf/org/apache/qpid/acl/EventAllow.h"
+#include "qmf/org/apache/qpid/acl/EventConnectionDeny.h"
+#include "qmf/org/apache/qpid/acl/EventQueueQuotaDeny.h"
+#include "qmf/org/apache/qpid/acl/EventDeny.h"
+#include "qmf/org/apache/qpid/acl/EventFileLoaded.h"
+#include "qmf/org/apache/qpid/acl/EventFileLoadFailed.h"
+
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+
+using namespace std;
+using namespace qpid::acl;
+using qpid::broker::Broker;
+using namespace qpid::sys;
+using qpid::management::ManagementAgent;
+using qpid::management::ManagementObject;
+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),
+ connectionCounter(new ConnectionCounter(*this, aclValues.aclMaxConnectPerUser, aclValues.aclMaxConnectPerIp, aclValues.aclMaxConnectTotal)),
+ resourceCounter(new ResourceCounter(*this, aclValues.aclMaxQueuesPerUser)),userRules(false)
+{
+
+ if (aclValues.aclMaxConnectPerUser > AclData::getConnectMaxSpec())
+ throw Exception("--connection-limit-per-user switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxConnectPerIp > AclData::getConnectMaxSpec())
+ throw Exception("--connection-limit-per-ip switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxConnectTotal > AclData::getConnectMaxSpec())
+ throw Exception("--max-connections switch cannot be larger than " + AclData::getMaxConnectSpecStr());
+ if (aclValues.aclMaxQueuesPerUser > AclData::getConnectMaxSpec())
+ throw Exception("--max-queues-per-user switch cannot be larger than " + AclData::getMaxQueueSpecStr());
+
+ agent = broker->getManagementAgent();
+
+ if (agent != 0) {
+ _qmf::Package packageInit(agent);
+ mgmtObject = _qmf::Acl::shared_ptr(new _qmf::Acl (agent, this, broker));
+ agent->addObject (mgmtObject);
+ mgmtObject->set_maxConnections(aclValues.aclMaxConnectTotal);
+ mgmtObject->set_maxConnectionsPerIp(aclValues.aclMaxConnectPerIp);
+ mgmtObject->set_maxConnectionsPerUser(aclValues.aclMaxConnectPerUser);
+ mgmtObject->set_maxQueuesPerUser(aclValues.aclMaxQueuesPerUser);
+ }
+
+ if (!aclValues.aclFile.empty()) {
+ std::string errorString;
+ if (!readAclFile(errorString)){
+ if (mgmtObject!=0) mgmtObject->set_enforcingAcl(0);
+ throw Exception("Could not read ACL file " + errorString);
+ }
+ } else {
+ loadEmptyAclRuleset();
+ QPID_LOG(debug, "ACL loaded empty rule set");
+ }
+ broker->getConnectionObservers().add(connectionCounter);
+ QPID_LOG(info, "ACL Plugin loaded");
+ if (mgmtObject!=0) mgmtObject->set_enforcingAcl(1);
+}
+
+
+void Acl::reportConnectLimit(const std::string user, const std::string addr)
+{
+ if (mgmtObject!=0)
+ mgmtObject->inc_connectionDenyCount();
+
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventConnectionDeny(user, addr));
+ }
+}
+
+
+void Acl::reportQueueLimit(const std::string user, const std::string queueName)
+{
+ if (mgmtObject!=0)
+ mgmtObject->inc_queueQuotaDenyCount();
+
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventQueueQuotaDeny(user, queueName));
+ }
+}
+
+
+bool Acl::authorise(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name,
+ std::map<Property, std::string>* params)
+{
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ // add real ACL check here...
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,name,params);
+
+
+ return result(aclreslt, id, action, objType, name);
+}
+
+bool Acl::authorise(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& ExchangeName,
+ const std::string& RoutingKey)
+{
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ // only use dataLocal here...
+ AclResult aclreslt = dataLocal->lookup(id,action,objType,ExchangeName,RoutingKey);
+
+ return result(aclreslt, id, action, objType, ExchangeName);
+}
+
+
+bool Acl::approveConnection(const qpid::broker::Connection& conn)
+{
+ const std::string& userName(conn.getUserId());
+ uint16_t connectionLimit(0);
+
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ (void) dataLocal->getConnQuotaForUser(userName, &connectionLimit);
+
+ return connectionCounter->approveConnection(
+ conn,
+ userName,
+ dataLocal->enforcingConnectionQuotas(),
+ connectionLimit,
+ dataLocal);
+}
+
+bool Acl::approveCreateQueue(const std::string& userId, const std::string& queueName)
+{
+// return resourceCounter->approveCreateQueue(userId, queueName);
+ uint16_t queueLimit(0);
+
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+
+ (void) dataLocal->getQueueQuotaForUser(userId, &queueLimit);
+
+ return resourceCounter->approveCreateQueue(userId, queueName, dataLocal->enforcingQueueQuotas(), queueLimit);
+}
+
+
+void Acl::recordDestroyQueue(const std::string& queueName)
+{
+ resourceCounter->recordDestroyQueue(queueName);
+}
+
+
+bool Acl::result(
+ const AclResult& aclreslt,
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name)
+{
+ bool result(false);
+
+ switch (aclreslt)
+ {
+ case ALLOWLOG:
+ QPID_LOG(info, "ACL Allow id:" << id
+ << " action:" << AclHelper::getActionStr(action)
+ << " ObjectType:" << AclHelper::getObjectTypeStr(objType)
+ << " Name:" << name );
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventAllow(id, AclHelper::getActionStr(action),
+ AclHelper::getObjectTypeStr(objType),
+ name, types::Variant::Map()));
+ }
+ // FALLTHROUGH
+ case ALLOW:
+ result = true;
+ break;
+
+ case DENYLOG:
+ QPID_LOG(info, "ACL Deny id:" << id
+ << " action:" << AclHelper::getActionStr(action)
+ << " ObjectType:" << AclHelper::getObjectTypeStr(objType)
+ << " Name:" << name);
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventDeny(id, AclHelper::getActionStr(action),
+ AclHelper::getObjectTypeStr(objType),
+ name, types::Variant::Map()));
+ }
+ // FALLTHROUGH
+ case DENY:
+ if (mgmtObject!=0)
+ mgmtObject->inc_aclDenyCount();
+ result = false;
+ break;
+
+ default:
+ assert (false);
+ }
+
+ return result;
+}
+
+bool Acl::readAclFile(std::string& errorText)
+{
+ // only set transferAcl = true if a rule implies the use of ACL on transfer, else keep false for performance reasons.
+ return readAclFile(aclValues.aclFile, errorText);
+}
+
+bool Acl::readAclFile(std::string& aclFile, std::string& errorText) {
+ boost::shared_ptr<AclData> d(new AclData);
+ AclReader ar(aclValues.aclMaxConnectPerUser, aclValues.aclMaxQueuesPerUser);
+ if (ar.read(aclFile, d)){
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventFileLoadFailed("", ar.getError()));
+ }
+ errorText = ar.getError();
+ QPID_LOG(error,ar.getError());
+ return false;
+ }
+
+ AclValidator validator;
+ validator.validate(d);
+
+ {
+ Mutex::ScopedLock locker(dataLock);
+ data = d;
+ }
+ transferAcl = data->transferAcl; // any transfer ACL
+ userRules = true; // rules in force came from an ACL file
+
+ if (data->transferAcl){
+ QPID_LOG(debug,"ACL: Transfer ACL is Enabled!");
+ }
+
+ if (data->enforcingConnectionQuotas()){
+ QPID_LOG(debug, "ACL: Connection quotas are Enabled.");
+ }
+
+ if (data->enforcingQueueQuotas()){
+ QPID_LOG(debug, "ACL: Queue quotas are Enabled.");
+ }
+
+ QPID_LOG(debug, "ACL: Default connection mode : "
+ << AclHelper::getAclResultStr(d->connectionMode()));
+
+ data->aclSource = aclFile;
+ if (mgmtObject!=0){
+ mgmtObject->set_transferAcl(transferAcl?1:0);
+ mgmtObject->set_policyFile(aclFile);
+ mgmtObject->set_lastAclLoad(Duration::FromEpoch());
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventFileLoaded(""));
+ }
+ }
+ return true;
+}
+
+//
+// loadEmptyAclRuleset()
+//
+// No ACL file is specified but ACL should run.
+// Create a ruleset as if only "ACL ALLOW ALL ALL" was in a file
+//
+void Acl::loadEmptyAclRuleset() {
+ boost::shared_ptr<AclData> d(new AclData);
+ d->decisionMode = ALLOW;
+ d->aclSource = "";
+ d->connectionDecisionMode = ALLOW;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ data = d;
+ }
+ if (mgmtObject!=0){
+ mgmtObject->set_transferAcl(transferAcl?1:0);
+ mgmtObject->set_policyFile("");
+ mgmtObject->set_lastAclLoad(Duration::FromEpoch());
+ if (agent != 0) {
+ agent->raiseEvent(_qmf::EventFileLoaded(""));
+ }
+ }
+}
+
+//
+// management lookup function performs general query on acl engine
+//
+Manageable::status_t Acl::lookup(qpid::management::Args& args, std::string& text)
+{
+ _qmf::ArgsAclLookup& ioArgs = (_qmf::ArgsAclLookup&) args;
+ Manageable::status_t result(STATUS_USER);
+
+ try {
+ ObjectType objType = AclHelper::getObjectType(ioArgs.i_object);
+ Action action = AclHelper::getAction( ioArgs.i_action);
+ std::map<Property, std::string> propertyMap;
+ for (::qpid::types::Variant::Map::const_iterator
+ iMapIter = ioArgs.i_propertyMap.begin();
+ iMapIter != ioArgs.i_propertyMap.end();
+ iMapIter++)
+ {
+ Property property = AclHelper::getProperty(iMapIter->first);
+ propertyMap.insert(make_pair(property, iMapIter->second));
+ }
+
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+ AclResult aclResult;
+ // CREATE CONNECTION does not use lookup()
+ if (action == ACT_CREATE && objType == OBJ_CONNECTION) {
+ std::string host = propertyMap[acl::PROP_HOST];
+ std::string logString;
+ aclResult = dataLocal->isAllowedConnection(
+ ioArgs.i_userId,
+ host,
+ logString);
+ } else {
+ aclResult = dataLocal->lookup(
+ ioArgs.i_userId,
+ action,
+ objType,
+ ioArgs.i_objectName,
+ &propertyMap);
+ }
+ ioArgs.o_result = AclHelper::getAclResultStr(aclResult);
+ result = STATUS_OK;
+
+ } catch (const std::exception& e) {
+ std::ostringstream oss;
+ oss << "AclLookup invalid name : " << e.what();
+ ioArgs.o_result = oss.str();
+ text = oss.str();
+ }
+
+ return result;
+}
+
+
+//
+// management lookupPublish function performs fastpath
+// PUBLISH EXCHANGE query on acl engine
+//
+Manageable::status_t Acl::lookupPublish(qpid::management::Args& args, std::string& /*text*/)
+{
+ _qmf::ArgsAclLookupPublish& ioArgs = (_qmf::ArgsAclLookupPublish&) args;
+ boost::shared_ptr<AclData> dataLocal;
+ {
+ Mutex::ScopedLock locker(dataLock);
+ dataLocal = data; //rcu copy
+ }
+ AclResult aclResult = dataLocal->lookup(
+ ioArgs.i_userId,
+ ACT_PUBLISH,
+ OBJ_EXCHANGE,
+ ioArgs.i_exchangeName,
+ ioArgs.i_routingKey);
+
+ ioArgs.o_result = AclHelper::getAclResultStr(aclResult);
+
+ return STATUS_OK;
+}
+
+
+Acl::~Acl(){
+ broker->getConnectionObservers().remove(connectionCounter);
+}
+
+ManagementObject::shared_ptr Acl::GetManagementObject(void) const
+{
+ return mgmtObject;
+}
+
+Manageable::status_t Acl::ManagementMethod (uint32_t methodId, Args& args, string& text)
+{
+ Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD;
+ QPID_LOG (debug, "ACL: Queue::ManagementMethod [id=" << methodId << "]");
+
+ switch (methodId)
+ {
+ case _qmf::Acl::METHOD_RELOADACLFILE :
+ readAclFile(text);
+ if (text.empty())
+ status = Manageable::STATUS_OK;
+ else
+ status = Manageable::STATUS_USER;
+ break;
+
+ case _qmf::Acl::METHOD_LOOKUP :
+ status = lookup(args, text);
+ break;
+
+ case _qmf::Acl::METHOD_LOOKUPPUBLISH :
+ status = lookupPublish(args, text);
+ break;
+ }
+
+ return status;
+}
diff --git a/qpid/cpp/src/qpid/acl/Acl.h b/qpid/cpp/src/qpid/acl/Acl.h
new file mode 100644
index 0000000000..df2fb66c82
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/Acl.h
@@ -0,0 +1,134 @@
+#ifndef QPID_ACL_ACL_H
+#define QPID_ACL_ACL_H
+
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+
+
+#include "qpid/acl/AclReader.h"
+#include "qpid/AclHost.h"
+#include "qpid/RefCounted.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/management/Manageable.h"
+#include "qpid/management/ManagementAgent.h"
+#include "qmf/org/apache/qpid/acl/Acl.h"
+#include "qpid/sys/Mutex.h"
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <string>
+
+
+namespace qpid {
+namespace broker {
+class Broker;
+class Connection;
+}
+
+namespace acl {
+class ConnectionCounter;
+class ResourceCounter;
+
+struct AclValues {
+ std::string aclFile;
+ uint16_t aclMaxConnectPerUser;
+ uint16_t aclMaxConnectPerIp;
+ uint16_t aclMaxConnectTotal;
+ uint16_t aclMaxQueuesPerUser;
+};
+
+
+class Acl : public broker::AclModule, public RefCounted, public management::Manageable
+{
+
+private:
+ acl::AclValues aclValues;
+ broker::Broker* broker;
+ bool transferAcl;
+ boost::shared_ptr<AclData> data;
+ qmf::org::apache::qpid::acl::Acl::shared_ptr mgmtObject;
+ qpid::management::ManagementAgent* agent;
+ mutable qpid::sys::Mutex dataLock;
+ boost::shared_ptr<ConnectionCounter> connectionCounter;
+ boost::shared_ptr<ResourceCounter> resourceCounter;
+ bool userRules;
+
+public:
+ Acl (AclValues& av, broker::Broker& b);
+
+ /** reportConnectLimit
+ * issue management counts and alerts for denied connections
+ */
+ void reportConnectLimit(const std::string user, const std::string addr);
+ void reportQueueLimit(const std::string user, const std::string queueName);
+
+ inline virtual bool doTransferAcl() {
+ return transferAcl;
+ };
+
+ inline virtual uint16_t getMaxConnectTotal() {
+ return aclValues.aclMaxConnectTotal;
+ };
+
+ inline virtual bool userAclRules() {
+ return userRules;
+ };
+
+// 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);
+
+ // Resource quota tracking
+ virtual bool approveConnection(const broker::Connection& connection);
+ virtual bool approveCreateQueue(const std::string& userId, const std::string& queueName);
+ virtual void recordDestroyQueue(const std::string& queueName);
+
+ 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);
+ void loadEmptyAclRuleset();
+ Manageable::status_t lookup (management::Args& args, std::string& text);
+ Manageable::status_t lookupPublish(management::Args& args, std::string& text);
+ virtual qpid::management::ManagementObject::shared_ptr 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
diff --git a/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp b/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
new file mode 100644
index 0000000000..ca3da50088
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclConnectionCounter.cpp
@@ -0,0 +1,320 @@
+/*
+ *
+ * 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 "Acl.h"
+#include "qpid/broker/Connection.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/Mutex.h"
+#include "qpid/sys/SocketAddress.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, per client IP address
+// and per total connection count.
+//
+
+
+//
+//
+//
+ConnectionCounter::ConnectionCounter(Acl& a, uint16_t nl, uint16_t hl, uint16_t tl) :
+ acl(a), nameLimit(nl), hostLimit(hl), totalLimit(tl), totalCurrentConnections(0) {}
+
+ConnectionCounter::~ConnectionCounter() {}
+
+
+//
+// limitApproveLH
+//
+// Connection creation approver. Return true only if user is under limit.
+// Called with lock held.
+//
+bool ConnectionCounter::limitApproveLH(
+ connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog) {
+
+ bool result(true);
+ if (theLimit > 0) {
+ uint16_t count;
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second;
+ result = count <= theLimit;
+ } else {
+ // Not found
+ count = 0;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover IP=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ }
+ return result;
+}
+
+
+//
+// countConnectionLH
+//
+// Increment the name's count in map and return an optional comparison
+// against a connection limit.
+// Called with dataLock already taken.
+//
+bool ConnectionCounter::countConnectionLH(
+ connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog,
+ bool enforceLimit) {
+
+ bool result(true);
+ uint16_t count(0);
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second + 1;
+ (*eRef).second = count;
+ } else {
+ theMap[theName] = count = 1;
+ }
+ if (enforceLimit) {
+ result = count <= theLimit;
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL ConnectionApprover user=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ 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) {
+
+ connectCountsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ uint16_t count = (uint16_t) (*eRef).second;
+ assert (count > 0);
+ if (1 == count) {
+ theMap.erase (eRef);
+ } else {
+ (*eRef).second = count - 1;
+ }
+ } else {
+ // User had no connections.
+ // Connections denied by ACL never get users added
+ //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 new connection: " << connection.getMgmtId());
+
+ const std::string& hostName(getClientHost(connection.getMgmtId()));
+
+ Mutex::ScopedLock locker(dataLock);
+
+ // Total connections goes up
+ totalCurrentConnections += 1;
+
+ // Record the fact that this connection exists
+ connectProgressMap[connection.getMgmtId()] = C_CREATED;
+
+ // Count the connection from this host.
+ (void) countConnectionLH(connectByHostMap, hostName, hostLimit, false, false);
+}
+
+
+//
+// closed - called during Connection's destructor
+//
+void ConnectionCounter::closed(broker::Connection& connection) {
+ QPID_LOG(trace, "ACL ConnectionCounter closed: " << connection.getMgmtId()
+ << ", userId:" << connection.getUserId());
+
+ 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 user in-use counts
+ releaseLH(connectByNameMap,
+ connection.getUserId());
+ } else {
+ // Connection was created but not opened.
+ // Don't decrement user count.
+ }
+
+ // Decrement host in-use count.
+ releaseLH(connectByHostMap,
+ getClientHost(connection.getMgmtId()));
+
+ // destroy connection progress indicator
+ connectProgressMap.erase(eRef);
+
+ } else {
+ // connection not found in progress map
+ QPID_LOG(notice, "ACL ConnectionCounter closed info for '" << connection.getMgmtId()
+ << "' not found in connection state pool");
+ }
+
+ // total connections
+ totalCurrentConnections -= 1;
+}
+
+
+//
+// approveConnection
+// check total connections, connections from IP, connections by user and
+// disallow if over any limit
+//
+bool ConnectionCounter::approveConnection(
+ const broker::Connection& connection,
+ const std::string& userName,
+ bool enforcingConnectionQuotas,
+ uint16_t connectionUserQuota,
+ boost::shared_ptr<AclData> localdata)
+{
+ const std::string& hostName(getClientHost(connection.getMgmtId()));
+
+ Mutex::ScopedLock locker(dataLock);
+
+ // Bump state from CREATED to OPENED
+ (void) countConnectionLH(connectProgressMap, connection.getMgmtId(),
+ C_OPENED, false, false);
+
+ // Run global black/white list check
+ sys::SocketAddress sa(hostName, "");
+ bool okByHostList(true);
+ std::string hostLimitText;
+ if (sa.isIp()) {
+ AclResult result = localdata->isAllowedConnection(userName, hostName, hostLimitText);
+ okByHostList = AclHelper::resultAllows(result);
+ if (okByHostList) {
+ QPID_LOG(trace, "ACL: ConnectionApprover host list " << hostLimitText);
+ }
+ }
+
+ // Approve total connections
+ bool okTotal = true;
+ if (totalLimit > 0) {
+ okTotal = totalCurrentConnections <= totalLimit;
+ QPID_LOG(trace, "ACL ConnectionApprover totalLimit=" << totalLimit
+ << " curValue=" << totalCurrentConnections
+ << " result=" << (okTotal ? "allow" : "deny"));
+ }
+
+ // Approve by IP host connections
+ bool okByIP = limitApproveLH(connectByHostMap, hostName, hostLimit, true);
+
+ // Count and Approve the connection by the user
+ bool okByUser = countConnectionLH(connectByNameMap, userName,
+ connectionUserQuota, true,
+ enforcingConnectionQuotas);
+
+ // Emit separate log for each disapproval
+ if (!okByHostList) {
+ QPID_LOG(error, "ACL: ConnectionApprover host list " << hostLimitText
+ << " Connection refused.");
+ }
+ if (!okTotal) {
+ QPID_LOG(error, "Client max total connection count limit of " << totalLimit
+ << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused");
+ }
+ if (!okByIP) {
+ QPID_LOG(error, "Client max per-host connection count limit of "
+ << hostLimit << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
+ }
+ if (!okByUser) {
+ QPID_LOG(error, "Client max per-user connection count limit of "
+ << connectionUserQuota << " exceeded by '"
+ << connection.getMgmtId() << "', user: '"
+ << userName << "'. Connection refused.");
+ }
+
+ // Count/Event once for each disapproval
+ bool result = okByHostList && okTotal && okByIP && okByUser;
+ if (!result) {
+ acl.reportConnectLimit(userName, hostName);
+ }
+
+ return result;
+}
+
+//
+// getClientIp - given a connection's mgmtId return the client host part.
+//
+// TODO: Ideally this would be a method of the connection itself.
+// TODO: Verify it works with rdma connection names.
+//
+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
+ std::string tmp = mgmtId.substr(hyphen+1, colon - hyphen - 1);
+ // undecorate ipv6
+ if (tmp.length() >= 3 && tmp.find("[") == 0 && tmp.rfind("]") == tmp.length()-1)
+ tmp = tmp.substr(1, tmp.length()-2);
+ return tmp;
+
+ } else {
+ // colon not found - use everything after hyphen
+ return mgmtId.substr(hyphen+1);
+ }
+ }
+
+ // no hyphen found - use whole string
+ return mgmtId;
+}
+
+}} // namespace qpid::ha
diff --git a/qpid/cpp/src/qpid/acl/AclConnectionCounter.h b/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
new file mode 100644
index 0000000000..3683b573ff
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclConnectionCounter.h
@@ -0,0 +1,106 @@
+#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 "qpid/acl/AclData.h"
+
+#include <map>
+
+namespace qpid {
+
+namespace broker {
+class Connection;
+}
+
+namespace acl {
+class 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 };
+
+ Acl& acl;
+ uint16_t nameLimit;
+ uint16_t hostLimit;
+ uint16_t totalLimit;
+ uint16_t totalCurrentConnections;
+ qpid::sys::Mutex dataLock;
+
+ /** Records per-connection state */
+ connectCountsMap_t connectProgressMap;
+
+ /** Records per-username counts */
+ connectCountsMap_t connectByNameMap;
+
+ /** Records per-host counts */
+ connectCountsMap_t connectByHostMap;
+
+ /** Given a connection's management ID, return the client host name */
+ std::string getClientHost(const std::string mgmtId);
+
+ /** Return approval for proposed connection */
+ bool limitApproveLH(connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog);
+
+ /** Record a connection.
+ * @return indication if user/host is over its limit */
+ bool countConnectionLH(connectCountsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog,
+ bool enforceLimit);
+
+ /** Release a connection */
+ void releaseLH(connectCountsMap_t& theMap,
+ const std::string& theName);
+
+public:
+ ConnectionCounter(Acl& acl, uint16_t nl, uint16_t hl, uint16_t tl);
+ ~ConnectionCounter();
+
+ // ConnectionObserver interface
+ void connection(broker::Connection& connection);
+ void closed(broker::Connection& connection);
+
+ // Connection counting
+ bool approveConnection(const broker::Connection& conn,
+ const std::string& userName,
+ bool enforcingConnectionQuotas,
+ uint16_t connectionLimit,
+ boost::shared_ptr<AclData> localdata
+ );
+};
+
+}} // namespace qpid::ha
+
+#endif /*!QPID_ACL_CONNECTIONCOUNTER_H*/
diff --git a/qpid/cpp/src/qpid/acl/AclData.cpp b/qpid/cpp/src/qpid/acl/AclData.cpp
new file mode 100644
index 0000000000..a629e44d60
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclData.cpp
@@ -0,0 +1,890 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclData.h"
+#include "qpid/acl/AclValidator.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+#include <sstream>
+#include <iomanip>
+
+namespace qpid {
+namespace acl {
+
+//
+// Instantiate the keyword strings
+//
+const std::string AclData::ACL_KEYWORD_USER_SUBST = "${user}";
+const std::string AclData::ACL_KEYWORD_DOMAIN_SUBST = "${domain}";
+const std::string AclData::ACL_KEYWORD_USERDOMAIN_SUBST = "${userdomain}";
+const std::string AclData::ACL_KEYWORD_ALL = "all";
+const std::string AclData::ACL_KEYWORD_ACL = "acl";
+const std::string AclData::ACL_KEYWORD_GROUP = "group";
+const std::string AclData::ACL_KEYWORD_QUOTA = "quota";
+const std::string AclData::ACL_KEYWORD_QUOTA_CONNECTIONS = "connections";
+const std::string AclData::ACL_KEYWORD_QUOTA_QUEUES = "queues";
+const char AclData::ACL_SYMBOL_WILDCARD = '*';
+const std::string AclData::ACL_KEYWORD_WILDCARD = "*";
+const char AclData::ACL_SYMBOL_LINE_CONTINUATION = '\\';
+const std::string AclData::ACL_KEYWORD_DEFAULT_EXCHANGE = "amq.default";
+
+//
+// constructor
+//
+AclData::AclData():
+ decisionMode(qpid::acl::DENY),
+ transferAcl(false),
+ aclSource("UNKNOWN"),
+ connectionDecisionMode(qpid::acl::ALLOW),
+ connQuotaRuleSettings(new quotaRuleSet),
+ queueQuotaRuleSettings(new quotaRuleSet),
+ connBWHostsGlobalRules(new bwHostRuleSet),
+ connBWHostsUserRules(new bwHostUserRuleMap)
+{
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) {
+ actionList[cnt]=0;
+ }
+}
+
+
+//
+// clear
+//
+void AclData::clear ()
+{
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++) {
+ if (actionList[cnt]) {
+ for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++) {
+ delete actionList[cnt][cnt1];
+ }
+ }
+ delete[] actionList[cnt];
+ }
+ transferAcl = false;
+ connectionDecisionMode = qpid::acl::ALLOW;
+ connQuotaRuleSettings->clear();
+ queueQuotaRuleSettings->clear();
+ connBWHostsGlobalRules->clear();
+ connBWHostsUserRules->clear();
+}
+
+void AclData::printDecisionRules(int userFieldWidth) {
+ AclValidator validator;
+ QPID_LOG(trace, "ACL: Decision rule cross reference");
+ for (int act=0; act<acl::ACTIONSIZE; act++) {
+ acl::Action action = acl::Action(act);
+ for (int obj=0; obj<acl::OBJECTSIZE; obj++) {
+ acl::ObjectType object = acl::ObjectType(obj);
+ if (actionList[act] != NULL && actionList[act][obj] != NULL) {
+ for (actObjItr aoitr = actionList[act][obj]->begin();
+ aoitr != actionList[act][obj]->end();
+ aoitr++) {
+ std::string user = (*aoitr).first;
+ ruleSetItr rsitr = (*aoitr).second.end();
+ for (size_t rCnt=0; rCnt < (*aoitr).second.size(); rCnt++) {
+ rsitr--;
+ std::vector<int> candidates;
+ validator.findPossibleLookupMatch(
+ action, object, rsitr->props, candidates);
+ std::stringstream ss;
+ std::string sep("");
+ for (std::vector<int>::const_iterator
+ itr = candidates.begin(); itr != candidates.end(); itr++) {
+ ss << sep << *itr;
+ sep = ",";
+ }
+ QPID_LOG(trace, "ACL: User: "
+ << std::setfill(' ') << std::setw(userFieldWidth +1) << std::left
+ << user << " "
+ << std::setfill(' ') << std::setw(acl::ACTION_STR_WIDTH +1) << std::left
+ << AclHelper::getActionStr(action)
+ << std::setfill(' ') << std::setw(acl::OBJECTTYPE_STR_WIDTH) << std::left
+ << AclHelper::getObjectTypeStr(object)
+ << " Rule: "
+ << rsitr->toString() << " may match Lookups : ("
+ << ss.str() << ")");
+ }
+ }
+ } else {
+ // no rules for action/object
+ }
+ }
+ }
+}
+
+//
+// matchProp
+//
+// Compare a rule's property name with a lookup name,
+// The rule's name may contain a trailing '*' to specify a wildcard match.
+//
+bool AclData::matchProp(const std::string& ruleStr,
+ const std::string& lookupStr)
+{
+ // allow wildcard on the end of rule strings...
+ if (ruleStr.data()[ruleStr.size()-1]==ACL_SYMBOL_WILDCARD) {
+ return ruleStr.compare(0,
+ ruleStr.size()-1,
+ lookupStr,
+ 0,
+ ruleStr.size()-1 ) == 0;
+ } else {
+ return ruleStr.compare(lookupStr) == 0;
+ }
+}
+
+
+//
+// lookupMatchRule
+//
+// Check a single rule and if it's a match return the decision
+//
+bool AclData::lookupMatchRule(
+ const ruleSetItr& rsItr,
+ const std::string& id,
+ const std::string& name,
+ const std::map<Property, std::string>* params,
+ AclResult& aclresult)
+{
+ QPID_LOG(debug, "ACL: checking rule " << rsItr->toString());
+
+ bool match = true;
+ bool limitChecked = true;
+
+ // Iterate this rule's properties. A 'match' is true when
+ // all of the rule's properties are found to be satisfied
+ // in the lookup param list. The lookup may specify things
+ // (they usually do) that are not in the rule properties but
+ // these things don't interfere with the rule match.
+
+ for (specPropertyMapItr rulePropMapItr = rsItr->props.begin();
+ (rulePropMapItr != rsItr->props.end()) && match;
+ rulePropMapItr++) {
+ // The rule property map's NAME property is given in
+ // the calling args and not in the param map.
+ if (rulePropMapItr->first == acl::SPECPROP_NAME)
+ {
+ // substitute user name into object name
+ bool result;
+ if (rsItr->ruleHasUserSub[PROP_NAME]) {
+ std::string sName(rulePropMapItr->second);
+ substituteUserId(sName, id);
+ result = matchProp(sName, name);
+ } else {
+ result = matchProp(rulePropMapItr->second, name);
+ }
+
+ if (result) {
+ QPID_LOG(debug, "ACL: lookup name '" << name
+ << "' matched with rule name '"
+ << rulePropMapItr->second << "'");
+ } else {
+ match = false;
+ QPID_LOG(debug, "ACL: lookup name '" << name
+ << "' didn't match with rule name '"
+ << rulePropMapItr->second << "'");
+ }
+ } else {
+ if (params) {
+ // The rule's property map non-NAME properties
+ // found in the lookup's params list.
+ // In some cases the param's index is not the same
+ // as rule's index.
+ propertyMapItr lookupParamItr;
+ switch (rulePropMapItr->first) {
+ case acl::SPECPROP_MAXPAGESLOWERLIMIT:
+ case acl::SPECPROP_MAXPAGESUPPERLIMIT:
+ lookupParamItr = params->find(PROP_MAXPAGES);
+ break;
+
+ case acl::SPECPROP_MAXPAGEFACTORLOWERLIMIT:
+ case acl::SPECPROP_MAXPAGEFACTORUPPERLIMIT:
+ lookupParamItr = params->find(PROP_MAXPAGEFACTOR);
+ break;
+
+ case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXQUEUECOUNT);
+ break;
+
+ case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXQUEUESIZE);
+ break;
+
+ case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXFILECOUNT);
+ break;
+
+ case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
+ lookupParamItr = params->find(PROP_MAXFILESIZE);
+ break;
+
+ default:
+ lookupParamItr = params->find((Property)rulePropMapItr->first);
+ break;
+ };
+
+ if (lookupParamItr == params->end()) {
+ // Now the rule has a specified property
+ // that does not exist in the caller's
+ // lookup params list.
+ // This rule does not match.
+ match = false;
+ QPID_LOG(debug, "ACL: lookup parameter map doesn't contain the rule property '"
+ << AclHelper::getPropertyStr(rulePropMapItr->first) << "'");
+ } else {
+ // Now account for the business of rules
+ // whose property indexes are mismatched.
+ switch (rulePropMapItr->first) {
+ case acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTUPPERLIMIT:
+ case acl::SPECPROP_MAXFILESIZEUPPERLIMIT:
+ case acl::SPECPROP_MAXPAGESUPPERLIMIT:
+ case acl::SPECPROP_MAXPAGEFACTORUPPERLIMIT:
+ limitChecked &=
+ compareInt(
+ rulePropMapItr->first,
+ boost::lexical_cast<std::string>(rulePropMapItr->second),
+ boost::lexical_cast<std::string>(lookupParamItr->second),
+ true);
+ break;
+
+ case acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT:
+ case acl::SPECPROP_MAXQUEUESIZELOWERLIMIT:
+ case acl::SPECPROP_MAXFILECOUNTLOWERLIMIT:
+ case acl::SPECPROP_MAXFILESIZELOWERLIMIT:
+ case acl::SPECPROP_MAXPAGESLOWERLIMIT:
+ case acl::SPECPROP_MAXPAGEFACTORLOWERLIMIT:
+ limitChecked &=
+ compareInt(
+ rulePropMapItr->first,
+ boost::lexical_cast<std::string>(rulePropMapItr->second),
+ boost::lexical_cast<std::string>(lookupParamItr->second),
+ false);
+ break;
+
+ default:
+ bool result;
+ if ((SPECPROP_ALTERNATE == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_ALTERNATE]) ||
+ (SPECPROP_QUEUENAME == rulePropMapItr->first && rsItr->ruleHasUserSub[PROP_QUEUENAME])) {
+ // These properties are allowed to have username substitution
+ std::string sName(rulePropMapItr->second);
+ substituteUserId(sName, id);
+ result = matchProp(sName, lookupParamItr->second);
+ } else if (SPECPROP_ROUTINGKEY == rulePropMapItr->first) {
+ // Routing key is allowed to have username substitution
+ // and it gets topic exchange matching
+ if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) {
+ std::string sKey(lookupParamItr->second);
+ substituteKeywords(sKey, id);
+ result = rsItr->matchRoutingKey(sKey);
+ } else {
+ result = rsItr->matchRoutingKey(lookupParamItr->second);
+ }
+ } else {
+ // Rules without substitution
+ result = matchProp(rulePropMapItr->second, lookupParamItr->second);
+ }
+
+ if (result) {
+ QPID_LOG(debug, "ACL: the pair("
+ << AclHelper::getPropertyStr(lookupParamItr->first)
+ << "," << lookupParamItr->second
+ << ") given in lookup matched the pair("
+ << AclHelper::getPropertyStr(rulePropMapItr->first) << ","
+ << rulePropMapItr->second
+ << ") given in the rule");
+ } else {
+ match = false;
+ QPID_LOG(debug, "ACL: the pair("
+ << AclHelper::getPropertyStr(lookupParamItr->first)
+ << "," << lookupParamItr->second
+ << ") given in lookup doesn't match the pair("
+ << AclHelper::getPropertyStr(rulePropMapItr->first)
+ << "," << rulePropMapItr->second
+ << ") given in the rule");
+ }
+ break;
+ };
+ }
+ } else {
+ // params don't exist.
+ }
+ }
+ }
+ if (match) {
+ aclresult = rsItr->ruleMode;
+ if (!limitChecked) {
+ // Now a lookup matched all rule properties but one
+ // of the numeric limit checks has failed.
+ // Demote allow rules to corresponding deny rules.
+ switch (aclresult) {
+ case acl::ALLOW:
+ aclresult = acl::DENY;
+ break;
+ case acl::ALLOWLOG:
+ aclresult = acl::DENYLOG;
+ break;
+ default:
+ break;
+ };
+ }
+ QPID_LOG(debug,"ACL: Successful match, the decision is:"
+ << AclHelper::getAclResultStr(aclresult));
+ } else {
+ // This rule did not match the requested lookup and
+ // does not contribute to an ACL decision.
+ }
+ return match;
+}
+
+//
+// lookup - general ACL lookup
+//
+// The ACL main business logic function of matching rules and declaring
+// an allow or deny result.
+//
+AclResult AclData::lookup(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name,
+ std::map<Property, std::string>* params)
+{
+ QPID_LOG(debug, "ACL: Lookup for id:" << id
+ << " action:" << AclHelper::getActionStr((Action) action)
+ << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType)
+ << " name:" << name
+ << " with params " << AclHelper::propertyMapToString(params));
+
+ // A typical log looks like:
+ // ACL: Lookup for id:bob@QPID action:create objectType:queue name:q2
+ // with params { durable=false passive=false autodelete=false
+ // exclusive=false alternate= policytype= maxqueuesize=0
+ // maxqueuecount=0 }
+
+ // Default result is blanket decision mode for the entire ACL list.
+ AclResult aclresult = decisionMode;
+
+ // Test for lists of rules at the intersection of the Action & Object
+ if (actionList[action] && actionList[action][objType]) {
+ // Find the list of rules for this actorId
+ AclData::actObjItr itrRule = actionList[action][objType]->find(id);
+
+ // If individual actorId not found then find a rule set for '*'.
+ if (itrRule == actionList[action][objType]->end()) {
+ itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
+ }
+ if (itrRule != actionList[action][objType]->end()) {
+ // A list of rules exists for this actor/action/object tuple.
+ // Iterate the rule set to search for a matching rule.
+ ruleSetItr rsItr = itrRule->second.end();
+ for (int cnt = itrRule->second.size(); cnt != 0; cnt--) {
+ rsItr--;
+ if (lookupMatchRule(rsItr, id, name, params, aclresult)) {
+ return aclresult;
+ }
+ }
+ } else {
+ // The Action-Object list has entries but not for this actorId
+ // nor for *.
+ }
+ } else {
+ // The Action-Object list has no entries.
+ }
+
+ QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode "
+ << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+}
+
+
+//
+// lookupMatchPublishExchangeRule
+//
+// check a single publish exchange rule
+//
+bool AclData::lookupMatchPublishExchangeRule(
+ const ruleSetItr& rsItr,
+ const std::string& id,
+ const std::string& name,
+ const std::string& routingKey,
+ AclResult& aclresult)
+{
+ QPID_LOG(debug, "ACL: checking rule " << rsItr->toString());
+
+ // Search on exchange name and routing key only if specfied in rule.
+ bool match =true;
+ if (rsItr->pubExchNameInRule) {
+ // substitute user name into object name
+ bool result;
+
+ if (rsItr->ruleHasUserSub[PROP_NAME]) {
+ std::string sName(rsItr->pubExchName);
+ substituteUserId(sName, id);
+ result = matchProp(sName, name);
+ }
+ else if (rsItr->pubExchNameMatchesBlank) {
+ result = name.empty();
+ } else {
+ result = matchProp(rsItr->pubExchName, name);
+ }
+
+ if (result) {
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
+ << name << "' matched with rule name '"
+ << rsItr->pubExchName << "'");
+ } else {
+ match= false;
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup exchange name '"
+ << name << "' did not match with rule name '"
+ << rsItr->pubExchName << "'");
+ }
+ }
+
+ if (match && rsItr->pubRoutingKeyInRule) {
+ if ((routingKey.find(ACL_KEYWORD_USER_SUBST, 0) != std::string::npos) ||
+ (routingKey.find(ACL_KEYWORD_DOMAIN_SUBST, 0) != std::string::npos) ||
+ (routingKey.find(ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos)) {
+ // The user is not allowed to present a routing key with the substitution key in it
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum <<
+ " User-specified routing key has substitution wildcard:" << routingKey
+ << ". Rule match prohibited.");
+ match = false;
+ } else {
+ bool result;
+ if (rsItr->ruleHasUserSub[PROP_ROUTINGKEY]) {
+ std::string sKey(routingKey);
+ substituteKeywords(sKey, id);
+ result = rsItr->matchRoutingKey(sKey);
+ } else {
+ result = rsItr->matchRoutingKey(routingKey);
+ }
+
+ if (result) {
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+ << routingKey << "' matched with rule routing key '"
+ << rsItr->pubRoutingKey << "'");
+ } else {
+ QPID_LOG(debug, "ACL: Rule: " << rsItr->rawRuleNum << " lookup key name '"
+ << routingKey << "' did not match with rule routing key '"
+ << rsItr->pubRoutingKey << "'");
+ match = false;
+ }
+ }
+ }
+
+ if (match) {
+ aclresult = rsItr->ruleMode;
+ QPID_LOG(debug,"ACL: Rule: " << rsItr->rawRuleNum << " Successful match, the decision is:"
+ << AclHelper::getAclResultStr(aclresult));
+ }
+ return match;
+}
+
+//
+// lookup - special PUBLISH EXCHANGE lookup
+//
+// The ACL main business logic function of matching rules and declaring
+// an allow or deny result. This lookup is the fastpath per-message
+// lookup to verify if a user is allowed to publish to an exchange with
+// a given key.
+//
+AclResult AclData::lookup(
+ const std::string& id,
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& /*Exchange*/ name,
+ const std::string& routingKey)
+{
+
+ QPID_LOG(debug, "ACL: Lookup for id:" << id
+ << " action:" << AclHelper::getActionStr((Action) action)
+ << " objectType:" << AclHelper::getObjectTypeStr((ObjectType) objType)
+ << " exchange name:" << name
+ << " with routing key " << routingKey);
+
+ AclResult aclresult = decisionMode;
+
+ if (actionList[action] && actionList[action][objType]){
+ AclData::actObjItr itrRule = actionList[action][objType]->find(id);
+
+ if (itrRule == actionList[action][objType]->end()) {
+ itrRule = actionList[action][objType]->find(ACL_KEYWORD_WILDCARD);
+ }
+ if (itrRule != actionList[action][objType]->end() ) {
+ // Found a rule list for this user-action-object set.
+ // Search the rule list for a matching rule.
+ ruleSetItr rsItr = itrRule->second.end();
+ for (int cnt = itrRule->second.size(); cnt != 0; cnt--) {
+ rsItr--;
+
+ if (lookupMatchPublishExchangeRule(rsItr, id, name, routingKey, aclresult)) {
+ return aclresult;
+ }
+ }
+ }
+ }
+ QPID_LOG(debug,"ACL: No successful match, defaulting to the decision mode "
+ << AclHelper::getAclResultStr(aclresult));
+ return aclresult;
+
+}
+
+
+
+//
+//
+//
+void AclData::setConnQuotaRuleSettings (
+ boost::shared_ptr<quotaRuleSet> quotaPtr)
+{
+ connQuotaRuleSettings = quotaPtr;
+}
+
+
+//
+// getConnQuotaForUser
+//
+// Return the true or false value of connQuotaRulesExist,
+// indicating whether any kind of lookup was done or not.
+//
+// When lookups are performed return the result value of
+// 1. The user's setting else
+// 2. The 'all' user setting else
+// 3. Zero
+// When lookups are not performed then return a result value of Zero.
+//
+bool AclData::getConnQuotaForUser(const std::string& theUserName,
+ uint16_t* theResult) const {
+ if (this->enforcingConnectionQuotas()) {
+ // look for this user explicitly
+ quotaRuleSetItr nameItr = (*connQuotaRuleSettings).find(theUserName);
+ if (nameItr != (*connQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " explicitly set to : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Look for the 'all' user
+ nameItr = (*connQuotaRuleSettings).find(ACL_KEYWORD_ALL);
+ if (nameItr != (*connQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " chosen through value for 'all' : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Neither userName nor "all" found.
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " absent in quota settings. Return value : 0");
+ *theResult = 0;
+ }
+ }
+ } else {
+ // Rules do not exist
+ QPID_LOG(trace, "ACL: Connection quota for user " << theUserName
+ << " unavailable; quota settings are not specified. Return value : 0");
+ *theResult = 0;
+ }
+ return this->enforcingConnectionQuotas();
+}
+
+//
+//
+//
+void AclData::setQueueQuotaRuleSettings (
+ boost::shared_ptr<quotaRuleSet> quotaPtr)
+{
+ queueQuotaRuleSettings = quotaPtr;
+}
+
+
+//
+// getQueueQuotaForUser
+//
+// Return the true or false value of queueQuotaRulesExist,
+// indicating whether any kind of lookup was done or not.
+//
+// When lookups are performed return the result value of
+// 1. The user's setting else
+// 2. The 'all' user setting else
+// 3. Zero
+// When lookups are not performed then return a result value of Zero.
+//
+bool AclData::getQueueQuotaForUser(const std::string& theUserName,
+ uint16_t* theResult) const {
+ if (this->enforcingQueueQuotas()) {
+ // look for this user explicitly
+ quotaRuleSetItr nameItr = (*queueQuotaRuleSettings).find(theUserName);
+ if (nameItr != (*queueQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
+ << " explicitly set to : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Look for the 'all' user
+ nameItr = (*queueQuotaRuleSettings).find(ACL_KEYWORD_ALL);
+ if (nameItr != (*queueQuotaRuleSettings).end()) {
+ QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
+ << " chosen through value for 'all' : " << (*nameItr).second);
+ *theResult = (*nameItr).second;
+ } else {
+ // Neither userName nor "all" found.
+ QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
+ << " absent in quota settings. Return value : 0");
+ *theResult = 0;
+ }
+ }
+ } else {
+ // Rules do not exist
+ QPID_LOG(trace, "ACL: Queue quota for user " << theUserName
+ << " unavailable; quota settings are not specified. Return value : 0");
+ *theResult = 0;
+ }
+ return this->enforcingQueueQuotas();
+}
+
+void AclData::setConnGlobalRules (boost::shared_ptr<bwHostRuleSet> cgr) {
+ connBWHostsGlobalRules = cgr;
+}
+
+void AclData::setConnUserRules (boost::shared_ptr<bwHostUserRuleMap> hurm) {
+ connBWHostsUserRules = hurm;
+}
+
+AclResult AclData::isAllowedConnection(const std::string& userName,
+ const std::string& hostName,
+ std::string& logText) {
+ bool decisionMade(false);
+ AclResult result(ALLOW);
+ for (bwHostRuleSetItr it=connBWHostsGlobalRules->begin();
+ it!=connBWHostsGlobalRules->end(); it++) {
+ if (it->getAclHost().match(hostName)) {
+ // This host matches a global spec and controls the
+ // allow/deny decision for this connection.
+ result = it->getAclResult();
+ logText = QPID_MSG("global rule " << it->toString()
+ << (AclHelper::resultAllows(result) ? " allows" : " denies")
+ << " connection for host " << hostName << ", user "
+ << userName);
+ decisionMade = true;
+ break;
+ } else {
+ // This rule in the global spec doesn't match and
+ // does not control the allow/deny decision.
+ }
+ }
+
+ // Run user black/white list check
+ if (!decisionMade) {
+ bwHostUserRuleMapItr itrRule = connBWHostsUserRules->find(userName);
+ if (itrRule != connBWHostsUserRules->end()) {
+ for (bwHostRuleSetItr it=(*itrRule).second.begin();
+ it!=(*itrRule).second.end(); it++) {
+ if (it->getAclHost().match(hostName)) {
+ // This host matches a user spec and controls the
+ // allow/deny decision for this connection.
+ result = it->getAclResult();
+ logText = QPID_MSG("global rule " << it->toString()
+ << (AclHelper::resultAllows(result) ? " allows" : " denies")
+ << " connection for host " << hostName << ", user "
+ << userName);
+ decisionMade = true;
+ break;
+ } else {
+ // This rule in the user's spec doesn't match and
+ // does not control the allow/deny decision.
+ }
+ }
+ }
+ }
+
+ // Apply global connection mode
+ if (!decisionMade) {
+ result = connectionDecisionMode;
+ logText = QPID_MSG("default connection policy "
+ << (AclHelper::resultAllows(result) ? "allows" : "denies")
+ << " connection for host " << hostName << ", user "
+ << userName);
+ }
+ return result;
+}
+
+//
+//
+//
+AclData::~AclData()
+{
+ clear();
+}
+
+
+//
+// Limit check an int limit
+//
+bool AclData::compareInt(const qpid::acl::SpecProperty theProperty,
+ const std::string theAclValue,
+ const std::string theLookupValue,
+ bool theMaxFlag)
+{
+ uint64_t aclRuleValue (0);
+ uint64_t lookupValue (0);
+
+ QPID_LOG(debug, "ACL: "
+ << (theMaxFlag ? "Upper" : "Lower") << "-limit comparison for property "
+ << AclHelper::getPropertyStr(theProperty)
+ << ". Success if lookup(" << theLookupValue
+ << ") "
+ << (theMaxFlag ? "<=" : ">=") << " rule(" << theAclValue << ")");
+
+ try {
+ aclRuleValue = boost::lexical_cast<uint64_t>(theAclValue);
+ }
+ catch(const boost::bad_lexical_cast&) {
+ assert (false);
+ return false;
+ }
+
+ if (aclRuleValue == 0) {
+ QPID_LOG(debug, "ACL: Comparison is always true when ACL rule value is zero");
+ return true;
+ }
+
+ try {
+ lookupValue = boost::lexical_cast<uint64_t>(theLookupValue);
+ }
+ catch(const boost::bad_lexical_cast&) {
+ QPID_LOG(error,"ACL: Illegal value given in lookup for property '"
+ << AclHelper::getPropertyStr(theProperty)
+ << "' : " << theLookupValue);
+ return false;
+ }
+
+ bool result =
+ (theMaxFlag ? lookupValue > aclRuleValue : lookupValue < aclRuleValue);
+ if ( result ) {
+ QPID_LOG(debug, "ACL: Limit exceeded for property '"
+ << AclHelper::getPropertyStr(theProperty) << "'");
+ return false;
+ }
+
+ return true;
+}
+
+const std::string DOMAIN_SEPARATOR("@");
+const std::string PERIOD(".");
+const std::string UNDERSCORE("_");
+//
+// substituteString
+// Given a name string from an Acl rule, substitute the replacement into it
+// wherever the placeholder directs.
+//
+void AclData::substituteString(std::string& targetString,
+ const std::string& placeholder,
+ const std::string& replacement)
+{
+ assert (!placeholder.empty());
+ if (placeholder.empty()) {
+ return;
+ }
+ size_t start_pos(0);
+ while((start_pos = targetString.find(placeholder, start_pos)) != std::string::npos) {
+ targetString.replace(start_pos, placeholder.length(), replacement);
+ start_pos += replacement.length();
+ }
+}
+
+
+//
+// normalizeUserId
+// Given a name string return it in a form usable as topic keys:
+// change "@" and "." to "_".
+//
+std::string AclData::normalizeUserId(const std::string& userId)
+{
+ std::string normalId(userId);
+ substituteString(normalId, DOMAIN_SEPARATOR, UNDERSCORE);
+ substituteString(normalId, PERIOD, UNDERSCORE);
+ return normalId;
+}
+
+
+//
+// substituteUserId
+// Given an Acl rule and an authenticated userId
+// do the keyword substitutions on the rule.
+//
+void AclData::substituteUserId(std::string& ruleString,
+ const std::string& userId)
+{
+ size_t locDomSeparator(0);
+ std::string user("");
+ std::string domain("");
+ std::string userdomain = normalizeUserId(userId);
+
+ locDomSeparator = userId.find(DOMAIN_SEPARATOR);
+ if (std::string::npos == locDomSeparator) {
+ // "@" not found. There's just a user name
+ user = normalizeUserId(userId);
+ } else {
+ // "@" found, split the names. Domain may be blank.
+ user = normalizeUserId(userId.substr(0,locDomSeparator));
+ domain = normalizeUserId(userId.substr(locDomSeparator+1));
+ }
+
+ substituteString(ruleString, ACL_KEYWORD_USER_SUBST, user);
+ substituteString(ruleString, ACL_KEYWORD_DOMAIN_SUBST, domain);
+ substituteString(ruleString, ACL_KEYWORD_USERDOMAIN_SUBST, userdomain);
+}
+
+
+//
+// substituteKeywords
+// Given an Acl rule and an authenticated userId
+// do reverse keyword substitutions on the rule.
+// That is, replace the normalized name in the rule string with
+// the keyword that represents it. This stragegy is used for
+// topic key lookups where the keyword string proper is in the
+// topic key search tree.
+//
+void AclData::substituteKeywords(std::string& ruleString,
+ const std::string& userId)
+{
+ size_t locDomSeparator(0);
+ std::string user("");
+ std::string domain("");
+ std::string userdomain = normalizeUserId(userId);
+
+ locDomSeparator = userId.find(DOMAIN_SEPARATOR);
+ if (std::string::npos == locDomSeparator) {
+ // "@" not found. There's just a user name
+ user = normalizeUserId(userId);
+ } else {
+ // "@" found, split the names
+ user = normalizeUserId(userId.substr(0,locDomSeparator));
+ domain = normalizeUserId(userId.substr(locDomSeparator+1));
+ }
+ std::string oRule(ruleString);
+ substituteString(ruleString, userdomain, ACL_KEYWORD_USERDOMAIN_SUBST);
+ substituteString(ruleString, user, ACL_KEYWORD_USER_SUBST);
+ substituteString(ruleString, domain, ACL_KEYWORD_DOMAIN_SUBST);
+}
+}}
diff --git a/qpid/cpp/src/qpid/acl/AclData.h b/qpid/cpp/src/qpid/acl/AclData.h
new file mode 100644
index 0000000000..105a5d9c67
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclData.h
@@ -0,0 +1,323 @@
+#ifndef QPID_ACL_ACLDATA_H
+#define QPID_ACL_ACLDATA_H
+
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/AclModule.h"
+#include "qpid/AclHost.h"
+#include "AclTopicMatch.h"
+#include "qpid/log/Statement.h"
+#include "boost/shared_ptr.hpp"
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+/** A rule for tracking black/white host connection settings.
+ * When a connection is attempted, the remote host is verified
+ * against lists of these rules. When the remote host is in
+ * the range specified by this aclHost then the AclResult is
+ * applied as allow/deny.
+ */
+class AclBWHostRule {
+public:
+ AclBWHostRule(AclResult r, std::string h) :
+ aclResult(r), aclHost(h) {}
+
+ std::string toString () const {
+ std::ostringstream ruleStr;
+ ruleStr << "[ruleMode = " << AclHelper::getAclResultStr(aclResult)
+ << " {" << aclHost.str() << "}";
+ return ruleStr.str();
+ }
+ const AclHost& getAclHost() const { return aclHost; }
+ const AclResult& getAclResult() const { return aclResult; }
+
+private:
+ AclResult aclResult;
+ AclHost aclHost;
+};
+
+
+class AclData {
+
+
+public:
+
+ typedef std::map<qpid::acl::Property, std::string> propertyMap;
+ typedef propertyMap::const_iterator propertyMapItr;
+
+ typedef std::map<qpid::acl::SpecProperty, std::string> specPropertyMap;
+ typedef specPropertyMap::const_iterator specPropertyMapItr;
+
+ //
+ // Rule
+ //
+ // Created by AclReader and stored in a ruleSet vector for subsequent
+ // run-time lookup matching and allow/deny decisions.
+ // RuleSet vectors are indexed by Action-Object-actorId so these
+ // attributes are not part of a rule.
+ // A single ACL file entry may create many rule entries in
+ // many ruleset vectors.
+ //
+ struct Rule {
+ typedef broker::TopicExchange::TopicExchangeTester topicTester;
+
+ int rawRuleNum; // rule number in ACL file
+ qpid::acl::AclResult ruleMode; // combined allow/deny log/nolog
+ specPropertyMap props; // properties to be matched
+ // pubXxx for publish exchange fastpath
+ bool pubRoutingKeyInRule;
+ std::string pubRoutingKey;
+ boost::shared_ptr<topicTester> pTTest;
+ bool pubExchNameInRule;
+ bool pubExchNameMatchesBlank;
+ std::string pubExchName;
+ std::vector<bool> ruleHasUserSub;
+ std::string lookupSource;
+ std::string lookupHelp;
+
+ Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p) :
+ rawRuleNum(ruleNum),
+ ruleMode(res),
+ props(p),
+ pubRoutingKeyInRule(false),
+ pubRoutingKey(),
+ pTTest(boost::shared_ptr<topicTester>(new topicTester())),
+ pubExchNameInRule(false),
+ pubExchNameMatchesBlank(false),
+ pubExchName(),
+ ruleHasUserSub(PROPERTYSIZE, false)
+ {}
+
+ // Variation of Rule for tracking PropertyDefs
+ // for AclValidation.
+ Rule (int ruleNum, qpid::acl::AclResult res, specPropertyMap& p,
+ const std::string& ls, const std::string& lh
+ ) :
+ rawRuleNum(ruleNum),
+ ruleMode(res),
+ props(p),
+ pubRoutingKeyInRule(false),
+ pubRoutingKey(),
+ pubExchNameInRule(false),
+ pubExchNameMatchesBlank(false),
+ pubExchName(),
+ ruleHasUserSub(PROPERTYSIZE, false),
+ lookupSource(ls),
+ lookupHelp(lh)
+ {}
+
+
+ std::string toString () const {
+ std::ostringstream ruleStr;
+ ruleStr << "[rule " << rawRuleNum
+ << " ruleMode = " << AclHelper::getAclResultStr(ruleMode)
+ << " props{";
+ for (specPropertyMapItr pMItr = props.begin();
+ pMItr != props.end();
+ pMItr++) {
+ ruleStr << " "
+ << AclHelper::getPropertyStr((SpecProperty) pMItr-> first)
+ << "=" << pMItr->second;
+ }
+ ruleStr << " }]";
+ return ruleStr.str();
+ }
+
+ void addTopicTest(const std::string& pattern) {
+ pTTest->addBindingKey(broker::TopicExchange::normalize(pattern));
+ }
+
+ // Topic Exchange tester
+ // return true if any bindings match 'pattern'
+ bool matchRoutingKey(const std::string& pattern) const
+ {
+ topicTester::BindingVec bv;
+ return pTTest->findMatches(pattern, bv);
+ }
+ };
+
+ typedef std::vector<Rule> ruleSet;
+ typedef ruleSet::const_iterator ruleSetItr;
+ typedef std::map<std::string, ruleSet > actionObject; // user
+ typedef actionObject::iterator actObjItr;
+ typedef actionObject* aclAction;
+ typedef std::map<std::string, uint16_t> quotaRuleSet; // <username, N>
+ typedef quotaRuleSet::const_iterator quotaRuleSetItr;
+ typedef std::vector<AclBWHostRule> bwHostRuleSet; // allow/deny hosts-vector
+ typedef bwHostRuleSet::const_iterator bwHostRuleSetItr;
+ typedef std::map<std::string, bwHostRuleSet> bwHostUserRuleMap; //<username, hosts-vector>
+ typedef bwHostUserRuleMap::const_iterator bwHostUserRuleMapItr;
+
+ // Action*[] -> Object*[] -> map<user, set<Rule> >
+ aclAction* actionList[qpid::acl::ACTIONSIZE];
+ qpid::acl::AclResult decisionMode; // allow/deny[-log] if no matching rule found
+ bool transferAcl;
+ std::string aclSource;
+ qpid::acl::AclResult connectionDecisionMode;
+
+ AclResult lookup(
+ const std::string& id, // actor id
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& name, // object name
+ std::map<Property, std::string>* params=0);
+
+ AclResult lookup(
+ const std::string& id, // actor id
+ const Action& action,
+ const ObjectType& objType,
+ const std::string& ExchangeName,
+ const std::string& RoutingKey);
+
+ boost::shared_ptr<const bwHostRuleSet> getGlobalConnectionRules() {
+ return connBWHostsGlobalRules;
+ }
+
+ boost::shared_ptr<const bwHostUserRuleMap> getUserConnectionRules() {
+ return connBWHostsUserRules;
+ }
+
+ bool matchProp(const std::string & src, const std::string& src1);
+ void clear ();
+ void printDecisionRules(int userFieldWidth);
+
+ static const std::string ACL_KEYWORD_USER_SUBST;
+ static const std::string ACL_KEYWORD_DOMAIN_SUBST;
+ static const std::string ACL_KEYWORD_USERDOMAIN_SUBST;
+ static const std::string ACL_KEYWORD_ALL;
+ static const std::string ACL_KEYWORD_ACL;
+ static const std::string ACL_KEYWORD_GROUP;
+ static const std::string ACL_KEYWORD_QUOTA;
+ static const std::string ACL_KEYWORD_QUOTA_CONNECTIONS;
+ static const std::string ACL_KEYWORD_QUOTA_QUEUES;
+ static const char ACL_SYMBOL_WILDCARD;
+ static const std::string ACL_KEYWORD_WILDCARD;
+ static const char ACL_SYMBOL_LINE_CONTINUATION;
+ static const std::string ACL_KEYWORD_DEFAULT_EXCHANGE;
+
+ void substituteString(std::string& targetString,
+ const std::string& placeholder,
+ const std::string& replacement);
+ std::string normalizeUserId(const std::string& userId);
+ void substituteUserId(std::string& ruleString,
+ const std::string& userId);
+ void substituteKeywords(std::string& ruleString,
+ const std::string& userId);
+
+ // Per user connection quotas extracted from acl rule file
+ // Set by reader
+ void setConnQuotaRuleSettings (boost::shared_ptr<quotaRuleSet>);
+ // Get by connection approvers
+ bool enforcingConnectionQuotas() const { return connQuotaRuleSettings->size() > 0; }
+ bool getConnQuotaForUser(const std::string&, uint16_t*) const;
+
+ // Per user queue quotas extracted from acl rule file
+ // Set by reader
+ void setQueueQuotaRuleSettings (boost::shared_ptr<quotaRuleSet>);
+ // Get by queue approvers
+ bool enforcingQueueQuotas() const { return queueQuotaRuleSettings->size() > 0; }
+ bool getQueueQuotaForUser(const std::string&, uint16_t*) const;
+
+ // Global connection Black/White list rules
+ void setConnGlobalRules (boost::shared_ptr<bwHostRuleSet>);
+
+ // Per-user connection Black/White list rules map
+ void setConnUserRules (boost::shared_ptr<bwHostUserRuleMap>);
+
+ /** getConnectMaxSpec
+ * Connection quotas are held in uint16_t variables.
+ * This function specifies the largest value that a user is allowed
+ * to declare for a connection quota. The upper limit serves two
+ * purposes: 1. It leaves room for magic numbers that may be declared
+ * by keyword names in Acl files and not have those numbers conflict
+ * with innocent user declared values, and 2. It makes the unsigned
+ * math very close to _MAX work reliably with no risk of accidental
+ * wrapping back to zero.
+ */
+ static uint16_t getConnectMaxSpec() {
+ return 65530;
+ }
+ static std::string getMaxConnectSpecStr() {
+ return "65530";
+ }
+
+ static uint16_t getQueueMaxSpec() {
+ return 65530;
+ }
+ static std::string getMaxQueueSpecStr() {
+ return "65530";
+ }
+
+ /**
+ * isAllowedConnection
+ * Return true if this user is allowed to connect to this host.
+ * Return log text describing both success and failure.
+ */
+ AclResult isAllowedConnection(const std::string& userName,
+ const std::string& hostName,
+ std::string& logText);
+
+ AclResult connectionMode() const {
+ return connectionDecisionMode;
+ }
+
+ AclData();
+ virtual ~AclData();
+
+private:
+
+ inline bool lookupMatchRule(
+ const ruleSetItr& rsItr,
+ const std::string& id,
+ const std::string& name,
+ const std::map<Property, std::string>* params,
+ AclResult& aclresult);
+
+ inline bool lookupMatchPublishExchangeRule(
+ const ruleSetItr& rsItr,
+ const std::string& id,
+ const std::string& name,
+ const std::string& routingKey,
+ AclResult& aclresult);
+
+ bool compareInt(const qpid::acl::SpecProperty theProperty,
+ const std::string theAclValue,
+ const std::string theLookupValue,
+ bool theMaxFlag);
+
+ // Per-user connection quota
+ boost::shared_ptr<quotaRuleSet> connQuotaRuleSettings;
+
+ // Per-user queue quota
+ boost::shared_ptr<quotaRuleSet> queueQuotaRuleSettings;
+
+ // Global host connection black/white rule set
+ boost::shared_ptr<bwHostRuleSet> connBWHostsGlobalRules;
+
+ // Per-user host connection black/white rule set map
+ boost::shared_ptr<bwHostUserRuleMap> connBWHostsUserRules;
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLDATA_H
diff --git a/qpid/cpp/src/qpid/acl/AclLexer.cpp b/qpid/cpp/src/qpid/acl/AclLexer.cpp
new file mode 100644
index 0000000000..4006e5271f
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclLexer.cpp
@@ -0,0 +1,141 @@
+/*
+ *
+ * Copyright (c) 2014 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclLexer.h"
+#include "qpid/RefCounted.h"
+#include "qpid/Exception.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/concept_check.hpp>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+// ObjectType
+const std::string objectNames[OBJECTSIZE] = {
+ "broker", "connection", "exchange", "link", "method", "query", "queue" };
+
+ObjectType AclHelper::getObjectType(const std::string& str) {
+ for (int i=0; i< OBJECTSIZE; ++i) {
+ if (str.compare(objectNames[i]) == 0)
+ return ObjectType(i);
+ }
+ throw qpid::Exception("Acl illegal object name: " + str);
+}
+
+const std::string& AclHelper::getObjectTypeStr(const ObjectType o) {
+ return objectNames[o];
+}
+
+// Action
+const std::string actionNames[ACTIONSIZE] = {
+ "access", "bind", "consume", "create", "delete",
+ "move", "publish", "purge", "redirect", "reroute",
+ "unbind", "update" };
+
+Action AclHelper::getAction(const std::string& str) {
+ for (int i=0; i< ACTIONSIZE; ++i) {
+ if (str.compare(actionNames[i]) == 0)
+ return Action(i);
+ }
+ throw qpid::Exception("Acl illegal action name: " + str);
+}
+
+const std::string& AclHelper::getActionStr(const Action a) {
+ return actionNames[a];
+}
+
+// Property
+// These are shared between broker and acl using code enums.
+const std::string propertyNames[PROPERTYSIZE] = {
+ "name", "durable", "owner", "routingkey", "autodelete", "exclusive", "type",
+ "alternate", "queuename", "exchangename", "schemapackage",
+ "schemaclass", "policytype", "paging", "host",
+
+ "maxpages", "maxpagefactor",
+ "maxqueuesize", "maxqueuecount", "maxfilesize", "maxfilecount"};
+
+Property AclHelper::getProperty(const std::string& str) {
+ for (int i=0; i< PROPERTYSIZE; ++i) {
+ if (str.compare(propertyNames[i]) == 0)
+ return Property(i);
+ }
+ throw qpid::Exception("Acl illegal property name: " + str);
+}
+
+const std::string& AclHelper::getPropertyStr(const Property p) {
+ return propertyNames[p];
+}
+
+// SpecProperty
+// These are shared between user acl files and acl using text.
+const std::string specPropertyNames[SPECPROPSIZE] = {
+ "name", "durable", "owner", "routingkey", "autodelete", "exclusive", "type",
+ "alternate", "queuename", "exchangename", "schemapackage",
+ "schemaclass", "policytype", "paging", "host",
+
+ "queuemaxsizelowerlimit", "queuemaxsizeupperlimit",
+ "queuemaxcountlowerlimit", "queuemaxcountupperlimit",
+ "filemaxsizelowerlimit", "filemaxsizeupperlimit",
+ "filemaxcountlowerlimit", "filemaxcountupperlimit",
+ "pageslowerlimit", "pagesupperlimit",
+ "pagefactorlowerlimit", "pagefactorupperlimit" };
+
+SpecProperty AclHelper::getSpecProperty(const std::string& str) {
+ for (int i=0; i< SPECPROPSIZE; ++i) {
+ if (str.compare(specPropertyNames[i]) == 0)
+ return SpecProperty(i);
+ }
+ // Allow old names in ACL file as aliases for newly-named properties
+ if (str.compare("maxqueuesize") == 0)
+ return SPECPROP_MAXQUEUESIZEUPPERLIMIT;
+ if (str.compare("maxqueuecount") == 0)
+ return SPECPROP_MAXQUEUECOUNTUPPERLIMIT;
+ throw qpid::Exception("Acl illegal spec property name: " + str);
+}
+
+const std::string& AclHelper::getPropertyStr(const SpecProperty p) {
+ return specPropertyNames[p];
+}
+
+// AclResult
+const std::string resultNames[RESULTSIZE] = {
+ "allow", "allow-log", "deny", "deny-log" };
+
+AclResult AclHelper::getAclResult(const std::string& str) {
+ for (int i=0; i< RESULTSIZE; ++i) {
+ if (str.compare(resultNames[i]) == 0)
+ return AclResult(i);
+ }
+ throw qpid::Exception("Acl illegal result name: " + str);
+}
+
+const std::string& AclHelper::getAclResultStr(const AclResult r) {
+ return resultNames[r];
+}
+
+bool AclHelper::resultAllows(const AclResult r) {
+ bool answer = r == ALLOW || r == ALLOWLOG;
+ return answer;
+}
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclLexer.h b/qpid/cpp/src/qpid/acl/AclLexer.h
new file mode 100644
index 0000000000..d3df411afd
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclLexer.h
@@ -0,0 +1,200 @@
+#ifndef QPID_ACL_ACLLEXER_H
+#define QPID_ACL_ACLLEXER_H
+
+/*
+ *
+ * Copyright (c) 2014 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+
+#include "qpid/Exception.h"
+#include "qpid/broker/BrokerImportExport.h"
+#include <boost/shared_ptr.hpp>
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <sstream>
+
+namespace qpid {
+
+namespace acl {
+
+ // Interface enumerations.
+ // These enumerations define enum lists and implied text strings
+ // to match. They are used in two areas:
+ // 1. In the ACL specifications in the ACL file, file parsing, and
+ // internal rule storage.
+ // 2. In the authorize interface in the rest of the broker where
+ // code requests the ACL module to authorize an action.
+
+ // ObjectType shared between ACL spec and ACL authorise interface
+ enum ObjectType {
+ OBJ_BROKER,
+ OBJ_CONNECTION,
+ OBJ_EXCHANGE,
+ OBJ_LINK,
+ OBJ_METHOD,
+ OBJ_QUERY,
+ OBJ_QUEUE,
+ OBJECTSIZE }; // OBJECTSIZE must be last in list
+
+ const int OBJECTTYPE_STR_WIDTH = 10;
+
+ // Action shared between ACL spec and ACL authorise interface
+ enum Action {
+ ACT_ACCESS,
+ ACT_BIND,
+ ACT_CONSUME,
+ ACT_CREATE,
+ ACT_DELETE,
+ ACT_MOVE,
+ ACT_PUBLISH,
+ ACT_PURGE,
+ ACT_REDIRECT,
+ ACT_REROUTE,
+ ACT_UNBIND,
+ ACT_UPDATE,
+ ACTIONSIZE }; // ACTIONSIZE must be last in list
+
+ const int ACTION_STR_WIDTH = 8;
+
+ // Property used in ACL authorize interface
+ enum Property {
+ PROP_NAME,
+ PROP_DURABLE,
+ PROP_OWNER,
+ PROP_ROUTINGKEY,
+ PROP_AUTODELETE,
+ PROP_EXCLUSIVE,
+ PROP_TYPE,
+ PROP_ALTERNATE,
+ PROP_QUEUENAME,
+ PROP_EXCHANGENAME,
+ PROP_SCHEMAPACKAGE,
+ PROP_SCHEMACLASS,
+ PROP_POLICYTYPE,
+ PROP_PAGING,
+ PROP_HOST,
+ PROP_MAXPAGES,
+ PROP_MAXPAGEFACTOR,
+ PROP_MAXQUEUESIZE,
+ PROP_MAXQUEUECOUNT,
+ PROP_MAXFILESIZE,
+ PROP_MAXFILECOUNT,
+ PROPERTYSIZE // PROPERTYSIZE must be last in list
+ };
+
+ // Property used in ACL spec file
+ // Note for properties common to file processing/rule storage and to
+ // broker rule lookups the identical enum values are used.
+ enum SpecProperty {
+ SPECPROP_NAME = PROP_NAME,
+ SPECPROP_DURABLE = PROP_DURABLE,
+ SPECPROP_OWNER = PROP_OWNER,
+ SPECPROP_ROUTINGKEY = PROP_ROUTINGKEY,
+ SPECPROP_AUTODELETE = PROP_AUTODELETE,
+ SPECPROP_EXCLUSIVE = PROP_EXCLUSIVE,
+ SPECPROP_TYPE = PROP_TYPE,
+ SPECPROP_ALTERNATE = PROP_ALTERNATE,
+ SPECPROP_QUEUENAME = PROP_QUEUENAME,
+ SPECPROP_EXCHANGENAME = PROP_EXCHANGENAME,
+ SPECPROP_SCHEMAPACKAGE = PROP_SCHEMAPACKAGE,
+ SPECPROP_SCHEMACLASS = PROP_SCHEMACLASS,
+ SPECPROP_POLICYTYPE = PROP_POLICYTYPE,
+ SPECPROP_PAGING = PROP_PAGING,
+ SPECPROP_HOST = PROP_HOST,
+
+ SPECPROP_MAXQUEUESIZELOWERLIMIT,
+ SPECPROP_MAXQUEUESIZEUPPERLIMIT,
+ SPECPROP_MAXQUEUECOUNTLOWERLIMIT,
+ SPECPROP_MAXQUEUECOUNTUPPERLIMIT,
+ SPECPROP_MAXFILESIZELOWERLIMIT,
+ SPECPROP_MAXFILESIZEUPPERLIMIT,
+ SPECPROP_MAXFILECOUNTLOWERLIMIT,
+ SPECPROP_MAXFILECOUNTUPPERLIMIT,
+ SPECPROP_MAXPAGESLOWERLIMIT,
+ SPECPROP_MAXPAGESUPPERLIMIT,
+ SPECPROP_MAXPAGEFACTORLOWERLIMIT,
+ SPECPROP_MAXPAGEFACTORUPPERLIMIT,
+ SPECPROPSIZE // SPECPROPSIZE must be last
+ };
+
+// AclResult shared between ACL spec and ACL authorise interface
+ enum AclResult {
+ ALLOW,
+ ALLOWLOG,
+ DENY,
+ DENYLOG,
+ RESULTSIZE
+ };
+
+
+ QPID_BROKER_CLASS_EXTERN class AclHelper {
+ private:
+ AclHelper(){}
+ public:
+ static QPID_BROKER_EXTERN ObjectType getObjectType(const std::string& str);
+ static QPID_BROKER_EXTERN const std::string& getObjectTypeStr(const ObjectType o);
+ static QPID_BROKER_EXTERN Action getAction(const std::string& str);
+ static QPID_BROKER_EXTERN const std::string& getActionStr(const Action a);
+ static QPID_BROKER_EXTERN Property getProperty(const std::string& str);
+ static QPID_BROKER_EXTERN const std::string& getPropertyStr(const Property p);
+ static QPID_BROKER_EXTERN SpecProperty getSpecProperty(const std::string& str);
+ static QPID_BROKER_EXTERN const std::string& getPropertyStr(const SpecProperty p);
+ static QPID_BROKER_EXTERN AclResult getAclResult(const std::string& str);
+ static QPID_BROKER_EXTERN const std::string& getAclResultStr(const AclResult r);
+ static QPID_BROKER_EXTERN bool resultAllows(const AclResult r);
+
+ typedef std::set<Property> propSet;
+ typedef boost::shared_ptr<propSet> propSetPtr;
+ typedef std::pair<Action, propSetPtr> actionPair;
+ typedef std::map<Action, propSetPtr> actionMap;
+ typedef boost::shared_ptr<actionMap> actionMapPtr;
+ typedef std::pair<ObjectType, actionMapPtr> objectPair;
+ typedef std::map<Property, std::string> propMap;
+ typedef propMap::const_iterator propMapItr;
+ typedef std::map<SpecProperty, std::string> specPropMap;
+ typedef specPropMap::const_iterator specPropMapItr;
+
+ //
+ // properyMapToString
+ //
+ template <typename T>
+ static std::string propertyMapToString(
+ const std::map<T, std::string>* params)
+ {
+ std::ostringstream ss;
+ ss << "{";
+ if (params)
+ {
+ for (typename std::map<T, std::string>::const_iterator
+ pMItr = params->begin(); pMItr != params->end(); pMItr++)
+ {
+ ss << " " << getPropertyStr((T) pMItr-> first)
+ << "=" << pMItr->second;
+ }
+ }
+ ss << " }";
+ return ss.str();
+ }
+
+ };
+
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLLEXER_H
diff --git a/qpid/cpp/src/qpid/acl/AclPlugin.cpp b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
new file mode 100644
index 0000000000..77580ba531
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclPlugin.cpp
@@ -0,0 +1,98 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include <sstream>
+#include "qpid/acl/Acl.h"
+#include "qpid/broker/Broker.h"
+#include "qpid/Plugin.h"
+#include "qpid/Options.h"
+#include "qpid/sys/Path.h"
+#include "qpid/log/Statement.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/utility/in_place_factory.hpp>
+
+namespace qpid {
+namespace acl {
+
+using namespace std;
+
+/** Note separating options from values to work around boost version differences.
+ * Old boost takes a reference to options objects, but new boost makes a copy.
+ * New boost allows a shared_ptr but that's not compatible with old boost.
+ */
+struct AclOptions : public Options {
+ AclValues& values;
+
+ AclOptions(AclValues& v) : Options("ACL Options"), values(v) {
+ values.aclMaxConnectTotal = 500;
+ addOptions()
+ ("acl-file", optValue(values.aclFile, "FILE"), "The policy file to load from, loaded from data dir")
+ ("connection-limit-per-user", optValue(values.aclMaxConnectPerUser, "N"), "The maximum number of connections allowed per user. 0 implies no limit.")
+ ("max-connections" , optValue(values.aclMaxConnectTotal, "N"), "The maximum combined number of connections allowed. 0 implies no limit.")
+ ("connection-limit-per-ip" , optValue(values.aclMaxConnectPerIp, "N"), "The maximum number of connections allowed per host IP address. 0 implies no limit.")
+ ("max-queues-per-user", optValue(values.aclMaxQueuesPerUser, "N"), "The maximum number of queues allowed per user. 0 implies no limit.")
+ ;
+ }
+};
+
+struct AclPlugin : public Plugin {
+
+ AclValues values;
+ AclOptions options;
+ boost::intrusive_ptr<Acl> acl;
+
+ AclPlugin() : options(values) {}
+
+ Options* getOptions() { return &options; }
+
+ void init(broker::Broker& b) {
+ if (acl) throw Exception("ACL plugin cannot be initialized twice in one process.");
+
+ if (!values.aclFile.empty()){
+ sys::Path aclFile(values.aclFile);
+ sys::Path dataDir(b.getDataDir().getPath());
+ if (!aclFile.isAbsolute() && !dataDir.empty())
+ values.aclFile = (dataDir + aclFile).str();
+ }
+ acl = new Acl(values, b);
+ b.setAcl(acl.get());
+ b.addFinalizer(boost::bind(&AclPlugin::shutdown, this));
+ }
+
+ template <class T> bool init(Plugin::Target& target) {
+ T* t = dynamic_cast<T*>(&target);
+ if (t) init(*t);
+ return t;
+ }
+
+ void earlyInitialize(Plugin::Target&) {}
+
+ void initialize(Plugin::Target& target) {
+ init<broker::Broker>(target);
+ }
+
+ void shutdown() { acl = 0; }
+};
+
+static AclPlugin instance; // Static initialization.
+
+// For test purposes.
+boost::intrusive_ptr<Acl> getGlobalAcl() { return instance.acl; }
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclReader.cpp b/qpid/cpp/src/qpid/acl/AclReader.cpp
new file mode 100644
index 0000000000..e8223c3570
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclReader.cpp
@@ -0,0 +1,827 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclReader.h"
+#include "qpid/acl/AclData.h"
+
+#include <cctype>
+#include <cstring>
+#include <fstream>
+#include <sstream>
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+#include <boost/lexical_cast.hpp>
+#include <algorithm>
+
+#include <iomanip> // degug
+#include <iostream> // debug
+
+#define ACL_FORMAT_ERR_LOG_PREFIX "ACL format error: " << fileName << ":" << lineNumber << ": "
+
+namespace qpid {
+namespace acl {
+
+ AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups) : res(r), actionAll(true), objStatus(NONE) {
+ processName(n, groups);
+ }
+ AclReader::aclRule::aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a) : res(r), actionAll(false), action(a), objStatus(NONE) {
+ processName(n, groups);
+ }
+
+ void AclReader::aclRule::setObjectType(const ObjectType o) {
+ objStatus = VALUE;
+ object = o;
+ }
+
+ void AclReader::aclRule::setObjectTypeAll() {
+ objStatus = ALL;
+ }
+
+ bool AclReader::aclRule::addProperty(const SpecProperty p, const std::string v) {
+ return props.insert(propNvPair(p, v)).second;
+ }
+
+ // Debug aid
+ std::string AclReader::aclRule::toString() {
+ std::ostringstream oss;
+ oss << AclHelper::getAclResultStr(res) << " [";
+ for (nsCitr itr = names.begin(); itr != names.end(); itr++) {
+ if (itr != names.begin()) oss << ", ";
+ oss << *itr;
+ }
+ oss << "]";
+ if (actionAll) {
+ oss << " *";
+ } else {
+ oss << " " << AclHelper::getActionStr(action);
+ }
+ if (objStatus == ALL) {
+ oss << " *";
+ } else if (objStatus == VALUE) {
+ oss << " " << AclHelper::getObjectTypeStr(object);
+ }
+ for (pmCitr i=props.begin(); i!=props.end(); i++) {
+ oss << " " << AclHelper::getPropertyStr(i->first) << "=" << i->second;
+ }
+ return oss.str();
+ }
+
+ void AclReader::loadDecisionData(boost::shared_ptr<AclData> d) {
+ d->clear();
+ QPID_LOG(debug, "ACL: Load Rules");
+ bool foundmode = false;
+ bool foundConnectionMode = false;
+
+ rlCitr i = rules.end();
+ for (int cnt = rules.size(); cnt; cnt--) {
+ i--;
+ QPID_LOG(debug, "ACL: Processing " << std::setfill(' ') << std::setw(2)
+ << cnt << " " << (*i)->toString());
+
+ if (!(*i)->actionAll && (*i)->objStatus == aclRule::VALUE &&
+ !validator.validateAllowedProperties(
+ (*i)->action, (*i)->object, (*i)->props, false)) {
+ // specific object/action has bad property
+ // this rule gets ignored
+ continue;
+ } else {
+ // action=all or object=none/all means the rule gets propagated
+ // possibly to many places.
+ // Invalid rule combinations are not propagated.
+ }
+
+ if (!foundmode && (*i)->actionAll && (*i)->names.size() == 1
+ && (*((*i)->names.begin())).compare(AclData::ACL_KEYWORD_WILDCARD) == 0) {
+ d->decisionMode = (*i)->res;
+ QPID_LOG(debug, "ACL: FoundMode "
+ << AclHelper::getAclResultStr(d->decisionMode));
+ foundmode = true;
+ } else if ((*i)->action == acl::ACT_CREATE && (*i)->object == acl::OBJ_CONNECTION) {
+ // Intercept CREATE CONNECTION rules process them into separate lists to
+ // be consumed in the connection approval code path.
+ propMap::const_iterator pName = (*i)->props.find(SPECPROP_NAME);
+ if (pName != (*i)->props.end()) {
+ throw Exception(QPID_MSG("ACL: CREATE CONNECTION rule " << cnt << " must not have a 'name' property"));
+ }
+ propMap::const_iterator pHost = (*i)->props.find(SPECPROP_HOST);
+ if (pHost == (*i)->props.end()) {
+ throw Exception(QPID_MSG("ACL: CREATE CONNECTION rule " << cnt << " has no 'host' property"));
+ }
+ // create the connection rule
+ bool allUsers = (*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0;
+ bool allHosts = pHost->second.compare(AclData::ACL_KEYWORD_ALL) == 0;
+ AclBWHostRule bwRule((*i)->res, (allHosts ? "" : pHost->second));
+
+ // apply the rule globally or to user list
+ if (allUsers) {
+ if (allHosts) {
+ // allow one specification of allUsers,allHosts
+ if (foundConnectionMode) {
+ throw Exception(QPID_MSG("ACL: only one CREATE CONNECTION rule for user=all and host=all allowed"));
+ }
+ foundConnectionMode = true;
+ d->connectionDecisionMode = (*i)->res;
+ QPID_LOG(trace, "ACL: Found connection mode: " << AclHelper::getAclResultStr( (*i)->res ));
+ } else {
+ // Rules for allUsers but not allHosts go into the global list
+ globalHostRules->insert( globalHostRules->begin(), bwRule );
+ }
+ } else {
+ // other rules go into binned rule sets for each user
+ for (nsCitr itr = (*i)->names.begin();
+ itr != (*i)->names.end();
+ itr++) {
+ (*userHostRules)[(*itr)].insert( (*userHostRules)[(*itr)].begin(), bwRule);
+ }
+ }
+ } else {
+ AclData::Rule rule(cnt, (*i)->res, (*i)->props);
+ // Record which properties have the user substitution string
+ for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+ if ((pItr->second.find(AclData::ACL_KEYWORD_USER_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_DOMAIN_SUBST, 0) != std::string::npos) ||
+ (pItr->second.find(AclData::ACL_KEYWORD_USERDOMAIN_SUBST, 0) != std::string::npos)) {
+ rule.ruleHasUserSub[pItr->first] = true;
+ }
+ }
+
+ // Find possible routingkey property and cache its pattern
+ for (pmCitr pItr=rule.props.begin(); pItr!=rule.props.end(); pItr++) {
+ if (acl::SPECPROP_ROUTINGKEY == pItr->first)
+ {
+ rule.pubRoutingKeyInRule = true;
+ rule.pubRoutingKey = (std::string)pItr->second;
+ rule.addTopicTest(rule.pubRoutingKey);
+ }
+ }
+
+ // Action -> Object -> map<user -> set<Rule> >
+ std::ostringstream actionstr;
+ for (int acnt = ((*i)->actionAll ? 0 : (*i)->action);
+ acnt < acl::ACTIONSIZE;
+ (*i)->actionAll ? acnt++ : acnt = acl::ACTIONSIZE) {
+
+ if (acnt == acl::ACT_PUBLISH)
+ {
+ d->transferAcl = true; // we have transfer ACL
+ // For Publish the only object should be Exchange
+ // and the only property should be routingkey.
+ // Go through the rule properties and find the name and the key.
+ // If found then place them specially for the lookup engine.
+ for (pmCitr pItr=(*i)->props.begin(); pItr!=(*i)->props.end(); pItr++) {
+ if (acl::SPECPROP_NAME == pItr->first)
+ {
+ rule.pubExchNameInRule = true;
+ rule.pubExchName = pItr->second;
+ rule.pubExchNameMatchesBlank = rule.pubExchName.compare(AclData::ACL_KEYWORD_DEFAULT_EXCHANGE) == 0;
+ }
+ }
+ }
+ actionstr << AclHelper::getActionStr((Action) acnt) << ",";
+
+ //find the Action, create if not exist
+ if (d->actionList[acnt] == NULL) {
+ d->actionList[acnt] =
+ new AclData::aclAction[qpid::acl::OBJECTSIZE];
+ for (int j = 0; j < qpid::acl::OBJECTSIZE; j++)
+ d->actionList[acnt][j] = NULL;
+ }
+
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0
+ : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+
+ //find the Object, create if not exist
+ if (d->actionList[acnt][ocnt] == NULL)
+ d->actionList[acnt][ocnt] =
+ new AclData::actionObject;
+
+ // add users and Rule to object set
+ bool allNames = false;
+ // check to see if names.begin is '*'
+ if ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0)
+ allNames = true;
+
+ for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+ if (validator.validateAllowedProperties(acl::Action(acnt),
+ acl::ObjectType(ocnt),
+ (*i)->props,
+ false)) {
+ AclData::actObjItr itrRule =
+ d->actionList[acnt][ocnt]->find(*itr);
+
+ if (itrRule == d->actionList[acnt][ocnt]->end()) {
+ AclData::ruleSet rSet;
+ rSet.push_back(rule);
+ d->actionList[acnt][ocnt]->insert
+ (make_pair(std::string(*itr), rSet));
+ } else {
+ itrRule->second.push_back(rule);
+ }
+ } else {
+ // Skip propagating this rule as it will never match.
+ }
+ }
+ }
+ }
+
+ std::ostringstream objstr;
+ for (int ocnt = ((*i)->objStatus != aclRule::VALUE ? 0 : (*i)->object);
+ ocnt < acl::OBJECTSIZE;
+ (*i)->objStatus != aclRule::VALUE ? ocnt++ : ocnt = acl::OBJECTSIZE) {
+ objstr << AclHelper::getObjectTypeStr((ObjectType) ocnt) << ",";
+ }
+
+ bool allNames = ((*(*i)->names.begin()).compare(AclData::ACL_KEYWORD_WILDCARD) == 0);
+ std::ostringstream userstr;
+ for (nsCitr itr = (allNames ? names.begin() : (*i)->names.begin());
+ itr != (allNames ? names.end() : (*i)->names.end());
+ itr++) {
+ userstr << *itr << ",";
+ }
+
+ QPID_LOG(debug, "ACL: Adding actions {" <<
+ actionstr.str().substr(0,actionstr.str().length()-1)
+ << "} to objects {" <<
+ objstr.str().substr(0,objstr.str().length()-1)
+ << "} with props " <<
+ AclHelper::propertyMapToString(&rule.props)
+ << " for users {" <<
+ userstr.str().substr(0,userstr.str().length()-1)
+ << "}");
+ }
+ }
+
+ // connection quota
+ d->setConnQuotaRuleSettings(connQuota);
+ // queue quota
+ d->setQueueQuotaRuleSettings(queueQuota);
+ // global B/W connection rules
+ d->setConnGlobalRules(globalHostRules);
+ // user B/W connection rules
+ d->setConnUserRules(userHostRules);
+ }
+
+
+ void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) {
+ if (name.compare(AclData::ACL_KEYWORD_ALL) == 0) {
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
+ } else {
+ gmCitr itr = groups.find(name);
+ if (itr == groups.end()) {
+ names.insert(name);
+ } else {
+ names.insert(itr->second->begin(), itr->second->end());
+ }
+ }
+ }
+
+ AclReader::AclReader(uint16_t theCliMaxConnPerUser, uint16_t theCliMaxQueuesPerUser) :
+ lineNumber(0), contFlag(false),
+ cliMaxConnPerUser (theCliMaxConnPerUser),
+ connQuotaRulesExist(false),
+ connQuota(new AclData::quotaRuleSet),
+ cliMaxQueuesPerUser (theCliMaxQueuesPerUser),
+ queueQuotaRulesExist(false),
+ queueQuota(new AclData::quotaRuleSet),
+ globalHostRules(new AclData::bwHostRuleSet),
+ userHostRules(new AclData::bwHostUserRuleMap) {
+ names.insert(AclData::ACL_KEYWORD_WILDCARD);
+ }
+
+ AclReader::~AclReader() {}
+
+ std::string AclReader::getError() {
+ return errorStream.str();
+ }
+
+ int AclReader::read(const std::string& fn, boost::shared_ptr<AclData> d) {
+ fileName = fn;
+ lineNumber = 0;
+ char buff[1024];
+ std::ifstream ifs(fn.c_str(), std::ios_base::in);
+ if (!ifs.good()) {
+ errorStream << "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ return -1;
+ }
+ // Propagate nonzero per-user max connection setting from CLI
+ if (cliMaxConnPerUser > 0) {
+ connQuotaRulesExist = true;
+ (*connQuota)[AclData::ACL_KEYWORD_ALL] = cliMaxConnPerUser;
+ }
+ // Propagate nonzero per-user max queue setting from CLI
+ if (cliMaxQueuesPerUser > 0) {
+ queueQuotaRulesExist = true;
+ (*queueQuota)[AclData::ACL_KEYWORD_ALL] = cliMaxQueuesPerUser;
+ }
+ // Loop to process the Acl file
+ try {
+ bool err = false;
+ while (ifs.good()) {
+ ifs.getline(buff, 1024);
+ lineNumber++;
+ if (std::strlen(buff) > 0 && buff[0] != '#') // Ignore blank lines and comments
+ err |= !processLine(buff);
+ }
+ if (!ifs.eof())
+ {
+ errorStream << "Unable to read ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F");
+ ifs.close();
+ return -2;
+ }
+ ifs.close();
+ if (err) return -3;
+ QPID_LOG(notice, "ACL: Read file \"" << fn << "\"");
+ } catch (const std::exception& e) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": " << e.what();
+ ifs.close();
+ return -4;
+ } catch (...) {
+ errorStream << "Unable to read ACL file \"" << fn << "\": Unknown exception";
+ ifs.close();
+ return -5;
+ }
+ printNames();
+ printRules();
+ printQuotas(AclData::ACL_KEYWORD_QUOTA_CONNECTIONS, connQuota);
+ printQuotas(AclData::ACL_KEYWORD_QUOTA_QUEUES, queueQuota);
+ try {
+ loadDecisionData(d);
+ } catch (const std::exception& e) {
+ errorStream << "Error loading decision data : " << e.what();
+ return -6;
+ }
+ printGlobalConnectRules();
+ printUserConnectRules();
+ validator.tracePropertyDefs();
+ d->printDecisionRules( printNamesFieldWidth() );
+
+ return 0;
+ }
+
+ bool AclReader::processLine(char* line) {
+ bool ret = false;
+ std::vector<std::string> toks;
+
+ // Check for continuation
+ char* contCharPtr = std::strrchr(line, AclData::ACL_SYMBOL_LINE_CONTINUATION);
+ bool cont = contCharPtr != 0;
+ if (cont) *contCharPtr = 0;
+
+ int numToks = tokenize(line, toks);
+
+ if (cont && numToks == 0){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line \"" << lineNumber << "\" contains an illegal extension.";
+ return false;
+ }
+
+ if (numToks && (toks[0].compare(AclData::ACL_KEYWORD_GROUP) == 0 || contFlag)) {
+ ret = processGroupLine(toks, cont);
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_ACL) == 0) {
+ ret = processAclLine(toks);
+ } else if (numToks && toks[0].compare(AclData::ACL_KEYWORD_QUOTA) == 0) {
+ ret = processQuotaLine(toks);
+ } else {
+ // Check for whitespace only line, ignore these
+ bool ws = true;
+ for (unsigned i=0; i<std::strlen(line) && ws; i++) {
+ if (!std::isspace(line[i])) ws = false;
+ }
+ if (ws) {
+ ret = true;
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Non-continuation line must start with \""
+ << AclData::ACL_KEYWORD_GROUP << "\", \""
+ << AclData::ACL_KEYWORD_ACL << "\". or \""
+ << AclData::ACL_KEYWORD_QUOTA << "\".";
+ ret = false;
+ }
+ }
+ contFlag = cont;
+ return ret;
+ }
+
+ int AclReader::tokenize(char* line, std::vector<std::string>& toks) {
+ const char* tokChars = " \t\n\f\v\r";
+ int cnt = 0;
+ char* cp = std::strtok(line, tokChars);
+ while (cp != 0) {
+ toks.push_back(std::string(cp));
+ cnt++;
+ cp = std::strtok(0, tokChars);
+ }
+ return cnt;
+ }
+
+
+ // Process 'quota' rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ const unsigned minimumSize = 3;
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for quota definition.";
+ return false;
+ }
+
+ if (toks[1].compare(AclData::ACL_KEYWORD_QUOTA_CONNECTIONS) == 0) {
+ if (processQuotaLine(toks, AclData::ACL_KEYWORD_QUOTA_CONNECTIONS, AclData::getConnectMaxSpec(), connQuota)) {
+ // We have processed a connection quota rule
+ connQuotaRulesExist = true;
+ return true;
+ }
+ } else if (toks[1].compare(AclData::ACL_KEYWORD_QUOTA_QUEUES) == 0) {
+ if (processQuotaLine(toks, AclData::ACL_KEYWORD_QUOTA_QUEUES, AclData::getConnectMaxSpec(), queueQuota)) {
+ // We have processed a queue quota rule
+ queueQuotaRulesExist = true;
+ return true;
+ }
+ } else {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Quota type \"" << toks[1] << "\" unrecognized.";
+ return false;
+ }
+ return false;
+ }
+
+
+ // Process quota rule lines
+ // Return true if the line is successfully processed without errors
+ bool AclReader::processQuotaLine(tokList& toks, const std::string theNoun, uint16_t maxSpec, aclQuotaRuleSet theRules) {
+ const unsigned toksSize = toks.size();
+
+ uint16_t nEntities(0);
+ try {
+ nEntities = boost::lexical_cast<uint16_t>(toks[2]);
+ } catch(const boost::bad_lexical_cast&) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", " << theNoun << " quota value \"" << toks[2]
+ << "\" cannot be converted to a 16-bit unsigned integer.";
+ return false;
+ }
+
+ // limit check the setting
+ if (nEntities > maxSpec)
+ {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", " << theNoun << " quota value \"" << toks[2]
+ << "\" exceeds maximum configuration setting of "
+ << maxSpec;
+ return false;
+ }
+
+ // Apply the ount to all names in rule
+ for (unsigned idx = 3; idx < toksSize; idx++) {
+ if (groups.find(toks[idx]) == groups.end()) {
+ // This is the name of an individual, not a group
+ (*theRules)[toks[idx]] = nEntities;
+ } else {
+ if (!processQuotaGroup(toks[idx], nEntities, theRules))
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ // Process quota group expansion
+ // Return true if the quota is applied to all members of the group
+ bool AclReader::processQuotaGroup(const std::string& theGroup, uint16_t theQuota, aclQuotaRuleSet theRules) {
+ gmCitr citr = groups.find(theGroup);
+
+ if (citr == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Failed to expand group \"" << theGroup << "\".";
+ return false;
+ }
+
+ for (nsCitr gni=citr->second->begin(); gni!=citr->second->end(); gni++) {
+ if (groups.find(*gni) == groups.end()) {
+ (*theRules)[*gni] = theQuota;
+ } else {
+ if (!processQuotaGroup(*gni, theQuota, theRules))
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ void AclReader::printQuotas(const std::string theNoun, aclQuotaRuleSet theRules) const {
+ QPID_LOG(debug, "ACL: " << theNoun << " quota: " << (*theRules).size() << " rules found:");
+ int cnt = 1;
+ for (AclData::quotaRuleSetItr itr=(*theRules).begin();
+ itr != (*theRules).end();
+ ++itr,++cnt) {
+ QPID_LOG(debug, "ACL: quota " << cnt << " : " << (*itr).second
+ << " " << theNoun << " for " << (*itr).first)
+ }
+ }
+
+
+ // Return true if the line is successfully processed without errors
+ // If cont is true, then groupName must be set to the continuation group name
+ bool AclReader::processGroupLine(tokList& toks, const bool cont) {
+ const unsigned toksSize = toks.size();
+
+ if (contFlag) {
+ gmCitr citr = groups.find(groupName);
+ for (unsigned i = 0; i < toksSize; i++) {
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ } else {
+ const unsigned minimumSize = (cont ? 2 : 3);
+ if (toksSize < minimumSize) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for group definition.";
+ return false;
+ }
+ if (!isValidGroupName(toks[1])) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Group name \"" << toks[1] << "\" contains illegal characters.";
+ return false;
+ }
+ gmCitr citr = addGroup(toks[1]);
+ if (citr == groups.end()) return false;
+ for (unsigned i = 2; i < toksSize; i++) {
+ if (isValidGroupName(toks[i])) {
+ if (toks[i] == groupName) {
+ QPID_LOG(debug, "ACL: Line: " << lineNumber
+ << ", Ignoring recursive sub-group \"" << toks[i] << "\".");
+ continue;
+ } else if (groups.find(toks[i]) == groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Sub-group \"" << toks[i] << "\" not defined yet.";
+ return false;
+ }
+ } else if (!isValidUserName(toks[i])) return false;
+ addName(toks[i], citr->second);
+ }
+ }
+ return true;
+ }
+
+ // Return true if sucessfully added group
+ AclReader::gmCitr AclReader::addGroup(const std::string& newGroupName) {
+ gmCitr citr = groups.find(newGroupName);
+ if (citr != groups.end()) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Duplicate group name \"" << newGroupName << "\".";
+ return groups.end();
+ }
+ groupPair p(newGroupName, nameSetPtr(new nameSet));
+ gmRes res = groups.insert(p);
+ assert(res.second);
+ groupName = newGroupName;
+ return res.first;
+ }
+
+ void AclReader::addName(const std::string& name, nameSetPtr groupNameSet) {
+ gmCitr citr = groups.find(name);
+ if (citr != groups.end()) {
+ // This is a previously defined group: add all the names in that group to this group
+ groupNameSet->insert(citr->second->begin(), citr->second->end());
+ } else {
+ // Not a known group name
+ groupNameSet->insert(name);
+ addName(name);
+ }
+ }
+
+ void AclReader::addName(const std::string& name) {
+ names.insert(name);
+ }
+
+ /**
+ * Emit debug logs exposing the name lists
+ */
+ void AclReader::printNames() const {
+ QPID_LOG(debug, "ACL: Group list: " << groups.size() << " groups found:" );
+ std::string tmp("ACL: ");
+ for (gmCitr i=groups.begin(); i!= groups.end(); i++) {
+ tmp += " \"";
+ tmp += i->first;
+ tmp += "\":";
+ for (nsCitr j=i->second->begin(); j!=i->second->end(); j++) {
+ tmp += " ";
+ tmp += *j;
+ }
+ QPID_LOG(debug, tmp);
+ tmp = "ACL: ";
+ }
+ QPID_LOG(debug, "ACL: name list: " << names.size() << " names found:" );
+ tmp = "ACL: ";
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ tmp += " ";
+ tmp += *k;
+ }
+ QPID_LOG(debug, tmp);
+ }
+
+ /**
+ * compute the width of longest user name
+ */
+ int AclReader::printNamesFieldWidth() const {
+ std::string::size_type max = 0;
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ max = std::max(max, (*k).length());
+ }
+ return max;
+ }
+
+ bool AclReader::processAclLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ if (toksSize < 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Insufficient tokens for acl definition.";
+ return false;
+ }
+
+ AclResult res;
+ try {
+ res = AclHelper::getAclResult(toks[1]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown ACL permission \"" << toks[1] << "\".";
+ return false;
+ }
+
+ bool actionAllFlag = toks[3].compare(AclData::ACL_KEYWORD_ALL) == 0;
+ bool userAllFlag = toks[2].compare(AclData::ACL_KEYWORD_ALL) == 0;
+ Action action;
+ if (actionAllFlag) {
+
+ if (userAllFlag && toksSize > 4) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Tokens found after action \"all\".";
+ return false;
+ }
+ action = ACT_CONSUME; // dummy; compiler must initialize action for this code path
+ } else {
+ try {
+ action = AclHelper::getAction(toks[3]);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown action \"" << toks[3] << "\".";
+ return false;
+ }
+ }
+
+ // Create rule obj; then add object (if any) and properties (if any)
+ aclRulePtr rule;
+ if (actionAllFlag) {
+ rule.reset(new aclRule(res, toks[2], groups));
+ } else {
+ rule.reset(new aclRule(res, toks[2], groups, action));
+ }
+
+ if (toksSize >= 5) { // object name-value pair
+ if (toks[4].compare(AclData::ACL_KEYWORD_ALL) == 0) {
+ rule->setObjectTypeAll();
+ } else {
+ try {
+ rule->setObjectType(AclHelper::getObjectType(toks[4]));
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown object \"" << toks[4] << "\".";
+ return false;
+ }
+ }
+ }
+
+ if (toksSize >= 6) { // property name-value pair(s)
+ for (unsigned i=5; i<toksSize; i++) {
+ nvPair propNvp = splitNameValuePair(toks[i]);
+ if (propNvp.second.size() == 0) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ <<", Badly formed property name-value pair \""
+ << propNvp.first << "\". (Must be name=value)";
+ return false;
+ }
+ SpecProperty prop;
+ try {
+ prop = AclHelper::getSpecProperty(propNvp.first);
+ } catch (...) {
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Unknown property \"" << propNvp.first << "\".";
+ return false;
+ }
+ rule->addProperty(prop, propNvp.second);
+ }
+ }
+ // Check if name (toks[2]) is group; if not, add as name of individual
+ if (toks[2].compare(AclData::ACL_KEYWORD_ALL) != 0) {
+ if (groups.find(toks[2]) == groups.end()) {
+ addName(toks[2]);
+ }
+ }
+
+ rules.push_back(rule);
+
+ return true;
+ }
+
+ // Debug aid
+ void AclReader::printRules() const {
+ QPID_LOG(debug, "ACL: Rule list: " << rules.size() << " ACL rules found:");
+ int cnt = 1;
+ for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString());
+ if (!(*i)->actionAll && (*i)->objStatus == aclRule::VALUE) {
+ (void)validator.validateAllowedProperties((*i)->action, (*i)->object, (*i)->props, true);
+ }
+ }
+ }
+
+ void AclReader::printConnectionRules(const std::string name, const AclData::bwHostRuleSet& rules) const {
+ QPID_LOG(debug, "ACL: " << name << " Connection Rule list : " << rules.size() << " rules found :");
+ int cnt = 1;
+ for (AclData::bwHostRuleSetItr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, "ACL: " << std::setfill(' ') << std::setw(2) << cnt << " " << i->toString());
+ }
+ }
+
+ void AclReader::printGlobalConnectRules() const {
+ printConnectionRules("global", *globalHostRules);
+ }
+
+ void AclReader::printUserConnectRules() const {
+ QPID_LOG(debug, "ACL: User Connection Rule lists : " << userHostRules->size() << " user lists found :");
+ int cnt = 1;
+ for (AclData::bwHostUserRuleMapItr i=userHostRules->begin(); i!=userHostRules->end(); i++,cnt++) {
+ printConnectionRules(std::string((*i).first), (*i).second);
+ }
+ }
+
+ // Static function
+ // Return true if the name is well-formed (ie contains legal characters)
+ bool AclReader::isValidGroupName(const std::string& name) {
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_') return false;
+ }
+ return true;
+ }
+
+ // Static function
+ // Split name-value pair around '=' char of the form "name=value"
+ AclReader::nvPair AclReader::splitNameValuePair(const std::string& nvpString) {
+ std::size_t pos = nvpString.find("=");
+ if (pos == std::string::npos || pos == nvpString.size() - 1) {
+ return nvPair(nvpString, "");
+ }
+ return nvPair(nvpString.substr(0, pos), nvpString.substr(pos+1));
+ }
+
+ // Returns true if a username has the name@realm format
+ bool AclReader::isValidUserName(const std::string& name){
+ size_t pos = name.find('@');
+ if ( pos == std::string::npos || pos == name.length() -1){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username '" << name << "' must contain a realm";
+ return false;
+ }
+ for (unsigned i=0; i<name.size(); i++) {
+ const char ch = name.at(i);
+ if (!std::isalnum(ch) && ch != '-' && ch != '_' && ch != '@' && ch != '.' && ch != '/'){
+ errorStream << ACL_FORMAT_ERR_LOG_PREFIX << "Line : " << lineNumber
+ << ", Username \"" << name << "\" contains illegal characters.";
+ return false;
+ }
+ }
+ return true;
+ }
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclReader.h b/qpid/cpp/src/qpid/acl/AclReader.h
new file mode 100644
index 0000000000..24237a82ce
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclReader.h
@@ -0,0 +1,150 @@
+#ifndef QPID_ACL_ACLREADER_H
+#define QPID_ACL_ACLREADER_H
+
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include <boost/shared_ptr.hpp>
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <memory>
+#include "qpid/acl/AclData.h"
+#include "qpid/acl/Acl.h"
+#include "qpid/broker/AclModule.h"
+#include "qpid/acl/AclValidator.h"
+
+namespace qpid {
+namespace acl {
+
+class AclReader {
+ typedef std::set<std::string> nameSet;
+ typedef nameSet::const_iterator nsCitr;
+ typedef boost::shared_ptr<nameSet> nameSetPtr;
+
+ typedef std::pair<std::string, nameSetPtr> groupPair;
+ typedef std::map<std::string, nameSetPtr> groupMap;
+ typedef groupMap::const_iterator gmCitr;
+ typedef std::pair<gmCitr, bool> gmRes;
+
+ typedef std::pair<SpecProperty, std::string> propNvPair;
+ typedef std::map<SpecProperty, std::string> propMap;
+ typedef propMap::const_iterator pmCitr;
+
+ //
+ // aclRule
+ //
+ // A temporary rule created during ACL file processing.
+ //
+ class aclRule {
+ public:
+ enum objectStatus {NONE, VALUE, ALL};
+
+ AclResult res;
+ nameSet names;
+ bool actionAll; // True if action is set to keyword "all"
+ Action action; // Ignored if action is set to keyword "all"
+ objectStatus objStatus;
+ ObjectType object; // Ignored for all status values except VALUE
+ propMap props;
+ public:
+ aclRule(const AclResult r, const std::string n, const groupMap& groups); // action = "all"
+ aclRule(const AclResult r, const std::string n, const groupMap& groups, const Action a);
+ void setObjectType(const ObjectType o);
+ void setObjectTypeAll();
+ bool addProperty(const SpecProperty p, const std::string v);
+ std::string toString(); // debug aid
+ private:
+ void processName(const std::string& name, const groupMap& groups);
+ };
+ typedef boost::shared_ptr<AclData::quotaRuleSet> aclQuotaRuleSet;
+ typedef boost::shared_ptr<aclRule> aclRulePtr;
+ typedef std::vector<aclRulePtr> ruleList;
+ typedef ruleList::const_iterator rlCitr;
+
+ typedef std::vector<std::string> tokList;
+ typedef tokList::const_iterator tlCitr;
+
+ typedef std::set<std::string> keywordSet;
+ typedef keywordSet::const_iterator ksCitr;
+ typedef std::pair<std::string, std::string> nvPair; // Name-Value pair
+
+ typedef boost::shared_ptr<std::vector<acl::AclBWHostRule> > aclGlobalHostRuleSet;
+ typedef boost::shared_ptr<std::map<std::string, std::vector<acl::AclBWHostRule> > > aclUserHostRuleSet;
+
+ std::string fileName;
+ int lineNumber;
+ bool contFlag;
+ std::string groupName;
+ nameSet names;
+ groupMap groups;
+ ruleList rules;
+ AclValidator validator;
+ std::ostringstream errorStream;
+
+ public:
+ AclReader(uint16_t cliMaxConnPerUser, uint16_t cliMaxQueuesPerUser);
+ virtual ~AclReader();
+ int read(const std::string& fn, boost::shared_ptr<AclData> d); // return=0 for success
+ std::string getError();
+
+ private:
+ bool processLine(char* line);
+ void loadDecisionData(boost::shared_ptr<AclData> d);
+ int tokenize(char* line, tokList& toks);
+
+ bool processGroupLine(tokList& toks, const bool cont);
+ gmCitr addGroup(const std::string& groupName);
+ void addName(const std::string& name, nameSetPtr groupNameSet);
+ void addName(const std::string& name);
+ void printNames() const; // debug aid
+ int printNamesFieldWidth() const;
+
+ bool processAclLine(tokList& toks);
+ void printRules() const; // debug aid
+ void printConnectionRules(const std::string name, const AclData::bwHostRuleSet& rules) const;
+ void printGlobalConnectRules() const;
+ void printUserConnectRules() const;
+ bool isValidUserName(const std::string& name);
+
+ bool processQuotaLine(tokList& toks);
+ bool processQuotaLine(tokList& toks, const std::string theNoun, uint16_t maxSpec, aclQuotaRuleSet theRules);
+ bool processQuotaGroup(const std::string&, uint16_t, aclQuotaRuleSet theRules);
+ void printQuotas(const std::string theNoun, aclQuotaRuleSet theRules) const;
+
+ static bool isValidGroupName(const std::string& name);
+ static nvPair splitNameValuePair(const std::string& nvpString);
+
+ const uint16_t cliMaxConnPerUser;
+ bool connQuotaRulesExist;
+ aclQuotaRuleSet connQuota;
+
+ const uint16_t cliMaxQueuesPerUser;
+ bool queueQuotaRulesExist;
+ aclQuotaRuleSet queueQuota;
+
+ aclGlobalHostRuleSet globalHostRules;
+ aclUserHostRuleSet userHostRules;
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLREADER_H
diff --git a/qpid/cpp/src/qpid/acl/AclResourceCounter.cpp b/qpid/cpp/src/qpid/acl/AclResourceCounter.cpp
new file mode 100644
index 0000000000..2527af6375
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclResourceCounter.cpp
@@ -0,0 +1,174 @@
+/*
+ *
+ * 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 "AclResourceCounter.h"
+#include "Acl.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 approves various resource creation requests:
+// Queues
+//
+
+
+//
+//
+//
+ResourceCounter::ResourceCounter(Acl& a, uint16_t ql) :
+ acl(a), queueLimit(ql) {}
+
+ResourceCounter::~ResourceCounter() {}
+
+
+//
+// limitApproveLH
+//
+// Resource creation approver.
+// If user is under limit increment count and return true.
+// Called with lock held.
+//
+bool ResourceCounter::limitApproveLH(
+ countsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog,
+ bool enforceLimit) {
+
+ bool result(true);
+ uint16_t count;
+ countsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ count = (uint16_t)(*eRef).second;
+ result = (enforceLimit ? count < theLimit : true);
+ if (result) {
+ count += 1;
+ (*eRef).second = count;
+ }
+ } else {
+ // user not found in map
+ if (enforceLimit) {
+ if (theLimit > 0) {
+ theMap[theName] = count = 1;
+ } else {
+ count = 0;
+ result = false;
+ }
+ }
+ else {
+ // not enforcing the limit
+ theMap[theName] = count = 1;
+ }
+ }
+ if (emitLog) {
+ QPID_LOG(trace, "ACL QueueApprover user=" << theName
+ << " limit=" << theLimit
+ << " curValue=" << count
+ << " result=" << (result ? "allow" : "deny"));
+ }
+ return result;
+}
+
+
+//
+// releaseLH
+//
+// Decrement the name's count in map.
+// called with dataLock already taken
+//
+void ResourceCounter::releaseLH(countsMap_t& theMap, const std::string& theName) {
+
+ countsMap_t::iterator eRef = theMap.find(theName);
+ if (eRef != theMap.end()) {
+ uint16_t count = (uint16_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 resource counter: Queue owner for queue '" << theName
+ << "' not found in resource count pool");
+ }
+}
+
+
+//
+// approveCreateQueue
+// Count an attempted queue creation by this user.
+// Disapprove if over limit.
+//
+bool ResourceCounter::approveCreateQueue(const std::string& userId,
+ const std::string& queueName,
+ bool enforcingQueueQuotas,
+ uint16_t queueUserQuota )
+{
+ Mutex::ScopedLock locker(dataLock);
+
+ bool okByQ = limitApproveLH(queuePerUserMap, userId, queueUserQuota, true, enforcingQueueQuotas);
+
+ if (okByQ) {
+ // Queue is owned by this userId
+ queueOwnerMap[queueName] = userId;
+
+ QPID_LOG(trace, "ACL create queue approved for user '" << userId
+ << "' queue '" << queueName << "'");
+ } else {
+
+ QPID_LOG(error, "Client max queue count limit of " << queueUserQuota
+ << " exceeded by '" << userId << "' creating queue '"
+ << queueName << "'. Queue creation denied.");
+
+ acl.reportQueueLimit(userId, queueName);
+ }
+ return okByQ;
+}
+
+
+//
+// recordDestroyQueue
+// Return a destroyed queue to a user's quota
+//
+void ResourceCounter::recordDestroyQueue(const std::string& queueName)
+{
+ Mutex::ScopedLock locker(dataLock);
+
+ queueOwnerMap_t::iterator eRef = queueOwnerMap.find(queueName);
+ if (eRef != queueOwnerMap.end()) {
+ releaseLH(queuePerUserMap, (*eRef).second);
+
+ queueOwnerMap.erase(eRef);
+ } else {
+ QPID_LOG(notice, "ACL resource counter: Queue '" << queueName
+ << "' not found in queue owner map");
+ }
+}
+
+}} // namespace qpid::acl
diff --git a/qpid/cpp/src/qpid/acl/AclResourceCounter.h b/qpid/cpp/src/qpid/acl/AclResourceCounter.h
new file mode 100644
index 0000000000..8809f73b18
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclResourceCounter.h
@@ -0,0 +1,78 @@
+#ifndef QPID_ACL_RESOURCECOUNTER_H
+#define QPID_ACL_RESOURCECOUNTER_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/sys/Mutex.h"
+
+#include <map>
+
+namespace qpid {
+
+namespace acl {
+class Acl;
+
+ /**
+ * Approve or disapprove resource creation requests
+ */
+class ResourceCounter
+{
+private:
+ typedef std::map<std::string, uint32_t> countsMap_t;
+ typedef std::map<std::string, std::string> queueOwnerMap_t;
+
+ Acl& acl;
+ uint16_t queueLimit;
+ qpid::sys::Mutex dataLock;
+
+ /** Records queueName-queueUserId */
+ queueOwnerMap_t queueOwnerMap;
+
+ /** Records queue-by-owner counts */
+ countsMap_t queuePerUserMap;
+
+ /** Return approval for proposed resource creation */
+ bool limitApproveLH(countsMap_t& theMap,
+ const std::string& theName,
+ uint16_t theLimit,
+ bool emitLog,
+ bool enforceLimit);
+
+ /** Release a connection */
+ void releaseLH(countsMap_t& theMap,
+ const std::string& theName);
+
+public:
+ ResourceCounter(Acl& acl, uint16_t ql);
+ ~ResourceCounter();
+
+ // Queue counting
+ bool approveCreateQueue(const std::string& userId,
+ const std::string& queueName,
+ bool enforcingQueueQuotas,
+ uint16_t queueUserQuota );
+ void recordDestroyQueue(const std::string& queueName);
+};
+
+}} // namespace qpid::acl
+
+#endif /*!QPID_ACL_RESOURCECOUNTER_H*/
diff --git a/qpid/cpp/src/qpid/acl/AclTopicMatch.h b/qpid/cpp/src/qpid/acl/AclTopicMatch.h
new file mode 100644
index 0000000000..654d1d63d4
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclTopicMatch.h
@@ -0,0 +1,89 @@
+#ifndef QPID_ACL_TOPIC_MATCH_H
+#define QPID_ACL_TOPIC_MATCH_H
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/TopicKeyNode.h"
+#include "qpid/broker/TopicExchange.h"
+#include "qpid/log/Statement.h"
+#include "boost/shared_ptr.hpp"
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace broker {
+
+// Class for executing topic exchange routing key matching rules in
+// Acl code. Allows or denies users publishing to an exchange.
+class TopicExchange::TopicExchangeTester {
+
+class boundNode;
+
+public:
+ typedef std::vector<bool> BindingVec;
+ typedef TopicKeyNode<boundNode> TestBindingNode;
+
+private:
+ // Target class to be bound into topic key tree
+ class boundNode {
+ public:
+ BindingVec bindingVector;
+ };
+
+ // Acl binding trees contain only one node each.
+ // When the iterator sees it then the node matches the caller's spec.
+ class TestFinder : public TestBindingNode::TreeIterator {
+ public:
+ TestFinder(BindingVec& m) : bv(m), found(false) {};
+ ~TestFinder() {};
+ bool visit(TestBindingNode& /*node*/) {
+ assert(!found);
+ found = true;
+ return true;
+ }
+ BindingVec& bv;
+ bool found;
+ };
+
+public:
+ TopicExchangeTester() {};
+ ~TopicExchangeTester() {};
+ bool addBindingKey(const std::string& bKey) {
+ std::string routingPattern = normalize(bKey);
+ boundNode *mbn = bindingTree.add(routingPattern);
+ if (mbn) {
+ // push a dummy binding to mark this node as "non-leaf"
+ mbn->bindingVector.push_back(true);
+ return true;
+ }
+ return false;
+ }
+
+ bool findMatches(const std::string& rKey, BindingVec& matches) {
+ TestFinder testFinder(matches);
+ bindingTree.iterateMatch( rKey, testFinder );
+ return testFinder.found;
+ }
+
+private:
+ TestBindingNode bindingTree;
+};
+}} // namespace qpid::broker
+
+#endif // QPID_ACL_TOPIC_MATCH_H
diff --git a/qpid/cpp/src/qpid/acl/AclValidator.cpp b/qpid/cpp/src/qpid/acl/AclValidator.cpp
new file mode 100644
index 0000000000..f905b4aca5
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclValidator.cpp
@@ -0,0 +1,536 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/acl/AclValidator.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/acl/AclLexer.h"
+#include "qpid/Exception.h"
+#include "qpid/log/Statement.h"
+#include "qpid/sys/IntegerTypes.h"
+#include "qpid/StringUtils.h"
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <numeric>
+#include <sstream>
+#include <iomanip>
+
+namespace qpid {
+namespace acl {
+
+ AclValidator::IntPropertyType::IntPropertyType(int64_t i,int64_t j) : min(i), max(j){
+ }
+
+ bool AclValidator::IntPropertyType::validate(const std::string& val) {
+ int64_t v;
+ try
+ {
+ v = boost::lexical_cast<int64_t>(val);
+ }catch(const boost::bad_lexical_cast&){
+ return 0;
+ }
+
+ if (v < min || v >= max){
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ std::string AclValidator::IntPropertyType::allowedValues() {
+ return "values should be between " +
+ boost::lexical_cast<std::string>(min) + " and " +
+ boost::lexical_cast<std::string>(max);
+ }
+
+ AclValidator::EnumPropertyType::EnumPropertyType(std::vector<std::string>& allowed): values(allowed){
+ }
+
+ bool AclValidator::EnumPropertyType::validate(const std::string& val) {
+ for (std::vector<std::string>::iterator itr = values.begin(); itr != values.end(); ++itr ){
+ if (val.compare(*itr) == 0){
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+ std::string AclValidator::EnumPropertyType::allowedValues() {
+ std::ostringstream oss;
+ oss << "possible values are one of { ";
+ for (std::vector<std::string>::iterator itr = values.begin(); itr != values.end(); itr++ ){
+ oss << "'" << *itr << "' ";
+ }
+ oss << "}";
+ return oss.str();
+ }
+
+ AclValidator::AclValidator() : propertyIndex(1) {
+ validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZELOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXQUEUESIZEUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTLOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXQUEUECOUNTUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILESIZELOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILESIZEUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILECOUNTLOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXFILECOUNTUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXPAGESLOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXPAGESUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXPAGEFACTORLOWERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ validators.insert(Validator(acl::SPECPROP_MAXPAGEFACTORUPPERLIMIT,
+ boost::shared_ptr<PropertyType>(
+ new IntPropertyType(0,std::numeric_limits<int64_t>::max()))));
+
+ std::string policyTypes[] = {"ring", "self-destruct", "reject"};
+ std::vector<std::string> v(policyTypes, policyTypes + sizeof(policyTypes) / sizeof(std::string));
+ validators.insert(Validator(acl::SPECPROP_POLICYTYPE,
+ boost::shared_ptr<PropertyType>(
+ new EnumPropertyType(v))));
+
+ // Insert allowed action/object/property sets (generated manually 20140712)
+#define RP registerProperties
+ RP( "Broker::getTimestampConfig",
+ "User querying message timestamp setting ",
+ ACT_ACCESS, OBJ_BROKER);
+ RP( "ExchangeHandlerImpl::query",
+ "AMQP 0-10 protocol received 'query' ",
+ ACT_ACCESS, OBJ_EXCHANGE, "name");
+ RP( "ExchangeHandlerImpl::bound",
+ "AMQP 0-10 query binding ",
+ ACT_ACCESS, OBJ_EXCHANGE, "name queuename routingkey");
+ RP( "ExchangeHandlerImpl::declare",
+ "AMQP 0-10 exchange declare ",
+ ACT_ACCESS, OBJ_EXCHANGE, "name type alternate durable autodelete");
+ RP( "Authorise::access",
+ "AMQP 1.0 exchange access ",
+ ACT_ACCESS, OBJ_EXCHANGE, "name type durable");
+ RP( "Authorise::access",
+ "AMQP 1.0 node resolution ",
+ ACT_ACCESS, OBJ_EXCHANGE, "name");
+ RP( "ManagementAgent::handleMethodRequest",
+ "Management method request ",
+ ACT_ACCESS, OBJ_METHOD, "name schemapackage schemaclass");
+ RP( "ManagementAgent::authorizeAgentMessage",
+ "Management agent method request ",
+ ACT_ACCESS, OBJ_METHOD, "name schemapackage schemaclass");
+ RP( "ManagementAgent::handleGetQuery",
+ "Management agent query ",
+ ACT_ACCESS, OBJ_QUERY, "name schemaclass");
+ RP( "Broker::queryQueue",
+ "QMF 'query queue' method ",
+ ACT_ACCESS, OBJ_QUEUE, "name");
+ RP( "QueueHandlerImpl::query",
+ "AMQP 0-10 query ",
+ ACT_ACCESS, OBJ_QUEUE, "name");
+ RP( "QueueHandlerImpl::declare",
+ "AMQP 0-10 queue declare ",
+ ACT_ACCESS, OBJ_QUEUE, "name alternate durable exclusive autodelete policytype maxqueuecount maxqueuesize");
+ RP( "Authorise::access",
+ "AMQP 1.0 queue access ",
+ ACT_ACCESS, OBJ_QUEUE, "name alternate durable exclusive autodelete policytype maxqueuecount maxqueuesize");
+ RP( "Authorise::access",
+ "AMQP 1.0 node resolution ",
+ ACT_ACCESS, OBJ_QUEUE, "name");
+ RP( "Broker::bind",
+ "AMQP 0-10 or QMF bind request ",
+ ACT_BIND, OBJ_EXCHANGE, "name queuename routingkey");
+ RP( "Authorise::outgoing",
+ "AMQP 1.0 new outgoing link from exchange",
+ ACT_BIND, OBJ_EXCHANGE, "name queuename routingkey");
+ RP( "MessageHandlerImpl::subscribe",
+ "AMQP 0-10 subscribe request ",
+ ACT_CONSUME, OBJ_QUEUE, "name");
+ RP( "Authorise::outgoing",
+ "AMQP 1.0 new outgoing link from queue ",
+ ACT_CONSUME, OBJ_QUEUE, "name");
+ RP( "ConnectionHandler",
+ "TCP/IP connection creation ",
+ ACT_CREATE, OBJ_CONNECTION, "host");
+ RP( "Broker::createExchange",
+ "Create exchange ",
+ ACT_CREATE, OBJ_EXCHANGE, "name type alternate durable autodelete");
+ RP( "ConnectionHandler::Handler::open",
+ "Interbroker link creation ",
+ ACT_CREATE, OBJ_LINK);
+ RP( "Authorise::interlink",
+ "Interbroker link creation ",
+ ACT_CREATE, OBJ_LINK);
+ RP( "Broker::createQueue",
+ "Create queue ",
+ ACT_CREATE, OBJ_QUEUE, "name alternate durable exclusive autodelete policytype paging maxpages maxpagefactor maxqueuecount maxqueuesize maxfilecount maxfilesize");
+ RP( "Broker::deleteExchange",
+ "Delete exchange ",
+ ACT_DELETE, OBJ_EXCHANGE, "name type alternate durable");
+ RP( "Broker::deleteQueue",
+ "Delete queue ",
+ ACT_DELETE, OBJ_QUEUE, "name alternate durable exclusive autodelete policytype");
+ RP( "Broker::queueMoveMessages",
+ "Management 'move queue' request ",
+ ACT_MOVE, OBJ_QUEUE, "name queuename");
+ RP( "SemanticState::route",
+ "AMQP 0-10 received message processing ",
+ ACT_PUBLISH, OBJ_EXCHANGE, "name routingkey");
+ RP( "Authorise::incoming",
+ "AMQP 1.0 establish sender link to queue ",
+ ACT_PUBLISH, OBJ_EXCHANGE, "routingkey");
+ RP( "Authorise::route",
+ "AMQP 1.0 received message processing ",
+ ACT_PUBLISH, OBJ_EXCHANGE, "name routingkey");
+ RP( "Queue::ManagementMethod",
+ "Management 'purge queue' request ",
+ ACT_PURGE, OBJ_QUEUE, "name");
+ RP( "QueueHandlerImpl::purge",
+ "Management 'purge queue' request ",
+ ACT_PURGE, OBJ_QUEUE, "name");
+ RP( "Broker::queueRedirect",
+ "Management 'redirect queue' request ",
+ ACT_REDIRECT,OBJ_QUEUE, "name queuename");
+ RP( "Queue::ManagementMethod",
+ "Management 'reroute queue' request ",
+ ACT_REROUTE, OBJ_QUEUE, "name exchangename");
+ RP( "Broker::unbind",
+ "Management 'unbind exchange' request ",
+ ACT_UNBIND, OBJ_EXCHANGE, "name queuename routingkey");
+ RP( "Broker::setTimestampConfig",
+ "User modifying message timestamp setting",
+ ACT_UPDATE, OBJ_BROKER);
+ }
+
+ AclValidator::~AclValidator(){
+ }
+
+ /* Iterate through the data model and validate the parameters. */
+ void AclValidator::validate(boost::shared_ptr<AclData> d) {
+
+ for (unsigned int cnt=0; cnt< qpid::acl::ACTIONSIZE; cnt++){
+
+ if (d->actionList[cnt]){
+
+ for (unsigned int cnt1=0; cnt1< qpid::acl::OBJECTSIZE; cnt1++){
+
+ if (d->actionList[cnt][cnt1]){
+
+ std::for_each(d->actionList[cnt][cnt1]->begin(),
+ d->actionList[cnt][cnt1]->end(),
+ boost::bind(&AclValidator::validateRuleSet, this, _1));
+ }
+ }
+ }
+ }
+ }
+
+ void AclValidator::validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules){
+ std::for_each(rules.second.begin(),
+ rules.second.end(),
+ boost::bind(&AclValidator::validateRule, this, _1));
+ }
+
+ void AclValidator::validateRule(qpid::acl::AclData::Rule& rule){
+ std::for_each(rule.props.begin(),
+ rule.props.end(),
+ boost::bind(&AclValidator::validateProperty, this, _1));
+ }
+
+ void AclValidator::validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop){
+ ValidatorItr itr = validators.find(prop.first);
+ if (itr != validators.end()){
+ QPID_LOG(debug,"ACL: Found validator for property '" << acl::AclHelper::getPropertyStr(itr->first)
+ << "'. " << itr->second->allowedValues());
+
+ if (!itr->second->validate(prop.second)){
+ QPID_LOG(debug, "ACL: Property failed validation. '" << prop.second << "' is not a valid value for '"
+ << AclHelper::getPropertyStr(prop.first) << "'");
+
+ throw Exception( prop.second + " is not a valid value for '" +
+ AclHelper::getPropertyStr(prop.first) + "', " +
+ itr->second->allowedValues());
+ }
+ }
+ }
+
+ /**
+ * validateAllowedProperties
+ * verify that at least one lookup definition can satisfy this
+ * action/object/props tuple.
+ * Return false and conditionally emit a warning log entry if the
+ * incoming definition can not be matched.
+ */
+ bool AclValidator::validateAllowedProperties(qpid::acl::Action action,
+ qpid::acl::ObjectType object,
+ const AclData::specPropertyMap& props,
+ bool emitLog) const {
+ // No rules defined means no match
+ if (!allowedSpecProperties[action][object].get()) {
+ if (emitLog) {
+ QPID_LOG(warning, "ACL rule ignored: Broker never checks for rules with action: '"
+ << AclHelper::getActionStr(action) << "' and object: '"
+ << AclHelper::getObjectTypeStr(object) << "'");
+ }
+ return false;
+ }
+ // two empty property sets is a match
+ if (allowedSpecProperties[action][object]->size() == 0) {
+ if ((props.size() == 0) ||
+ (props.size() == 1 && props.find(acl::SPECPROP_NAME) != props.end())) {
+ return true;
+ }
+ }
+ // Scan vector of rules looking for one that matches all properties
+ bool validRuleFound = false;
+ for (std::vector<AclData::Rule>::const_iterator
+ ruleItr = allowedSpecProperties[action][object]->begin();
+ ruleItr != allowedSpecProperties[action][object]->end() && !validRuleFound;
+ ruleItr++) {
+ // Scan one rule
+ validRuleFound = true;
+ for(AclData::specPropertyMapItr itr = props.begin();
+ itr != props.end();
+ itr++) {
+ if ((*itr).first != acl::SPECPROP_NAME &&
+ ruleItr->props.find((*itr).first) ==
+ ruleItr->props.end()) {
+ // Test property not found in this rule
+ validRuleFound = false;
+ break;
+ }
+ }
+ }
+ if (!validRuleFound) {
+ if (emitLog) {
+ QPID_LOG(warning, "ACL rule ignored: Broker checks for rules with action: '"
+ << AclHelper::getActionStr(action) << "' and object: '"
+ << AclHelper::getObjectTypeStr(object)
+ << "' but will never match with property set: "
+ << AclHelper::propertyMapToString(&props));
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return a list of indexes of definitions that this lookup might match
+ */
+ void AclValidator::findPossibleLookupMatch(qpid::acl::Action action,
+ qpid::acl::ObjectType object,
+ const AclData::specPropertyMap& props,
+ std::vector<int>& result) const {
+ if (!allowedSpecProperties[action][object].get()) {
+ return;
+ } else {
+ // Scan vector of rules returning the indexes of all that match
+ bool validRuleFound;
+ for (std::vector<AclData::Rule>::const_iterator
+ ruleItr = allowedSpecProperties[action][object]->begin();
+ ruleItr != allowedSpecProperties[action][object]->end();
+ ruleItr++) {
+ // Scan one rule
+ validRuleFound = true;
+ for(AclData::specPropertyMapItr
+ itr = props.begin(); itr != props.end(); itr++) {
+ if ((*itr).first != acl::SPECPROP_NAME &&
+ ruleItr->props.find((*itr).first) ==
+ ruleItr->props.end()) {
+ // Test property not found in this rule
+ validRuleFound = false;
+ break;
+ }
+ }
+ if (validRuleFound) {
+ result.push_back(ruleItr->rawRuleNum);
+ }
+ }
+ }
+ return;
+ }
+
+ /**
+ * Emit trace log of original property definitions
+ */
+ void AclValidator::tracePropertyDefs() {
+ QPID_LOG(trace, "ACL: Definitions of action, object, (allowed properties) lookups");
+ for (int iA=0; iA<acl::ACTIONSIZE; iA++) {
+ for (int iO=0; iO<acl::OBJECTSIZE; iO++) {
+ if (allowedSpecProperties[iA][iO].get()) {
+ for (std::vector<AclData::Rule>::const_iterator
+ ruleItr = allowedSpecProperties[iA][iO]->begin();
+ ruleItr != allowedSpecProperties[iA][iO]->end();
+ ruleItr++) {
+ std::string pstr;
+ for (AclData::specPropertyMapItr pMItr = ruleItr->props.begin();
+ pMItr != ruleItr->props.end();
+ pMItr++) {
+ pstr += AclHelper::getPropertyStr((SpecProperty) pMItr-> first);
+ pstr += ",";
+ }
+ QPID_LOG(trace, "ACL: Lookup "
+ << std::setfill(' ') << std::setw(2)
+ << ruleItr->rawRuleNum << ": "
+ << ruleItr->lookupHelp << " "
+ << std::setfill(' ') << std::setw(acl::ACTION_STR_WIDTH +1) << std::left
+ << AclHelper::getActionStr(acl::Action(iA))
+ << std::setfill(' ') << std::setw(acl::OBJECTTYPE_STR_WIDTH) << std::left
+ << AclHelper::getObjectTypeStr(acl::ObjectType(iO))
+ << " (" << pstr.substr(0, pstr.length()-1) << ")");
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Construct a record of all the calls that the broker will
+ * make to acl::authorize and the properties for each call.
+ * From that create the list of all the spec properties that
+ * users are then allowed to specify in acl rule files.
+ */
+ void AclValidator::registerProperties(
+ const std::string& source,
+ const std::string& description,
+ Action action,
+ ObjectType object,
+ const std::string& properties) {
+ if (!allowedProperties[action][object].get()) {
+ boost::shared_ptr<std::set<Property> > t1(new std::set<Property>());
+ allowedProperties[action][object] = t1;
+ boost::shared_ptr<std::vector<AclData::Rule> > t2(new std::vector<AclData::Rule>());
+ allowedSpecProperties[action][object] = t2;
+ }
+ std::vector<std::string> props = split(properties, " ");
+ AclData::specPropertyMap spm;
+ for (size_t i=0; i<props.size(); i++) {
+ Property prop = AclHelper::getProperty(props[i]);
+ allowedProperties[action][object]->insert(prop);
+ // Given that the broker will be calling with this property,
+ // determine what user rule settings are allowed.
+ switch (prop) {
+ // Cases where broker and Acl file share property name and meaning
+ case PROP_NAME:
+ spm[SPECPROP_NAME]="";
+ break;
+ case PROP_DURABLE:
+ spm[SPECPROP_DURABLE]="";
+ break;
+ case PROP_OWNER:
+ spm[SPECPROP_OWNER]="";
+ break;
+ case PROP_ROUTINGKEY:
+ spm[SPECPROP_ROUTINGKEY]="";
+ break;
+ case PROP_AUTODELETE:
+ spm[SPECPROP_AUTODELETE]="";
+ break;
+ case PROP_EXCLUSIVE:
+ spm[SPECPROP_EXCLUSIVE]="";
+ break;
+ case PROP_TYPE:
+ spm[SPECPROP_TYPE]="";
+ break;
+ case PROP_ALTERNATE:
+ spm[SPECPROP_ALTERNATE]="";
+ break;
+ case PROP_QUEUENAME:
+ spm[SPECPROP_QUEUENAME]="";
+ break;
+ case PROP_EXCHANGENAME:
+ spm[SPECPROP_EXCHANGENAME]="";
+ break;
+ case PROP_SCHEMAPACKAGE:
+ spm[SPECPROP_SCHEMAPACKAGE]="";
+ break;
+ case PROP_SCHEMACLASS:
+ spm[SPECPROP_SCHEMACLASS]="";
+ break;
+ case PROP_POLICYTYPE:
+ spm[SPECPROP_POLICYTYPE]="";
+ break;
+ case PROP_PAGING:
+ spm[SPECPROP_PAGING]="";
+ break;
+ case PROP_HOST:
+ spm[SPECPROP_HOST]="";
+ break;
+ // Cases where broker supplies a property but Acl has upper/lower limit for it
+ case PROP_MAXPAGES:
+ spm[SPECPROP_MAXPAGESLOWERLIMIT]="";
+ spm[SPECPROP_MAXPAGESUPPERLIMIT]="";
+ break;
+ case PROP_MAXPAGEFACTOR:
+ spm[SPECPROP_MAXPAGEFACTORLOWERLIMIT]="";
+ spm[SPECPROP_MAXPAGEFACTORUPPERLIMIT]="";
+ break;
+ case PROP_MAXQUEUESIZE:
+ spm[SPECPROP_MAXQUEUESIZELOWERLIMIT]="";
+ spm[SPECPROP_MAXQUEUESIZEUPPERLIMIT]="";
+ break;
+ case PROP_MAXQUEUECOUNT:
+ spm[SPECPROP_MAXQUEUECOUNTLOWERLIMIT]="";
+ spm[SPECPROP_MAXQUEUECOUNTUPPERLIMIT]="";
+ break;
+ case PROP_MAXFILESIZE:
+ spm[SPECPROP_MAXFILESIZELOWERLIMIT]="";
+ spm[SPECPROP_MAXFILESIZEUPPERLIMIT]="";
+ break;
+ case PROP_MAXFILECOUNT:
+ spm[SPECPROP_MAXFILECOUNTLOWERLIMIT]="";
+ spm[SPECPROP_MAXFILECOUNTUPPERLIMIT]="";
+ break;
+ default:
+ throw Exception( "acl::RegisterProperties no case for property: " +
+ AclHelper::getPropertyStr(prop) );
+ }
+ }
+ AclData::Rule someProps(propertyIndex, acl::ALLOW, spm, source, description);
+ propertyIndex++;
+ allowedSpecProperties[action][object]->push_back(someProps);
+ }
+
+}}
diff --git a/qpid/cpp/src/qpid/acl/AclValidator.h b/qpid/cpp/src/qpid/acl/AclValidator.h
new file mode 100644
index 0000000000..8f555797c2
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/AclValidator.h
@@ -0,0 +1,106 @@
+#ifndef QPID_ACL_ACLVALIDATOR_H
+#define QPID_ACL_ACLVALIDATOR_H
+
+
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * 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.
+ *
+ */
+
+#include "qpid/broker/AclModule.h"
+#include "qpid/acl/AclData.h"
+#include "qpid/sys/IntegerTypes.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/concept_check.hpp>
+#include <vector>
+#include <sstream>
+
+namespace qpid {
+namespace acl {
+
+class AclValidator {
+
+ /* Base Property */
+ class PropertyType{
+
+ public:
+ virtual ~PropertyType(){};
+ virtual bool validate(const std::string& val)=0;
+ virtual std::string allowedValues()=0;
+ };
+
+ class IntPropertyType : public PropertyType{
+ int64_t min;
+ int64_t max;
+
+ public:
+ IntPropertyType(int64_t min,int64_t max);
+ virtual ~IntPropertyType (){};
+ virtual bool validate(const std::string& val);
+ virtual std::string allowedValues();
+ };
+
+ class EnumPropertyType : public PropertyType{
+ std::vector<std::string> values;
+
+ public:
+ EnumPropertyType(std::vector<std::string>& allowed);
+ virtual ~EnumPropertyType (){};
+ virtual bool validate(const std::string& val);
+ virtual std::string allowedValues();
+ };
+
+ typedef std::pair<acl::SpecProperty,boost::shared_ptr<PropertyType> > Validator;
+ typedef std::map<acl::SpecProperty,boost::shared_ptr<PropertyType> > ValidatorMap;
+ typedef ValidatorMap::iterator ValidatorItr;
+ typedef boost::shared_ptr<std::set<Property> > AllowedProperties [ACTIONSIZE][OBJECTSIZE];
+ typedef boost::shared_ptr<std::vector<AclData::Rule> > AllowedSpecProperties[ACTIONSIZE][OBJECTSIZE];
+
+ ValidatorMap validators;
+ AllowedProperties allowedProperties;
+ AllowedSpecProperties allowedSpecProperties;
+
+public:
+
+ void validateRuleSet(std::pair<const std::string, qpid::acl::AclData::ruleSet>& rules);
+ void validateRule(qpid::acl::AclData::Rule& rule);
+ void validateProperty(std::pair<const qpid::acl::SpecProperty, std::string>& prop);
+ void validate(boost::shared_ptr<AclData> d);
+ bool validateAllowedProperties(qpid::acl::Action action,
+ qpid::acl::ObjectType object,
+ const AclData::specPropertyMap& props,
+ bool emitLog) const;
+ void findPossibleLookupMatch(qpid::acl::Action action,
+ qpid::acl::ObjectType object,
+ const AclData::specPropertyMap& props,
+ std::vector<int>& result) const;
+ void tracePropertyDefs();
+
+ AclValidator();
+ ~AclValidator();
+
+private:
+ void registerProperties(const std::string& source,
+ const std::string& description,
+ Action action,
+ ObjectType object,
+ const std::string& properties = "");
+ int propertyIndex;
+};
+
+}} // namespace qpid::acl
+
+#endif // QPID_ACL_ACLVALIDATOR_H
diff --git a/qpid/cpp/src/qpid/acl/management-schema.xml b/qpid/cpp/src/qpid/acl/management-schema.xml
new file mode 100644
index 0000000000..2ac20bb324
--- /dev/null
+++ b/qpid/cpp/src/qpid/acl/management-schema.xml
@@ -0,0 +1,85 @@
+<schema package="org.apache.qpid.acl">
+
+<!--
+ * Copyright (c) 2008 The Apache Software Foundation
+ *
+ * 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.
+-->
+
+ <class name="Acl">
+ <property name="brokerRef" type="objId" references="org.apache.qpid.broker:Broker" access="RO" index="y" parentRef="y"/>
+ <property name="policyFile" type="lstr" access="RO" desc="Name of the policy file"/>
+ <property name="enforcingAcl" type="bool" access="RO" desc="Currently Enforcing ACL"/>
+ <property name="transferAcl" type="bool" access="RO" desc="Any transfer ACL rules in force"/>
+ <property name="lastAclLoad" type="absTime" access="RO" desc="Timestamp of last successful load of ACL"/>
+ <property name="maxConnections" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxConnectionsPerIp" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxConnectionsPerUser" type="uint16" access="RO" desc="Maximum allowed connections"/>
+ <property name="maxQueuesPerUser" type="uint16" access="RO" desc="Maximum allowed queues"/>
+ <statistic name="aclDenyCount" type="count64" unit="request" desc="Number of ACL requests denied"/>
+ <statistic name="connectionDenyCount" type="count64" unit="connection" desc="Number of connections denied"/>
+ <statistic name="queueQuotaDenyCount" type="count64" unit="queue" desc="Number of queue creations denied"/>
+
+ <method name="reloadACLFile" desc="Reload the ACL file"/>
+
+ <!--
+ Lookup is a general object lookup
+ User Name
+ Action
+ Object
+ Object Name
+ Property Map consisting of <"name" "value"> string pairs.
+ -->
+ <method name="Lookup" desc="Lookup: user action object [objectName [propertyMap]]">
+ <arg name="userId" dir="I" type="lstr"/>
+ <arg name="action" dir="I" type="lstr"/>
+ <arg name="object" dir="I" type="lstr"/>
+ <arg name="objectName" dir="I" type="lstr"/>
+ <arg name="propertyMap" dir="I" type="map"/>
+ <arg name="result" dir="O" type="lstr"/>
+ </method>
+
+ <!--
+ LookupPublish is a specific lookup for a PUBLISH EXCHANGE fastpath
+ User Name
+ Exchange Name
+ Routing Key
+ -->
+ <method name="LookupPublish" desc="Lookup PUBLISH EXCHANGE: user exchangeName routingKey">
+ <arg name="userId" dir="I" type="lstr"/>
+ <arg name="exchangeName" dir="I" type="lstr"/>
+ <arg name="routingKey" dir="I" type="lstr"/>
+ <arg name="result" dir="O" type="lstr"/>
+ </method>
+
+ </class>
+
+ <eventArguments>
+ <arg name="action" type="sstr"/>
+ <arg name="arguments" type="map"/>
+ <arg name="objectName" type="sstr"/>
+ <arg name="objectType" type="sstr"/>
+ <arg name="reason" type="lstr"/>
+ <arg name="userId" type="sstr"/>
+ <arg name="clientAddr" type="sstr"/>
+ <arg name="queueName" type="sstr"/>
+ </eventArguments>
+
+ <event name="allow" sev="inform" args="userId, action, objectType, objectName, arguments"/>
+ <event name="deny" sev="notice" args="userId, action, objectType, objectName, arguments"/>
+ <event name="connectionDeny" sev="notice" args="userId, clientAddr"/>
+ <event name="queueQuotaDeny" sev="notice" args="userId, queueName"/>
+ <event name="fileLoaded" sev="inform" args="userId"/>
+ <event name="fileLoadFailed" sev="error" args="userId, reason"/>
+
+</schema>