Browse Source

Add tokauth and webstatus modules

master
Tyler Sommer 2 years ago
parent
commit
b5c2e67aaa
Signed by: tyler-sommer GPG Key ID: C09C010500DBD008
3 changed files with 193 additions and 0 deletions
  1. +22
    -0
      modules/data/tokauth/tmpl/index.tmpl
  2. +131
    -0
      modules/tokauth.cpp
  3. +40
    -0
      modules/webstatus.cpp

+ 22
- 0
modules/data/tokauth/tmpl/index.tmpl View File

@ -0,0 +1,22 @@
<? I18N znc-tokauth ?>
<? INC Header.tmpl ?>
<form method="post" action="<? VAR URIPrefix TOP ?><? VAR ModPath ?>set_token">
<? INC _csrf_check.tmpl ?>
<div class="section">
<h3><? FORMAT "Set token" ?></h3>
<div class="sectionbg">
<div class="sectionbody">
<div class="subsection full">
<div class="inputlabel"><? FORMAT "Token:" ?></div>
<input type="text" name="token" size="40"/>
</div>
<div class="subsection submitline">
<input type="submit" name="add" value="<? FORMAT "Set Token" ?>" />
</div>
</div>
</div>
</div>
</form>
<? INC Footer.tmpl ?>

+ 131
- 0
modules/tokauth.cpp View File

@ -0,0 +1,131 @@
#include <znc/User.h>
#include <znc/znc.h>
static CString t_s(const char *str) {
return CString(str);
}
class CTokenAuthMod : public CModule {
public:
MODCONSTRUCTOR(CTokenAuthMod) {
m_Cache.SetTTL(60000 /*ms*/);
}
~CTokenAuthMod() override {}
bool OnLoad(const CString &sArgs, CString &sMessage) override;
bool Save() {
ClearNV(false);
SetNV("__salt", m_Salt, false);
for (const auto& it : m_Tokens) {
CString sVal = it.second;
if (!sVal.empty()) SetNV(it.first, sVal, false);
}
return SaveRegistry();
}
EModRet OnLoginAttempt(std::shared_ptr<CAuthBase> Auth) override {
const CString &sUsername = Auth->GetUsername();
const CString &sToken = Auth->GetPassword();
CUser *pUser = CZNC::Get().FindUser(sUsername);
bool bSuccess = false;
if (!pUser) {
DEBUG("tokauth: Did not find matching user [" + sUsername + "] - skipping auth");
return CONTINUE;
}
if (m_Tokens[sUsername].empty()) {
DEBUG("tokauth: User has not configured token [" + sUsername + "] - skipping auth");
return CONTINUE;
}
const CString sRemoteIP = Auth->GetRemoteIP();
const CString sCacheKey = CString(sUsername + ":" + sToken + ":" + sRemoteIP).SHA256();
if (m_Cache.HasItem(sCacheKey)) {
bSuccess = true;
DEBUG("tokauth: Found [" + sUsername + "] in cache");
} else {
const CString sHashed = CString(sToken + ":" + m_Salt).SHA256();
if (sHashed == m_Tokens[sUsername]) {
m_Cache.AddItem(sCacheKey);
bSuccess = true;
DEBUG("tokauth: Successful token authentication [" + sUsername + "]");
}
}
if (bSuccess) {
Auth->AcceptLogin(*pUser);
return HALT;
}
return CONTINUE;
}
CString GetWebMenuTitle() override { return "tokauth"; }
bool OnWebRequest(CWebSock &WebSock, const CString &sPageName,
CTemplate &Tmpl) override {
CUser* pUser = WebSock.GetSession()->GetUser();
const CString &sUsername = pUser->GetUsername();
if (sPageName == "index") {
return true;
} else if (sPageName == "set_token" && WebSock.IsPost()) {
std::shared_ptr<CWebSession> spSession = WebSock.GetSession();
if (!ValidateWebRequestCSRFCheck(WebSock, sPageName)) {
spSession->AddError("failed to set token. csrf failed");
WebSock.Redirect(GetWebPath());
return true;
}
const CString sToken = WebSock.GetParam("token", true);
if (sToken.length() == 0) {
spSession->AddError("failed to set token. token cannot be blank");
} else {
const CString sHashed = CString(sToken + ":" + m_Salt).SHA256();
DEBUG("webstatus: Updated token for [" + sUsername + "] SHA256: [" + sHashed + "]");
m_Tokens[sUsername] = sHashed;
Save();
spSession->AddSuccess("set auth token successfully");
}
WebSock.Redirect(GetWebPath());
return true;
}
return false;
}
protected:
TCacheMap<CString> m_Cache;
MCString m_Tokens;
CString m_Salt;
};
bool CTokenAuthMod::OnLoad(const CString &sArgs, CString &sMessage) {
if (!HasNV("__salt")) {
SetNV("__salt", CUtils::GetSalt());
}
m_Salt = GetNV("__salt");
for (MCString::const_iterator it = BeginNV(); it != EndNV(); ++it) {
if (it->first == "__salt") {
continue;
}
if (CZNC::Get().FindUser(it->first) == nullptr) {
DEBUG("Unknown user in saved data [" + it->first + "]");
continue;
}
m_Tokens[it->first] = it->second;
}
return true;
}
template<>
void TModInfo<CTokenAuthMod>(CModInfo &Info) {}
GLOBALMODULEDEFS(CTokenAuthMod, t_s("lets users set a token for use as an "
"alternative to username and password"));

+ 40
- 0
modules/webstatus.cpp View File

@ -0,0 +1,40 @@
#include <znc/User.h>
#include <znc/znc.h>
static CString t_s(const char *str) {
return CString(str);
}
class CWebStatus : public CModule {
private:
bool IndexPage(CWebSock &WebSock) {
const CString &sResult = "{\"result\":\"ok\"}\r\n";
WebSock.PrintHeader(sResult.length(), "application/json", 200, "OK");
WebSock.Write(sResult);
WebSock.Close(Csock::CLT_AFTERWRITE);
return true;
}
public:
MODCONSTRUCTOR(CWebStatus) {}
~CWebStatus() override {}
bool OnWebRequest(CWebSock &WebSock, const CString &sPageName,
CTemplate &Tmpl) override {
CUser* pUser = WebSock.GetSession()->GetUser();
const CString &sUsername = pUser->GetUsername();
if (sPageName == "index") {
return IndexPage(WebSock);
}
return false;
}
};
template<>
void TModInfo<CWebStatus>(CModInfo &Info) {}
GLOBALMODULEDEFS(CWebStatus, t_s("prints a json blob with the znc daemon "
"status info using a configurable token"));

Loading…
Cancel
Save