/* Web Polygraph http://www.web-polygraph.org/ * (C) 2003-2006 The Measurement Factory * Licensed under the Apache License, Version 2.0 */ #include "base/polygraph.h" #include #include "xstd/h/iomanip.h" #include "xstd/gadgets.h" #include "base/RndPermut.h" #include "base/opts.h" #include "base/polyOpts.h" #include "base/CmdLine.h" #include "pgl/PglPp.h" #include "pgl/PglCtx.h" #include "pgl/PglStaticSemx.h" #include "pgl/PglStringSym.h" #include "pgl/MembershipMapSym.h" #include "pgl/AgentArrIter.h" #include "pgl/RobotSym.h" #include "client/UserCred.h" #include "client/MembershipMap.h" class MyOpts: public OptGrp { public: MyOpts(): theHelpOpt(this, "help", "list of options"), theVersOpt(this, "version", "package version info"), theTemplate(this, "template ", "LDIF template"), theCfgName(this, "config ", "PGL configuration"), theCfgDirs(this, "cfg_dirs ", "directories for PGL #includes"), avoidDups(this, "avoid_duplicates ", "[slowly] avoid duplicate LDIF records", false), theGlbRngSeed(this, "global_rng_seed ","per-test r.n.g. seed", 1) {} virtual bool validate() const; //virtual ostream &printAnonym(ostream &os) const; //virtual bool parseAnonym(const Array &opts); //virtual bool canParseAnonym() const { return true; } public: HelpOpt theHelpOpt; VersionOpt theVersOpt; StrOpt theTemplate; StrOpt theCfgName; StrArrOpt theCfgDirs; BoolOpt avoidDups; IntOpt theGlbRngSeed; }; // empty-line-separated blob from LDIF template class LdifTemplate { protected: struct Part { enum Type { ptNone = 0, ptPlain, ptGroup, ptUserName, ptUserPassword } type; String *image; Part(Type aType = ptNone, String *anImage = 0): type(aType), image(anImage) {} }; public: LdifTemplate(); ~LdifTemplate(); bool empty() const { return theParts.count() == 0; } bool groupDependent() const { return isGroupDependent; } bool userDependent() const { return isUserDependent; } String instantiate(const String &gname, const UserCred &creds) const; String instantiate(const String &gname) const; String instantiate() const; void load(istream &is); protected: String instantiate(const String *gname, const UserCred *creds) const; void addPlainPart(const char *beg, const char *end); void addPart(const Part &part); protected: Array theParts; // parts of the template bool isGroupDependent; bool isUserDependent; }; static Array TheMemberships; static Array TheUsedUserGroupNames; static Array TheCredentials; static int TheInstantiatedTemplatesCount = 0; static MyOpts TheOpts; /* MyOpt */ bool MyOpts::validate() const { if (!theTemplate) cerr << "must specify LDIF template file (--template)" << endl; else if (!theCfgName) cerr << "must specify PGL configuration file (--config)" << endl; else return true; return false; } /* LdifTemplate */ LdifTemplate::LdifTemplate(): isGroupDependent(false), isUserDependent(false) { } LdifTemplate::~LdifTemplate() { while (theParts.count()) delete theParts.pop().image; } String LdifTemplate::instantiate(const String *gname, const UserCred *creds) const { // split creds into uname and password const char *password = 0; int unameLen = 0; if (creds) { unameLen = creds->name().size(); password = creds->password().data(); } String res; for (int i = 0; i < theParts.count(); ++i) { const Part &part = theParts[i]; switch (part.type) { case Part::ptPlain: Assert(part.image); res += *part.image; break; case Part::ptGroup: Assert(gname); res += *gname; break; case Part::ptUserName: Assert(creds); res.append(creds->image().data(), unameLen); break; case Part::ptUserPassword: Assert(creds); res += password; break; default: Assert(false); } } return res; } String LdifTemplate::instantiate(const String &gname, const UserCred &creds) const { return instantiate(&gname, &creds); } String LdifTemplate::instantiate(const String &gname) const { return instantiate(&gname, 0); } String LdifTemplate::instantiate() const { return instantiate(0, 0); } void LdifTemplate::load(istream &is) { String buf; char c; while (is.read(&c, 1)) { const bool atEol = !buf || buf.last() == '\n'; buf += c; if (c == '\n' && atEol) break; } if (!buf) return; const char *p = buf.cstr(); while (const char *pholder = strchr(p, '{')) { if (strncmp(pholder, "{group}", 7) == 0) { isGroupDependent = true; addPlainPart(p, pholder); addPart(Part(Part::ptGroup)); } else if (strncmp(pholder, "{username}", 10) == 0) { isUserDependent = true; addPlainPart(p, pholder); addPart(Part(Part::ptUserName)); } else if (strncmp(pholder, "{password}", 10) == 0) { isUserDependent = true; addPlainPart(p, pholder); addPart(Part(Part::ptUserPassword)); } else { cerr << "unknown placeholder near `"; const char *eop = strchr(pholder, '}'); eop = eop ? eop+1 : (pholder + Min((int)strlen(pholder), 10)); cerr.write(pholder, eop - pholder); exit(1); } p = strchr(pholder, '}') + 1; } addPlainPart(p, p + strlen(p)); } void LdifTemplate::addPlainPart(const char *beg, const char *end) { if (end > beg) { String *image = new String(beg, end - beg); addPart(Part(Part::ptPlain, image)); } } void LdifTemplate::addPart(const Part &part) { theParts.append(part); } static void noteBlob(const String &blob) { if (TheOpts.avoidDups) { static Map blobsSeen; if (int *count = blobsSeen.valp(blob)) { (*count)++; return; } blobsSeen.add(blob, 1); } cout << blob << endl; TheInstantiatedTemplatesCount++; } static void userDependentStep(const LdifTemplate &tmp) { for (int u = 0; u < TheCredentials.count(); ++u) { const UserCred &credentials = *TheCredentials[u]; int matchCount = 0; for (int g = 0; g < TheMemberships.count(); ++g) { for (MembershipMap::GroupIterator i = TheMemberships[g]->groupIterator(credentials); i; ++i) { noteBlob(tmp.instantiate(*i, credentials)); ++matchCount; } } if (!matchCount) { cerr << "warning: user " << credentials.image() << " belongs to no group " << "and will not have LDIF records" << endl; } } } static void groupDependentStep(const LdifTemplate &tmp) { for (int g = 0; g < TheUsedUserGroupNames.count(); ++g) { noteBlob(tmp.instantiate(*TheUsedUserGroupNames[g])); } } static void step(const LdifTemplate &tmp) { if (tmp.userDependent()) { userDependentStep(tmp); } else if (tmp.groupDependent()) { groupDependentStep(tmp); } else { noteBlob(tmp.instantiate()); } } template static void logStats(const String &label, const Stats &stats) { const int padding = Max(0, 30 - label.len()); clog << "fyi: " << label << ": " << setw(padding) << " " << setw(10) << stats << endl; } int main(int argc, char **argv) { CmdLine cmd; cmd.configure(Array() << &TheOpts); if (!cmd.parse(argc, argv) || !TheOpts.validate()) return -1; configureStream(cout, 2); configureStream(clog, 3); // set random seeds GlbPermut().reseed(TheOpts.theGlbRngSeed); // parse templates ifstream f(TheOpts.theTemplate.cstr()); Array templates; while (f) { LdifTemplate *tmp = new LdifTemplate(); tmp->load(f); if (!tmp->empty()) templates.append(tmp); } logStats("templates", templates.count()); if (!templates.count()) cerr << TheOpts.theTemplate << ": warning: no templates found" << endl; // parse PGL TheOpts.theCfgDirs.copy(PglPp::TheDirs); PglStaticSemx::Interpret(TheOpts.theCfgName); logStats("use()d MembershipMaps", PglStaticSemx::TheMembershipsToUse.count()); if (!PglStaticSemx::TheMembershipsToUse.count()) cerr << TheOpts.theCfgName << ": warning: no MembershipMaps use()d" << endl; long groupSpace = 0; long userSpace = 0; for (int i = 0; i < PglStaticSemx::TheMembershipsToUse.count(); ++i) { MembershipMap *g = new MembershipMap; g->configure(*PglStaticSemx::TheMembershipsToUse[i], i+1); groupSpace += g->groupNameCount(); userSpace += g->userNameCount(); TheMemberships.append(g); } logStats("possible user group names", groupSpace); logStats("possible user credentials", userSpace); for (AgentArrIter u(PglStaticSemx::TheAgentsToUse, 0, "Robot"); u; u.nextSym()) { const RobotSym &r = (const RobotSym&)u.agentSym()->cast("Robot"); Array creds; r.credentials(creds); for (int c = 0; c < creds.count(); ++c) { TheCredentials.append(new UserCred(*creds[c])); } } logStats("user credentials", TheCredentials.count()); for (int g = 0; g < TheMemberships.count(); ++g) { TheMemberships[g]->collectUsedGroupNames(TheCredentials, TheUsedUserGroupNames); } logStats("user groups", TheUsedUserGroupNames.count()); //logStats("mean groups per user", TheUsedUserGroupNames.count()/(double)TheCredentials.count()); //logStats("mean users per group", TheCredentials.count()/(double)TheUsedUserGroupNames.count()); for (int t = 0; t < templates.count(); ++t) step(*templates[t]); logStats("instantiated templates", TheInstantiatedTemplatesCount); // cleanup while (templates.count()) delete templates.pop(); while (TheUsedUserGroupNames.count()) delete TheUsedUserGroupNames.pop(); return 0; }