diff options
-rw-r--r-- | include/access.h | 21 | ||||
-rw-r--r-- | modules/commands/cs_access.cpp | 4 | ||||
-rw-r--r-- | modules/commands/cs_seen.cpp | 9 | ||||
-rw-r--r-- | modules/commands/cs_status.cpp | 21 | ||||
-rw-r--r-- | modules/commands/ns_alist.cpp | 13 | ||||
-rw-r--r-- | modules/cs_statusupdate.cpp | 8 | ||||
-rw-r--r-- | modules/webcpanel/pages/nickserv/alist.cpp | 7 | ||||
-rw-r--r-- | src/access.cpp | 93 | ||||
-rw-r--r-- | src/regchannel.cpp | 102 |
9 files changed, 165 insertions, 113 deletions
diff --git a/include/access.h b/include/access.h index c77d9aa29..6f0124928 100644 --- a/include/access.h +++ b/include/access.h @@ -78,12 +78,7 @@ class CoreExport ChanAccess : public Serializable Serialize::Reference<NickCore> nc; public: - typedef std::multimap<const ChanAccess *, const ChanAccess *> Set; - /* shows the 'path' taken to determine if an access entry matches a user - * .first are access entries checked - * .second are access entries which match - */ - typedef std::pair<Set, Set> Path; + typedef std::vector<ChanAccess *> Path; /* The provider that created this access entry */ AccessProvider *provider; @@ -103,12 +98,14 @@ class CoreExport ChanAccess : public Serializable void Serialize(Serialize::Data &data) const anope_override; static Serializable* Unserialize(Serializable *obj, Serialize::Data &); + static const unsigned int MAX_DEPTH = 4; + /** Check if this access entry matches the given user or account * @param u The user * @param nc The account - * @param p The path to the access object which matches will be put here + * @param next Next channel to check if any */ - virtual bool Matches(const User *u, const NickCore *nc, Path &p) const; + virtual bool Matches(const User *u, const NickCore *nc, ChannelInfo* &next) const; /** Check if this access entry has the given privilege. * @param name The privilege name @@ -136,13 +133,13 @@ class CoreExport ChanAccess : public Serializable /* A group of access entries. This is used commonly, for example with ChannelInfo::AccessFor, * to show what access a user has on a channel because users can match multiple access entries. */ -class CoreExport AccessGroup : public std::vector<ChanAccess *> +class CoreExport AccessGroup { public: + /* access entries + paths */ + std::vector<ChanAccess::Path> paths; /* Channel these access entries are on */ const ChannelInfo *ci; - /* Path from these entries to other entries that they depend on */ - ChanAccess::Path path; /* Account these entries affect, if any */ const NickCore *nc; /* super_admin always gets all privs. founder is a special case where ci->founder == nc */ @@ -170,6 +167,8 @@ class CoreExport AccessGroup : public std::vector<ChanAccess *> bool operator<(const AccessGroup &other) const; bool operator>=(const AccessGroup &other) const; bool operator<=(const AccessGroup &other) const; + + inline bool empty() const { return paths.empty(); } }; #endif diff --git a/modules/commands/cs_access.cpp b/modules/commands/cs_access.cpp index 543082a2d..6e9b79c6a 100644 --- a/modules/commands/cs_access.cpp +++ b/modules/commands/cs_access.cpp @@ -370,7 +370,7 @@ class CommandCSAccess : public Command if (ci->c) for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) { - ChanAccess::Path p; + ChannelInfo *p; if (access->Matches(cit->second->user, cit->second->user->Account(), p)) timebuf = "Now"; } @@ -407,7 +407,7 @@ class CommandCSAccess : public Command if (ci->c) for (Channel::ChanUserList::const_iterator cit = ci->c->users.begin(), cit_end = ci->c->users.end(); cit != cit_end; ++cit) { - ChanAccess::Path p; + ChannelInfo *p; if (access->Matches(cit->second->user, cit->second->user->Account(), p)) timebuf = "Now"; } diff --git a/modules/commands/cs_seen.cpp b/modules/commands/cs_seen.cpp index 41f8ee97c..80bc8e548 100644 --- a/modules/commands/cs_seen.cpp +++ b/modules/commands/cs_seen.cpp @@ -236,9 +236,14 @@ class CommandSeen : public Command AccessGroup ag = source.c->ci->AccessFor(na->nc); time_t last = 0; - for (unsigned i = 0; i < ag.size(); ++i) + for (unsigned int i = 0; i < ag.paths.size(); ++i) { - ChanAccess *a = ag[i]; + ChanAccess::Path &p = ag.paths[i]; + + if (p.empty()) + continue; + + ChanAccess *a = p[p.size() - 1]; if (a->GetAccount() == na->nc && a->last_seen > last) last = a->last_seen; diff --git a/modules/commands/cs_status.cpp b/modules/commands/cs_status.cpp index 28fe60619..b984d5952 100644 --- a/modules/commands/cs_status.cpp +++ b/modules/commands/cs_status.cpp @@ -57,11 +57,26 @@ public: { source.Reply(_("Access for \002%s\002 on \002%s\002:"), nick.c_str(), ci->name.c_str()); - for (unsigned i = 0; i < ag.size(); ++i) + for (unsigned i = 0; i < ag.paths.size(); ++i) { - ChanAccess *acc = ag[i]; + ChanAccess::Path &p = ag.paths[i]; - source.Reply(_("\002%s\002 matches access entry %s, which has privilege %s."), nick.c_str(), acc->Mask().c_str(), acc->AccessSerialize().c_str()); + if (p.empty()) + continue; + + if (p.size() == 1) + { + ChanAccess *acc = p[0]; + + source.Reply(_("\002%s\002 matches access entry %s, which has privilege %s."), nick.c_str(), acc->Mask().c_str(), acc->AccessSerialize().c_str()); + } + else + { + ChanAccess *first = p[0]; + ChanAccess *acc = p[p.size() - 1]; + + source.Reply(_("\002%s\002 matches access entry %s (from entry %s), which has privilege %s."), nick.c_str(), acc->Mask().c_str(), first->Mask().c_str(), acc->AccessSerialize().c_str()); + } } } diff --git a/modules/commands/ns_alist.cpp b/modules/commands/ns_alist.cpp index d273ec59f..6ccbe94ba 100644 --- a/modules/commands/ns_alist.cpp +++ b/modules/commands/ns_alist.cpp @@ -86,8 +86,17 @@ class CommandNSAList : public Command entry["Number"] = stringify(chan_count); entry["Channel"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name; - for (unsigned j = 0; j < access.size(); ++j) - entry["Access"] = entry["Access"] + ", " + access[j]->AccessSerialize(); + for (unsigned j = 0; j < access.paths.size(); ++j) + { + ChanAccess::Path &p = access.paths[i]; + + // not interested in indirect access + if (p.size() != 1) + continue; + + ChanAccess *a = p[0]; + entry["Access"] = entry["Access"] + ", " + a->AccessSerialize(); + } entry["Access"] = entry["Access"].substr(2); entry["Description"] = ci->desc; list.AddEntry(entry); diff --git a/modules/cs_statusupdate.cpp b/modules/cs_statusupdate.cpp index b5febbe76..ffdbec3e7 100644 --- a/modules/cs_statusupdate.cpp +++ b/modules/cs_statusupdate.cpp @@ -23,8 +23,8 @@ class StatusUpdate : public Module { User *user = it->second->user; - ChanAccess::Path p; - if (user->server != Me && access->Matches(user, user->Account(), p)) + ChannelInfo *next; + if (user->server != Me && access->Matches(user, user->Account(), next)) { AccessGroup ag = ci->AccessFor(user); @@ -46,8 +46,8 @@ class StatusUpdate : public Module { User *user = it->second->user; - ChanAccess::Path p; - if (user->server != Me && access->Matches(user, user->Account(), p)) + ChannelInfo *next; + if (user->server != Me && access->Matches(user, user->Account(), next)) { AccessGroup ag = ci->AccessFor(user); diff --git a/modules/webcpanel/pages/nickserv/alist.cpp b/modules/webcpanel/pages/nickserv/alist.cpp index fbb37224e..d5b331f4f 100644 --- a/modules/webcpanel/pages/nickserv/alist.cpp +++ b/modules/webcpanel/pages/nickserv/alist.cpp @@ -46,10 +46,9 @@ bool WebCPanel::NickServ::Alist::OnRequest(HTTPProvider *server, const Anope::st replacements["NUMBERS"] = stringify(chan_count); replacements["CHANNELS"] = (ci->HasExt("CS_NO_EXPIRE") ? "!" : "") + ci->name; - Anope::string access_str; - for (unsigned i = 0; i < access.size(); ++i) - access_str += ", " + access[i]->AccessSerialize(); - replacements["ACCESSES"] = access_str.substr(2); + + const ChanAccess *highest = access.Highest(); + replacements["ACCESSES"] = highest ? highest->AccessSerialize() : ""; } TemplateFileServer page("nickserv/alist.html"); diff --git a/src/access.cpp b/src/access.cpp index abd55714f..007e6d281 100644 --- a/src/access.cpp +++ b/src/access.cpp @@ -250,8 +250,10 @@ Serializable* ChanAccess::Unserialize(Serializable *obj, Serialize::Data &data) return access; } -bool ChanAccess::Matches(const User *u, const NickCore *acc, Path &p) const +bool ChanAccess::Matches(const User *u, const NickCore *acc, ChannelInfo* &next) const { + next = NULL; + if (this->nc) return this->nc == acc; @@ -276,28 +278,7 @@ bool ChanAccess::Matches(const User *u, const NickCore *acc, Path &p) const if (IRCD->IsChannelValid(this->mask)) { - ChannelInfo *tci = ChannelInfo::Find(this->mask); - if (tci) - { - for (unsigned i = 0; i < tci->GetAccessCount(); ++i) - { - ChanAccess *a = tci->GetAccess(i); - std::pair<const ChanAccess *, const ChanAccess *> pair = std::make_pair(this, a); - - std::pair<Set::iterator, Set::iterator> range = p.first.equal_range(this); - for (; range.first != range.second; ++range.first) - if (range.first->first == pair.first && range.first->second == pair.second) - goto cont; - - p.first.insert(pair); - if (a->Matches(u, acc, p)) - p.second.insert(pair); - - cont:; - } - - return p.second.count(this) > 0; - } + next = ChannelInfo::Find(this->mask); } return false; @@ -347,37 +328,30 @@ bool ChanAccess::operator<=(const ChanAccess &other) const return !(*this > other); } -AccessGroup::AccessGroup() : std::vector<ChanAccess *>() +AccessGroup::AccessGroup() { this->ci = NULL; this->nc = NULL; this->super_admin = this->founder = false; } -static bool HasPriv(const AccessGroup &ag, const ChanAccess *access, const Anope::string &name) +static bool HasPriv(const ChanAccess::Path &path, const Anope::string &name) { - EventReturn MOD_RESULT; - FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name)); - if (MOD_RESULT == EVENT_ALLOW || access->HasPriv(name)) + if (path.empty()) + return false; + + for (unsigned int i = 0; i < path.size(); ++i) { - typedef std::multimap<const ChanAccess *, const ChanAccess *> path; - std::pair<path::const_iterator, path::const_iterator> it = ag.path.second.equal_range(access); - if (it.first != it.second) - /* check all of the paths for this entry */ - for (; it.first != it.second; ++it.first) - { - const ChanAccess *a = it.first->second; - /* if only one path fully matches then we are ok */ - if (HasPriv(ag, a, name)) - return true; - } - else - /* entry is the end of a chain, all entries match, ok */ - return true; + ChanAccess *access = path[i]; + + EventReturn MOD_RESULT; + FOREACH_RESULT(OnCheckPriv, MOD_RESULT, (access, name)); + + if (MOD_RESULT != EVENT_ALLOW && !access->HasPriv(name)) + return false; } - /* entry does not match or none of the chains fully match */ - return false; + return true; } bool AccessGroup::HasPriv(const Anope::string &name) const @@ -393,7 +367,7 @@ bool AccessGroup::HasPriv(const Anope::string &name) const bool auto_mode = !name.find("AUTO"); /* Only grant founder privilege if this isn't an auto mode or if they don't match any entries in this group */ - if ((!auto_mode || this->empty()) && this->founder) + if ((!auto_mode || paths.empty()) && this->founder) return true; EventReturn MOD_RESULT; @@ -401,23 +375,40 @@ bool AccessGroup::HasPriv(const Anope::string &name) const if (MOD_RESULT != EVENT_CONTINUE) return MOD_RESULT == EVENT_ALLOW; - for (unsigned i = this->size(); i > 0; --i) + for (unsigned int i = paths.size(); i > 0; --i) { - ChanAccess *access = this->at(i - 1); + const ChanAccess::Path &path = paths[i - 1]; - if (::HasPriv(*this, access, name)) + if (::HasPriv(path, name)) return true; } return false; } +static ChanAccess *HighestInPath(const ChanAccess::Path &path) +{ + ChanAccess *highest = NULL; + + for (unsigned int i = 0; i < path.size(); ++i) + if (highest == NULL || *path[i] > *highest) + highest = path[i]; + + return highest; +} + const ChanAccess *AccessGroup::Highest() const { ChanAccess *highest = NULL; - for (unsigned i = 0; i < this->size(); ++i) - if (highest == NULL || *this->at(i) > *highest) - highest = this->at(i); + + for (unsigned int i = 0; i < paths.size(); ++i) + { + ChanAccess *hip = HighestInPath(paths[i]); + + if (highest == NULL || *hip > *highest) + highest = hip; + } + return highest; } diff --git a/src/regchannel.cpp b/src/regchannel.cpp index df7359bb9..fe985215d 100644 --- a/src/regchannel.cpp +++ b/src/regchannel.cpp @@ -384,6 +384,39 @@ ChanAccess *ChannelInfo::GetAccess(unsigned index) const return acc; } +static void FindMatchesRecurse(ChannelInfo *ci, const User *u, const NickCore *account, unsigned int depth, std::vector<ChanAccess::Path> &paths, ChanAccess::Path &path) +{ + if (depth > ChanAccess::MAX_DEPTH) + return; + + for (unsigned int i = 0; i < ci->GetAccessCount(); ++i) + { + ChanAccess *a = ci->GetAccess(i); + ChannelInfo *next = NULL; + + if (a->Matches(u, u->Account(), next)) + { + ChanAccess::Path next_path = path; + next_path.push_back(a); + + paths.push_back(next_path); + } + else if (next) + { + ChanAccess::Path next_path = path; + next_path.push_back(a); + + FindMatchesRecurse(next, u, account, depth + 1, paths, next_path); + } + } +} + +static void FindMatches(AccessGroup &group, ChannelInfo *ci, const User *u, const NickCore *account) +{ + ChanAccess::Path path; + FindMatchesRecurse(ci, u, account, 0, group.paths, path); +} + AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed) { AccessGroup group; @@ -404,20 +437,20 @@ AccessGroup ChannelInfo::AccessFor(const User *u, bool updateLastUsed) group.ci = this; group.nc = nc; - for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) - { - ChanAccess *a = this->GetAccess(i); - if (a->Matches(u, u->Account(), group.path)) - group.push_back(a); - } + FindMatches(group, this, u, u->Account()); - if (group.founder || !group.empty()) + if (group.founder || !group.paths.empty()) { if (updateLastUsed) this->last_used = Anope::CurTime; - for (unsigned i = 0; i < group.size(); ++i) - group[i]->last_seen = Anope::CurTime; + for (unsigned i = 0; i < group.paths.size(); ++i) + { + ChanAccess::Path &p = group.paths[i]; + + for (unsigned int j = 0; j < p.size(); ++j) + p[j]->last_seen = Anope::CurTime; + } } return group; @@ -431,14 +464,9 @@ AccessGroup ChannelInfo::AccessFor(const NickCore *nc, bool updateLastUsed) group.ci = this; group.nc = nc; - for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) - { - ChanAccess *a = this->GetAccess(i); - if (a->Matches(NULL, nc, group.path)) - group.push_back(a); - } + FindMatches(group, this, NULL, nc); - if (group.founder || !group.empty()) + if (group.founder || !group.paths.empty()) if (updateLastUsed) this->last_used = Anope::CurTime; @@ -452,28 +480,34 @@ unsigned ChannelInfo::GetAccessCount() const return this->access->size(); } -unsigned ChannelInfo::GetDeepAccessCount() const +static unsigned int GetDeepAccessCount(const ChannelInfo *ci, std::set<const ChannelInfo *> &seen, unsigned int depth) { - ChanAccess::Path path; - for (unsigned i = 0, end = this->GetAccessCount(); i < end; ++i) - { - ChanAccess *a = this->GetAccess(i); - a->Matches(NULL, NULL, path); - } + if (depth > ChanAccess::MAX_DEPTH || seen.count(ci)) + return 0; + seen.insert(ci); - unsigned count = this->GetAccessCount(); - std::set<const ChannelInfo *> channels; - channels.insert(this); - for (ChanAccess::Set::iterator it = path.first.begin(); it != path.first.end(); ++it) + unsigned int total = 0; + + for (unsigned int i = 0; i < ci->GetAccessCount(); ++i) { - const ChannelInfo *ci = it->first->ci; - if (!channels.count(ci)) - { - channels.count(ci); - count += ci->GetAccessCount(); - } + ChanAccess::Path path; + ChanAccess *a = ci->GetAccess(i); + ChannelInfo *next = NULL; + + a->Matches(NULL, NULL, next); + ++total; + + if (next) + total += GetDeepAccessCount(ci, seen, depth + 1); } - return count; + + return total; +} + +unsigned ChannelInfo::GetDeepAccessCount() const +{ + std::set<const ChannelInfo *> seen; + return ::GetDeepAccessCount(this, seen, 0); } ChanAccess *ChannelInfo::EraseAccess(unsigned index) |