From 804cfbdaf19ee803f362b6aa4c35696ca4e850c3 Mon Sep 17 00:00:00 2001 From: "Carl C. Trieloff" Date: Tue, 2 Sep 2008 21:49:55 +0000 Subject: QPID-107 Implementation for ACL for C++ broker git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@691396 13f79535-47bb-0310-9956-ffa450edef68 --- cpp/src/qpid/acl/AclReader.cpp | 461 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 446 insertions(+), 15 deletions(-) (limited to 'cpp/src/qpid/acl/AclReader.cpp') 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 #include -//#include // debug #include +#include +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +#include // degug +#include // 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 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 set > + 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;jactionList[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 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 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 /*d*/) { - std::vector toks; - int numToks = tokenizeLine(line, toks); - for (int i=0; i& toks) { - const char* tokChars = " \t\n"; +int AclReader::tokenize(char* line, std::vector& 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& 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; iaddProperty(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(); itoString()); + } +} + +// 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