|
|
@ -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")); |