summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam <Adam@anope.org>2016-02-13 14:16:29 -0500
committerAdam <Adam@anope.org>2016-02-13 14:16:29 -0500
commit4e2ca31cf5fc874ab928dc4d3735f4345a910d0d (patch)
tree1590da2a86eb164389dd781870648d8f3824f9a3
parentaddd2a1987dccff6ad4c8a68f5c48c9f850912ec (diff)
Rewrite access path system to be simplier and use recursion
Show where access is "from" in chanserv/status
-rw-r--r--include/access.h21
-rw-r--r--modules/commands/cs_access.cpp4
-rw-r--r--modules/commands/cs_seen.cpp9
-rw-r--r--modules/commands/cs_status.cpp21
-rw-r--r--modules/commands/ns_alist.cpp13
-rw-r--r--modules/cs_statusupdate.cpp8
-rw-r--r--modules/webcpanel/pages/nickserv/alist.cpp7
-rw-r--r--src/access.cpp93
-rw-r--r--src/regchannel.cpp102
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)