summaryrefslogtreecommitdiff
path: root/cpp/src/qpid/acl/AclReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/qpid/acl/AclReader.cpp')
-rw-r--r--cpp/src/qpid/acl/AclReader.cpp461
1 files changed, 446 insertions, 15 deletions
diff --git a/cpp/src/qpid/acl/AclReader.cpp b/cpp/src/qpid/acl/AclReader.cpp
index 0a9517bc76..e8828a55bb 100644
--- a/cpp/src/qpid/acl/AclReader.cpp
+++ b/cpp/src/qpid/acl/AclReader.cpp
@@ -18,47 +18,274 @@
#include "qpid/acl/AclReader.h"
+#include <cctype>
#include <cstring>
-//#include <iostream> // debug
#include <fstream>
+#include <sstream>
+#include "qpid/log/Statement.h"
+#include "qpid/Exception.h"
+
+#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 Property p, const std::string v) {
+ return props.insert(propNvPair(p, v)).second;
+}
+
+bool AclReader::aclRule::validate(const AclHelper::objectMapPtr& /*validationMap*/) {
+ // TODO - invalid rules won't ever be called in real life...
+ return true;
+}
+
+// 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");
+ int cnt = rules.size();
+ bool foundmode = false;
+ for (rlCitr i=rules.end()-1; cnt; i--,cnt--) {
+ QPID_LOG(debug, "ACL Processing " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString());
+
+ if (!foundmode && (*i)->actionAll && (*i)->names.size()==1 && (*((*i)->names.begin())).compare("*")==0 ){
+ d->decisionMode = (*i)->res;
+ QPID_LOG(debug, "ACL FoundMode " << AclHelper::getAclResultStr(d->decisionMode));
+ foundmode=true;
+ }else{
+ AclData::rule rule((*i)->props);
+ bool addrule= true;
+
+ switch ((*i)->res)
+ {
+ case qpid::acl::ALLOWLOG:
+ rule.log = true;
+ if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG)
+ rule.logOnly = true;
+ break;
+ case qpid::acl::ALLOW:
+ if (d->decisionMode == qpid::acl::ALLOW || d->decisionMode == qpid::acl::ALLOWLOG)
+ addrule = false;
+ break;
+ case qpid::acl::DENYLOG:
+ rule.log = true;
+ if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG)
+ rule.logOnly = true;
+ break;
+ case qpid::acl::DENY:
+ if (d->decisionMode == qpid::acl::DENY || d->decisionMode == qpid::acl::DENYLOG)
+ addrule = false;
+ break;
+ default:
+ throw Exception("Invalid ACL Result loading rules.");
+ }
+
+
+ // Action -> Object -> map<user -> set<Rule> >
+ if (addrule){
+ for (int acnt= ((*i)->actionAll?0:(*i)->action);
+ acnt< acl::ACTIONSIZE; (*i)->actionAll?acnt++:acnt=acl::ACTIONSIZE ) {
+
+ if (acnt == acl::PUBLISH) d->transferAcl = true; // we have transfer ACL
+
+ QPID_LOG(debug, "ACL Adding action:" << 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;
+ }
+
+ // optimize this loop to limit to valid options only!!
+ for (int ocnt= ((*i)->objStatus!=aclRule::VALUE ?0:(*i)->object);
+ ocnt< acl::OBJECTSIZE;
+ (*i)->objStatus!=aclRule::VALUE?ocnt++:ocnt=acl::OBJECTSIZE ) {
+
+ QPID_LOG(debug, "ACL Adding object:" << AclHelper::getObjectTypeStr((ObjectType)ocnt) );
+
+ //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("*")==0 ) allNames = true;
+
+ for (nsCitr itr = (allNames?names.begin():(*i)->names.begin());
+ itr != (allNames?names.end():(*i)->names.end()); itr++) {
+ AclData::actObjItr itrRule = d->actionList[acnt][ocnt]->find(*itr);
+ if (itrRule == d->actionList[acnt][ocnt]->end()) {
+ QPID_LOG(debug, "ACL Adding rule & user:" << *itr);
+ AclData::ruleSet rSet;
+ rSet.push_back(rule);
+ d->actionList[acnt][ocnt]->insert(make_pair( std::string(*itr) , rSet) );
+ }else{
+
+ // TODO add code to check for dead rules
+ // allow peter create queue name=tmp <-- dead rule!!
+ // allow peter create queue
+
+ itrRule->second.push_back(rule);
+ QPID_LOG(debug, "ACL Adding rule to user:" << *itr);
+ }
+ }
+
+ }
+
+ }
+ }else{
+ QPID_LOG(debug, "ACL Skipping based on Mode:" << AclHelper::getAclResultStr(d->decisionMode) );
+ }
+ }
+
+ }
+
+
+}
+
+
+
+
+void AclReader::aclRule::processName(const std::string& name, const groupMap& groups) {
+ if (name.compare("all") == 0) {
+ names.insert("*");
+ } else {
+ gmCitr itr = groups.find(name);
+ if (itr == groups.end()) {
+ names.insert(name);
+ } else {
+ names.insert(itr->second->begin(), itr->second->end());
+ }
+ }
+}
+
+AclReader::AclReader() : lineNumber(0), contFlag(false), validationMap(new AclHelper::objectMap) {
+ AclHelper::loadValidationMap(validationMap);
+}
+
+AclReader::~AclReader() {}
+
int AclReader::read(const std::string& fn, boost::shared_ptr<AclData> d) {
-//std::cout << "AclReader::read(" << fn << ")" << std::endl << std::flush;
+ fileName = fn;
+ lineNumber = 0;
char buff[1024];
std::ifstream ifs(fn.c_str(), std::ios_base::in);
if (!ifs.good()) {
- // error/exception - file open error
+ QPID_LOG(error, "Unable to open ACL file \"" << fn << "\": eof=" << (ifs.eof()?"T":"F") << "; fail=" << (ifs.fail()?"T":"F") << "; bad=" << (ifs.bad()?"T":"F"));
return -1;
}
try {
+ bool err = false;
while (ifs.good()) {
ifs.getline(buff, 1024);
- processLine(buff, d);
+ lineNumber++;
+ if (std::strlen(buff) > 0 && buff[0] != '#') // Ignore blank lines and comments
+ err |= !processLine(buff);
+ }
+ if (!ifs.eof())
+ {
+ QPID_LOG(error, "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, "Read ACL file \"" << fn << "\"");
+ } catch (const std::exception& e) {
+ QPID_LOG(error, "Unable to read ACL file \"" << fn << "\": " << e.what());
+ ifs.close();
+ return -4;
} catch (...) {
- // error/exception - file read/processing error
+ QPID_LOG(error, "Unable to read ACL file \"" << fn << "\": Unknown exception");
ifs.close();
- return -2;
+ return -5;
}
+ printNames();
+ printRules();
+ loadDecisionData(d);
+
return 0;
}
+bool AclReader::processLine(char* line) {
+ bool ret = false;
+ std::vector<std::string> toks;
+
+ // Check for continuation
+ char* contCharPtr = std::strrchr(line, '\\');
+ bool cont = contCharPtr != 0;
+ if (cont) *contCharPtr = 0;
-void AclReader::processLine(char* line, boost::shared_ptr<AclData> /*d*/) {
- std::vector<std::string> toks;
- int numToks = tokenizeLine(line, toks);
- for (int i=0; i<numToks; i++) {
-// DO MAGIC STUFF HERE
-//std::cout << "tok " << i << ": " << toks[i] << std::endl << std::flush;
- }
+ int numToks = tokenize(line, toks);
+ if (numToks && (toks[0].compare("group") == 0 || contFlag)) {
+ ret = processGroupLine(toks, cont);
+ } else if (numToks && toks[0].compare("acl") == 0) {
+ ret = processAclLine(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 {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Non-continuation line must start with \"group\" or \"acl\".");
+ ret = false;
+ }
+ }
+ contFlag = cont;
+ return ret;
}
-int AclReader::tokenizeLine(char* line, std::vector<std::string>& toks) {
- const char* tokChars = " \t\n";
+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) {
@@ -69,5 +296,209 @@ int AclReader::tokenizeLine(char* line, std::vector<std::string>& toks) {
return cnt;
}
+// 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 (!checkName(toks[i])) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters.");
+ return false;
+ }
+ addName(toks[i], citr->second);
+ }
+ } else {
+ if (toksSize < (cont ? 2 : 3)) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Insufficient tokens for group definition.");
+ return false;
+ }
+ if (!checkName(toks[1])) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "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 (!checkName(toks[i])) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Name \"" << toks[i] << "\" contains illegal characters.");
+ 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()) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "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);
+}
+
+// Debug aid
+void AclReader::printNames() const {
+ QPID_LOG(debug, "Group list: " << groups.size() << " groups found:" );
+ std::string tmp;
+ 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.clear();
+ }
+ QPID_LOG(debug, "Name list: " << names.size() << " names found:" );
+ tmp.clear();
+ for (nsCitr k=names.begin(); k!=names.end(); k++) {
+ tmp += " ";
+ tmp += *k;
+ }
+ QPID_LOG(debug, tmp);
+}
+
+bool AclReader::processAclLine(tokList& toks) {
+ const unsigned toksSize = toks.size();
+ if (toksSize < 4) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Insufficient tokens for acl definition.");
+ return false;
+ }
+
+ AclResult res;
+ try {
+ res = AclHelper::getAclResult(toks[1]);
+ } catch (...) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Unknown ACL permission \"" << toks[1] << "\".");
+ return false;
+ }
+
+ bool actionAllFlag = toks[3].compare("all") == 0;
+ Action action;
+ if (actionAllFlag) {
+ if (toksSize > 4) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Tokens found after action \"all\".");
+ return false;
+ }
+ action = CONSUME; // dummy; compiler must initialize action for this code path
+ } else {
+ try {
+ action = AclHelper::getAction(toks[3]);
+ } catch (...) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "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("all") == 0) {
+ rule->setObjectTypeAll();
+ } else {
+ try {
+ rule->setObjectType(AclHelper::getObjectType(toks[4]));
+ } catch (...) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "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) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Badly formed property name-value pair \"" << propNvp.first << "\". (Must be name=value)");
+ return false;
+ }
+ Property prop;
+ try {
+ prop = AclHelper::getProperty(propNvp.first);
+ } catch (...) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "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("all") != 0) {
+ if (groups.find(toks[2]) == groups.end()) {
+ addName(toks[2]);
+ }
+ }
+
+ // If rule validates, add to rule list
+ if (!rule->validate(validationMap)) {
+ QPID_LOG(error, ACL_FORMAT_ERR_LOG_PREFIX << "Invalid object/action/property combination.");
+ return false;
+ }
+ rules.push_back(rule);
+
+ return true;
+}
+
+// Debug aid
+void AclReader::printRules() const {
+ QPID_LOG(debug, "Rule list: " << rules.size() << " ACL rules found:");
+ int cnt = 0;
+ for (rlCitr i=rules.begin(); i<rules.end(); i++,cnt++) {
+ QPID_LOG(debug, " " << std::setfill(' ') << std::setw(2) << cnt << " " << (*i)->toString());
+ }
+}
+
+// Static function
+// Return true if the name is well-formed (ie contains legal characters)
+bool AclReader::checkName(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));
+}
}} // namespace qpid::acl