1
0
mirror of https://github.com/anope/anope.git synced 2026-06-12 17:24:49 +02:00

Move fingerprints to their own table.

This commit is contained in:
Sadie Powell
2026-02-22 15:57:47 +00:00
parent 66f53128ce
commit f380b27ed3
6 changed files with 230 additions and 63 deletions
+1
View File
@@ -42,6 +42,7 @@ struct ModeData;
class Module; class Module;
class NickAlias; class NickAlias;
class NickCore; class NickCore;
namespace NickServ { struct Cert; }
struct Oper; struct Oper;
namespace OperServ { struct Exception; } namespace OperServ { struct Exception; }
class OperType; class OperType;
+2 -2
View File
@@ -793,13 +793,13 @@ public:
* @param nc The nick * @param nc The nick
* @param entry The entry * @param entry The entry
*/ */
virtual void OnNickAddCert(NickCore *nc, const Anope::string &entry) ATTR_NOT_NULL(2) { throw NotImplementedException(); } virtual void OnNickAddCert(NickCore *nc, const NickServ::Cert *entry) ATTR_NOT_NULL(2, 3) { throw NotImplementedException(); }
/** Called from NickCore::EraseCert() /** Called from NickCore::EraseCert()
* @param nc pointer to the NickCore * @param nc pointer to the NickCore
* @param entry The fingerprint * @param entry The fingerprint
*/ */
virtual void OnNickEraseCert(NickCore *nc, const Anope::string &entry) ATTR_NOT_NULL(2) { throw NotImplementedException(); } virtual void OnNickEraseCert(NickCore *nc, const NickServ::Cert *entry) ATTR_NOT_NULL(2, 3) { throw NotImplementedException(); }
/** Called when a user requests info for a nick /** Called when a user requests info for a nick
* @param source The user requesting info * @param source The user requesting info
+21 -2
View File
@@ -19,12 +19,31 @@
namespace NickServ namespace NickServ
{ {
struct Cert;
class CertList; class CertList;
class CertService; class CertService;
ServiceReference<CertService> cert_service(NICKSERV_CERT_SERVICE, NICKSERV_CERT_SERVICE); ServiceReference<CertService> cert_service(NICKSERV_CERT_SERVICE, NICKSERV_CERT_SERVICE);
} }
struct NickServ::Cert
{
/** The account this cert is for. */
Serialize::Reference<NickCore> account;
/** The time at which this certificate was created. */
time_t created = 0;
/** The user who created this certificate. */
Anope::string creator;
/** If non-empty then a description of the certificate. */
Anope::string description;
/** The TLS fingerprint for the certificate. */
Anope::string fingerprint;
};
class NickServ::CertList class NickServ::CertList
{ {
protected: protected:
@@ -39,7 +58,7 @@ public:
* *
* Adds a new entry into the cert list. * Adds a new entry into the cert list.
*/ */
virtual void AddCert(const Anope::string &entry) = 0; virtual NickServ::Cert *AddCert(const Anope::string &entry) = 0;
/** Get an entry from the nick's cert list by index /** Get an entry from the nick's cert list by index
* *
@@ -48,7 +67,7 @@ public:
* *
* Retrieves an entry from the certificate list corresponding to the given index. * Retrieves an entry from the certificate list corresponding to the given index.
*/ */
virtual Anope::string GetCert(unsigned entry) const = 0; virtual NickServ::Cert *GetCert(unsigned entry) const = 0;
virtual unsigned GetCertCount() const = 0; virtual unsigned GetCertCount() const = 0;
+20 -2
View File
@@ -16,8 +16,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: Anope\n" "Project-Id-Version: Anope\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-02-22 16:58+0000\n" "POT-Creation-Date: 2026-02-22 17:02+0000\n"
"PO-Revision-Date: 2026-02-22 16:58+0000\n" "PO-Revision-Date: 2026-02-22 17:03+0000\n"
"Last-Translator: Sadie Powell <sadie@witchery.services>\n" "Last-Translator: Sadie Powell <sadie@witchery.services>\n"
"Language-Team: English\n" "Language-Team: English\n"
"Language: en_US\n" "Language: en_US\n"
@@ -363,6 +363,18 @@ msgstr ""
msgid "[auto-memo] The memo you sent to %s has been viewed." msgid "[auto-memo] The memo you sent to %s has been viewed."
msgstr "" msgstr ""
msgid "{fingerprint}"
msgstr ""
msgid "{fingerprint} ({description})"
msgstr ""
msgid "{fingerprint} -- created by {creator} at {created}"
msgstr ""
msgid "{fingerprint} -- created by {creator} at {created} ({description})"
msgstr ""
msgid "{host}: {session} sessions" msgid "{host}: {session} sessions"
msgstr "" msgstr ""
@@ -3040,6 +3052,9 @@ msgstr ""
msgid "Find a user's status on a channel" msgid "Find a user's status on a channel"
msgstr "" msgstr ""
msgid "Fingerprint"
msgstr ""
#, c-format #, c-format
msgid "Fingerprint %s already present on %s's certificate list." msgid "Fingerprint %s already present on %s's certificate list."
msgstr "" msgstr ""
@@ -6070,6 +6085,9 @@ msgstr ""
msgid "VIEW [mask | list]" msgid "VIEW [mask | list]"
msgstr "" msgstr ""
msgid "VIEW [nickname]"
msgstr ""
msgid "VIEW [vhost-mask | entry-num | list]" msgid "VIEW [vhost-mask | entry-num | list]"
msgstr "" msgstr ""
+185 -56
View File
@@ -15,7 +15,20 @@
#include "module.h" #include "module.h"
#include "modules/nickserv/cert.h" #include "modules/nickserv/cert.h"
static Anope::unordered_map<NickCore *> certmap; #define NICKSERV_CERT_TYPE "NSCert"
struct NSCertInfo final
: NickServ::Cert
, Serializable
{
NSCertInfo(Extensible *ext)
: Serializable(NICKSERV_CERT_TYPE)
{
account = anope_dynamic_static_cast<NickCore *>(ext);
}
};
static Anope::unordered_map<NSCertInfo *> certmap;
struct CertServiceImpl final struct CertServiceImpl final
: NickServ::CertService : NickServ::CertService
@@ -27,9 +40,9 @@ struct CertServiceImpl final
NickCore *FindAccountFromCert(const Anope::string &cert) override NickCore *FindAccountFromCert(const Anope::string &cert) override
{ {
Anope::unordered_map<NickCore *>::iterator it = certmap.find(cert); auto it = certmap.find(cert);
if (it != certmap.end()) if (it != certmap.end())
return it->second; return it->second->account;
return NULL; return NULL;
} }
@@ -48,8 +61,10 @@ struct CertServiceImpl final
struct NSCertListImpl final struct NSCertListImpl final
: NickServ::CertList : NickServ::CertList
{ {
friend class NSCertInfoType;
Serialize::Reference<NickCore> nc; Serialize::Reference<NickCore> nc;
std::vector<Anope::string> certs; std::vector<NSCertInfo *> certs;
public: public:
NSCertListImpl(Extensible *obj) : nc(anope_dynamic_static_cast<NickCore *>(obj)) { } NSCertListImpl(Extensible *obj) : nc(anope_dynamic_static_cast<NickCore *>(obj)) { }
@@ -65,11 +80,15 @@ public:
* *
* Adds a new entry into the cert list. * Adds a new entry into the cert list.
*/ */
void AddCert(const Anope::string &entry) override NickServ::Cert *AddCert(const Anope::string &entry) override
{ {
this->certs.push_back(entry); auto *cert = new NSCertInfo(nc);
certmap[entry] = nc; cert->fingerprint = entry;
FOREACH_MOD(OnNickAddCert, (this->nc, entry));
this->certs.push_back(cert);
certmap[entry] = cert;
FOREACH_MOD(OnNickAddCert, (this->nc, cert));
return cert;
} }
/** Get an entry from the nick's cert list by index /** Get an entry from the nick's cert list by index
@@ -79,10 +98,11 @@ public:
* *
* Retrieves an entry from the certificate list corresponding to the given index. * Retrieves an entry from the certificate list corresponding to the given index.
*/ */
Anope::string GetCert(unsigned entry) const override NickServ::Cert *GetCert(unsigned entry) const override
{ {
if (entry >= this->certs.size()) if (entry >= this->certs.size())
return ""; return nullptr;
return this->certs[entry]; return this->certs[entry];
} }
@@ -100,7 +120,10 @@ public:
*/ */
bool FindCert(const Anope::string &entry) const override bool FindCert(const Anope::string &entry) const override
{ {
return std::find(this->certs.begin(), this->certs.end(), entry) != this->certs.end(); auto it = std::find_if(this->certs.begin(), this->certs.end(), [&entry](const NSCertInfo *cert) {
return cert->fingerprint == entry;
});
return it != this->certs.end();
} }
/** Erase a fingerprint from the nick's certificate list /** Erase a fingerprint from the nick's certificate list
@@ -111,34 +134,45 @@ public:
*/ */
void EraseCert(const Anope::string &entry) override void EraseCert(const Anope::string &entry) override
{ {
std::vector<Anope::string>::iterator it = std::find(this->certs.begin(), this->certs.end(), entry); auto it = std::find_if(this->certs.begin(), this->certs.end(), [&entry](const NSCertInfo *cert) {
return cert->fingerprint == entry;
});
if (it != this->certs.end()) if (it != this->certs.end())
{ {
FOREACH_MOD(OnNickEraseCert, (this->nc, entry)); FOREACH_MOD(OnNickEraseCert, (this->nc, *it));
certmap.erase(entry); certmap.erase(entry);
delete *it;
this->certs.erase(it); this->certs.erase(it);
} }
} }
void ReplaceCert(const Anope::string &oldentry, const Anope::string &newentry) override void ReplaceCert(const Anope::string &oldentry, const Anope::string &newentry) override
{ {
auto it = std::find(this->certs.begin(), this->certs.end(), oldentry); auto oldit = std::find_if(this->certs.begin(), this->certs.end(), [&oldentry](const NSCertInfo *cert) {
if (it == this->certs.end()) return cert->fingerprint == oldentry;
});
if (oldit == this->certs.end())
return; // We can't replace a non-existent cert. return; // We can't replace a non-existent cert.
FOREACH_MOD(OnNickEraseCert, (this->nc, oldentry)); FOREACH_MOD(OnNickEraseCert, (this->nc, *oldit));
certmap.erase(oldentry); certmap.erase(oldentry);
if (std::find(this->certs.begin(), this->certs.end(), newentry) != this->certs.end()) auto newit = std::find_if(this->certs.begin(), this->certs.end(), [&newentry](const NSCertInfo *cert) {
return cert->fingerprint == newentry;
});
if (newit != this->certs.end())
{ {
// The cert we're upgrading to already exists. // The cert we're upgrading to already exists.
this->certs.erase(it); delete *newit;
this->certs.erase(newit);
return; return;
} }
*it = newentry; auto *cert = *newit;
certmap[newentry] = nc; cert->fingerprint = newentry;
FOREACH_MOD(OnNickAddCert, (this->nc, newentry)); certmap[newentry] = cert;
FOREACH_MOD(OnNickAddCert, (this->nc, cert));
} }
/** Clears the entire nick's cert list /** Clears the entire nick's cert list
@@ -148,8 +182,11 @@ public:
void ClearCert() override void ClearCert() override
{ {
FOREACH_MOD(OnNickClearCert, (this->nc)); FOREACH_MOD(OnNickClearCert, (this->nc));
for (const auto &cert : certs) for (const auto *cert : certs)
certmap.erase(cert); {
delete cert;
certmap.erase(cert->fingerprint);
}
this->certs.clear(); this->certs.clear();
} }
@@ -164,45 +201,89 @@ public:
{ {
ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<NSCertListImpl>(m, ename) { } ExtensibleItem(Module *m, const Anope::string &ename) : ::ExtensibleItem<NSCertListImpl>(m, ename) { }
void ExtensibleSerialize(const Extensible *e, const Serializable *s, Serialize::Data &data) const override
{
if (s->GetSerializableType()->GetName() != NICKCORE_TYPE)
return;
const NickCore *n = anope_dynamic_static_cast<const NickCore *>(e);
auto *c = this->Get(n);
if (c == NULL || !c->GetCertCount())
return;
std::ostringstream oss;
for (unsigned i = 0; i < c->GetCertCount(); ++i)
oss << c->GetCert(i) << " ";
data.Store("cert", oss.str());
}
void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override void ExtensibleUnserialize(Extensible *e, Serializable *s, Serialize::Data &data) override
{ {
// Begin 2.0 compatibility.
if (s->GetSerializableType()->GetName() != NICKCORE_TYPE) if (s->GetSerializableType()->GetName() != NICKCORE_TYPE)
return; return;
NickCore *n = anope_dynamic_static_cast<NickCore *>(e); auto *nc = anope_dynamic_static_cast<NickCore *>(e);
auto *c = this->Require(n); auto *cl = this->Require(nc);
// Delete the old cert list.
for (const auto *cert : cl->certs)
{
delete cert;
certmap.erase(cert->fingerprint);
}
cl->certs.clear();
// Add the new cert list
Anope::string buf; Anope::string buf;
data["cert"] >> buf; data["cert"] >> buf;
spacesepstream sep(buf); for (spacesepstream sep(buf); sep.GetToken(buf); )
for (const auto &cert : c->certs)
certmap.erase(cert);
c->certs.clear();
while (sep.GetToken(buf))
{ {
c->certs.push_back(buf); auto *cert = new NSCertInfo(e);
certmap[buf] = n; cert->fingerprint = buf;
cl->certs.push_back(cert);
certmap[buf] = cert;
} }
// End 2.0 compatibility.
} }
}; };
}; };
class NSCertInfoType final
: public Serialize::Type
{
public:
NSCertInfoType()
: Serialize::Type(NICKSERV_CERT_TYPE)
{
}
void Serialize(Serializable *obj, Serialize::Data &data) const override
{
const auto *cert = static_cast<const NSCertInfo *>(obj);
data.Store("account", cert->account->GetId());
data.Store("created", cert->created);
data.Store("creator", cert->creator);
data.Store("description", cert->description);
data.Store("fingerprint", cert->fingerprint);
}
Serializable *Unserialize(Serializable *obj, Serialize::Data &data) const override
{
uint64_t account = 0;
data["account"] >> account;
auto *nc = NickCore::FindId(account);
if (!nc)
return nullptr; // Missing user.
NSCertInfo *cert;
if (obj)
cert = anope_dynamic_static_cast<NSCertInfo *>(obj);
else
cert = new NSCertInfo(nc);
data["created"] >> cert->created;
data["creator"] >> cert->creator;
data["description"] >> cert->description;
data["fingerprint"] >> cert->fingerprint;
if (!obj)
{
auto *cl = nc->Require<NSCertListImpl>(NICKSERV_CERT_EXT);
cl->certs.push_back(cert);
certmap[cert->fingerprint] = cert;
}
return cert;
}
};
class CommandNSCert final class CommandNSCert final
: public Command : public Command
{ {
@@ -244,7 +325,10 @@ private:
return; return;
} }
cl->AddCert(certfp); auto *cert = cl->AddCert(certfp);
cert->created = Anope::CurTime;
cert->creator = source.GetNick();
Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD certificate fingerprint " << certfp << " to " << nc->display; Log(nc == source.GetAccount() ? LOG_COMMAND : LOG_ADMIN, source, this) << "to ADD certificate fingerprint " << certfp << " to " << nc->display;
source.Reply(_("\002%s\002 added to %s's certificate list."), certfp.c_str(), nc->display.c_str()); source.Reply(_("\002%s\002 added to %s's certificate list."), certfp.c_str(), nc->display.c_str());
} }
@@ -278,7 +362,7 @@ private:
source.Reply(_("\002%s\002 deleted from %s's certificate list."), certfp.c_str(), nc->display.c_str()); source.Reply(_("\002%s\002 deleted from %s's certificate list."), certfp.c_str(), nc->display.c_str());
} }
static void DoList(CommandSource &source, const NickCore *nc) static void DoList(CommandSource &source, const NickCore *nc, bool full)
{ {
auto *cl = nc->GetExt<NickServ::CertList>(NICKSERV_CERT_EXT); auto *cl = nc->GetExt<NickServ::CertList>(NICKSERV_CERT_EXT);
@@ -288,12 +372,51 @@ private:
return; return;
} }
source.Reply(_("Certificate list for %s:"), nc->display.c_str()); ListFormatter list(source.GetAccount());
list.AddColumn(_("Fingerprint"));
if (full)
{
list.AddColumn(_("Creator")).AddColumn(_("Created"));
list.SetFlexible([](ListFormatter::ListEntry &row)
{
return row["Description"].empty()
? _("\002{fingerprint}\002 -- created by {creator} at {created}")
: _("\002{fingerprint}\002 -- created by {creator} at {created} ({description})");
});
}
else
{
list.SetFlexible([](ListFormatter::ListEntry &row)
{
return row["Description"].empty()
? _("\002{fingerprint}\002")
: _("\002{fingerprint}\002 ({description})");
});
}
list.AddColumn(_("Description"));
for (unsigned i = 0; i < cl->GetCertCount(); ++i) for (unsigned i = 0; i < cl->GetCertCount(); ++i)
{ {
Anope::string fingerprint = cl->GetCert(i); auto *cert = cl->GetCert(i);
source.Reply(" %s", fingerprint.c_str()); ListFormatter::ListEntry entry;
entry["Fingerprint"] = cert->fingerprint;
entry["Description"] = cert->description;
if (full)
{
entry["Created"] = cert->created
? Anope::strftime(cert->created, nullptr, true)
: TIME_UNKNOWN;
entry["Creator"] = cert->creator.empty()
? TIME_UNKNOWN
: cert->creator;
}
list.AddEntry(entry);
} }
source.Reply(_("Certificate list for %s:"), nc->display.c_str());
list.SendTo(source);
} }
public: public:
@@ -303,6 +426,7 @@ public:
this->SetSyntax(_("ADD [\037nickname\037] [\037fingerprint\037]")); this->SetSyntax(_("ADD [\037nickname\037] [\037fingerprint\037]"));
this->SetSyntax(_("DEL [\037nickname\037] \037fingerprint\037")); this->SetSyntax(_("DEL [\037nickname\037] \037fingerprint\037"));
this->SetSyntax(_("LIST [\037nickname\037]")); this->SetSyntax(_("LIST [\037nickname\037]"));
this->SetSyntax(_("VIEW [\037nickname\037]"));
} }
void Execute(CommandSource &source, const std::vector<Anope::string> &params) override void Execute(CommandSource &source, const std::vector<Anope::string> &params) override
@@ -310,7 +434,7 @@ public:
const Anope::string &cmd = params[0]; const Anope::string &cmd = params[0];
Anope::string nick, certfp; Anope::string nick, certfp;
if (cmd.equals_ci("LIST")) if (cmd.equals_ci("LIST") || cmd.equals_ci("VIEW"))
nick = params.size() > 1 ? params[1] : ""; nick = params.size() > 1 ? params[1] : "";
else else
{ {
@@ -344,7 +468,9 @@ public:
nc = source.nc; nc = source.nc;
if (cmd.equals_ci("LIST")) if (cmd.equals_ci("LIST"))
return this->DoList(source, nc); return this->DoList(source, nc, false);
if (cmd.equals_ci("VIEW"))
return this->DoList(source, nc, true);
else if (nc->HasExt("NS_SUSPENDED")) else if (nc->HasExt("NS_SUSPENDED"))
source.Reply(NICK_X_SUSPENDED, nc->display.c_str()); source.Reply(NICK_X_SUSPENDED, nc->display.c_str());
else if (Anope::ReadOnly) else if (Anope::ReadOnly)
@@ -498,6 +624,7 @@ private:
CommandNSSASetAutologin commandnssasetautologin; CommandNSSASetAutologin commandnssasetautologin;
NSCertListImpl::ExtensibleItem certs; NSCertListImpl::ExtensibleItem certs;
CertServiceImpl cs; CertServiceImpl cs;
NSCertInfoType cert_type;
bool CanLogin(User *u, NickCore *nc) bool CanLogin(User *u, NickCore *nc)
{ {
@@ -558,7 +685,9 @@ public:
return; return;
auto *cl = certs.Require(na->nc); auto *cl = certs.Require(na->nc);
cl->AddCert(u->fingerprint); auto *cert = cl->AddCert(u->fingerprint);
cert->created = Anope::CurTime;
cert->creator = u->nick;
auto *NickServ = Config->GetClient("NickServ"); auto *NickServ = Config->GetClient("NickServ");
u->SendMessage(NickServ, _("Your SSL certificate fingerprint \002%s\002 has been automatically added to your certificate list."), u->fingerprint.c_str()); u->SendMessage(NickServ, _("Your SSL certificate fingerprint \002%s\002 has been automatically added to your certificate list."), u->fingerprint.c_str());
+1 -1
View File
@@ -41,7 +41,7 @@ bool WebCPanel::NickServ::Cert::OnRequest(HTTP::Provider *server, const Anope::s
auto *cl = na->nc->GetExt<::NickServ::CertList>(NICKSERV_CERT_EXT); auto *cl = na->nc->GetExt<::NickServ::CertList>(NICKSERV_CERT_EXT);
if (cl) if (cl)
for (unsigned i = 0; i < cl->GetCertCount(); ++i) for (unsigned i = 0; i < cl->GetCertCount(); ++i)
replacements["CERTS"] = cl->GetCert(i); replacements["CERTS"] = cl->GetCert(i)->fingerprint;
TemplateFileServer page("nickserv/cert.html"); TemplateFileServer page("nickserv/cert.html");
page.Serve(server, page_name, client, message, reply, replacements); page.Serve(server, page_name, client, message, reply, replacements);