forked from unofficial-mirrors/znc
3 changed files with 193 additions and 0 deletions
@ -0,0 +1,22 @@
@@ -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 ?> |
@ -0,0 +1,131 @@
@@ -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")); |
@ -0,0 +1,40 @@
@@ -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…
Reference in new issue