Files
wlan-cloud-ucentralsec/src/AuthService.cpp
2023-12-05 07:16:32 -08:00

963 lines
32 KiB
C++

//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <ctime>
#include "framework/KafkaManager.h"
#include "framework/KafkaTopics.h"
#include "Poco/JWT/Signer.h"
#include "Poco/JWT/Token.h"
#include "Poco/Net/OAuth20Credentials.h"
#include "Poco/StringTokenizer.h"
#include "AuthService.h"
#include "StorageService.h"
#include "framework/MicroServiceFuncs.h"
#include "MFAServer.h"
#include "MessagingTemplates.h"
#include "SMTPMailerService.h"
namespace OpenWifi {
AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) {
switch (C) {
case 1:
return USERNAME;
case 2:
return SERVER;
case 3:
return CUSTOM;
default:
return USERNAME;
}
}
int AuthService::AccessTypeToInt(ACCESS_TYPE T) {
switch (T) {
case USERNAME:
return 1;
case SERVER:
return 2;
case CUSTOM:
return 3;
}
return 1; // some compilers complain...
}
#if defined(TIP_CERT_SERVICE)
static const std::string DefaultPasswordRule{
"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[\\{\\}\\(\\)~_\\+\\|\\\\\\[\\]\\;\\:\\<\\>\\."
"\\,\\/\\?\\\"\\'\\`\\=#?!@$%^&*-]).{12,}$"};
#else
static const std::string DefaultPasswordRule{
"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[\\{\\}\\(\\)~_\\+\\|\\\\\\[\\]\\;\\:\\<\\>\\."
"\\,\\/\\?\\\"\\'\\`\\=#?!@$%^&*-]).{8,}$"};
#endif
int AuthService::Start() {
poco_information(Logger(), "Starting...");
TokenAging_ =
(uint64_t)MicroServiceConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60);
RefreshTokenLifeSpan_ = (uint64_t)MicroServiceConfigGetInt(
"authentication.refresh_token.lifespan", 90 * 24 * 60 * 600);
HowManyOldPassword_ = MicroServiceConfigGetInt("authentication.oldpasswords", 5);
AccessPolicy_ = MicroServiceConfigGetString("openwifi.document.policy.access",
"/wwwassets/access_policy.html");
PasswordPolicy_ = MicroServiceConfigGetString("openwifi.document.policy.password",
"/wwwassets/password_policy.html");
PasswordValidation_ = PasswordValidationStr_ = MicroServiceConfigGetString(
"authentication.validation.expression", DefaultPasswordRule);
SubPasswordValidation_ = SubPasswordValidationStr_ =
MicroServiceConfigGetString("subscriber.validation.expression", DefaultPasswordRule);
SubAccessPolicy_ = MicroServiceConfigGetString("subscriber.policy.access",
"/wwwassets/access_policy.html");
SubPasswordPolicy_ = MicroServiceConfigGetString("subscriber.policy.password",
"/wwwassets/password_policy.html");
HelperEmail_ =
MicroServiceConfigGetString("helper.user.email", "openwifi@telecominfraproject.com");
SubHelperEmail_ =
MicroServiceConfigGetString("helper.sub.email", "openwifi@telecominfraproject.com");
GlobalHelperEmail_ = MicroServiceConfigGetString("helper.user.global.email",
"openwifi@telecominfraproject.com");
GlobalSubHelperEmail_ = MicroServiceConfigGetString("helper.sub.global.email",
"openwifi@telecominfraproject.com");
HelperSite_ = MicroServiceConfigGetString("helper.user.site", "telecominfraproject.com");
SubHelperSite_ = MicroServiceConfigGetString("helper.sub.site", "telecominfraproject.com");
SystemLoginSite_ =
MicroServiceConfigGetString("helper.user.login", "telecominfraproject.com");
SubSystemLoginSite_ =
MicroServiceConfigGetString("helper.sub.login", "telecominfraproject.com");
UserSignature_ =
MicroServiceConfigGetString("helper.user.signature", "Telecom Infra Project");
SubSignature_ =
MicroServiceConfigGetString("helper.sub.signature", "Telecom Infra Project");
return 0;
}
void AuthService::Stop() {
poco_information(Logger(), "Stopping...");
poco_information(Logger(), "Stopped...");
}
bool AuthService::RefreshUserToken(Poco::Net::HTTPServerRequest &Request,
const std::string &RefreshToken,
SecurityObjects::UserInfoAndPolicy &UI) {
try {
std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
return false;
}
uint64_t RevocationDate = 0;
std::string UserId;
if (StorageService()->UserTokenDB().GetToken(CallToken, UI.webtoken, UserId,
RevocationDate) &&
UI.webtoken.refresh_token_ == RefreshToken) {
auto now = Utils::Now();
// Create a new token
auto NewToken = GenerateTokenHMAC(UI.webtoken.access_token_, CUSTOM);
auto NewRefreshToken = RefreshToken;
if (now - UI.webtoken.lastRefresh_ < RefreshTokenLifeSpan_) {
NewRefreshToken = GenerateTokenHMAC(UI.webtoken.refresh_token_, CUSTOM);
UI.webtoken.lastRefresh_ = now;
}
StorageService()->UserTokenDB().RefreshToken(CallToken, NewToken, NewRefreshToken,
UI.webtoken.lastRefresh_);
UI.webtoken.access_token_ = NewToken;
UI.webtoken.refresh_token_ = NewRefreshToken;
return true;
}
return false;
} catch (...) {
}
return false;
}
bool AuthService::RefreshSubToken(Poco::Net::HTTPServerRequest &Request,
const std::string &RefreshToken,
SecurityObjects::UserInfoAndPolicy &UI) {
try {
std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
return false;
}
uint64_t RevocationDate = 0;
std::string UserId;
if (StorageService()->SubTokenDB().GetToken(CallToken, UI.webtoken, UserId,
RevocationDate) &&
UI.webtoken.refresh_token_ == RefreshToken) {
auto now = Utils::Now();
// Create a new token
auto NewToken = GenerateTokenHMAC(UI.webtoken.access_token_, CUSTOM);
auto NewRefreshToken = RefreshToken;
if (now - UI.webtoken.lastRefresh_ < RefreshTokenLifeSpan_) {
NewRefreshToken = GenerateTokenHMAC(UI.webtoken.refresh_token_, CUSTOM);
UI.webtoken.lastRefresh_ = now;
}
StorageService()->SubTokenDB().RefreshToken(CallToken, NewToken, NewRefreshToken,
UI.webtoken.lastRefresh_);
UI.webtoken.access_token_ = NewToken;
UI.webtoken.refresh_token_ = NewRefreshToken;
return true;
}
return false;
} catch (...) {
}
return false;
}
[[nodiscard]] bool AuthService::IsAuthorized(const std::string &SessionToken,
SecurityObjects::UserInfoAndPolicy &UInfo,
std::uint64_t TID, bool &Expired) {
// std::lock_guard Guard(Mutex_);
std::string CallToken{SessionToken};
Expired = false;
try {
SecurityObjects::WebToken WT;
uint64_t RevocationDate = 0;
std::string UserId;
if (StorageService()->UserTokenDB().GetToken(CallToken, WT, UserId, RevocationDate)) {
if (RevocationDate != 0) {
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}",
TID, Utils::SanitizeToken(CallToken)));
return false;
}
auto now = Utils::Now();
Expired = (WT.created_ + WT.expires_in_) < now;
if (StorageService()->UserDB().GetUserById(UserId, UInfo.userinfo)) {
UInfo.webtoken = WT;
poco_trace(Logger(), fmt::format("TokenValidation success for TID={} Token={}",
TID, Utils::SanitizeToken(CallToken)));
return true;
}
}
} catch (const Poco::Exception &E) {
Logger().log(E);
}
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID,
Utils::SanitizeToken(CallToken)));
return false;
}
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest &Request, std::string &SessionToken,
SecurityObjects::UserInfoAndPolicy &UInfo, std::uint64_t TID,
bool &Expired) {
// std::lock_guard Guard(Mutex_);
std::string CallToken;
Expired = false;
try {
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID,
Utils::SanitizeToken(CallToken)));
return false;
}
SessionToken = CallToken;
return IsAuthorized(SessionToken, UInfo, TID, Expired);
} catch (const Poco::Exception &E) {
Logger().log(E);
}
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID,
Utils::SanitizeToken(CallToken)));
return false;
}
bool AuthService::IsSubAuthorized(Poco::Net::HTTPServerRequest &Request,
std::string &SessionToken,
SecurityObjects::UserInfoAndPolicy &UInfo, std::uint64_t TID,
bool &Expired) {
// std::lock_guard Guard(Mutex_);
std::string CallToken;
Expired = false;
try {
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if (CallToken.empty()) {
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID,
Utils::SanitizeToken(CallToken)));
return false;
}
SecurityObjects::WebToken WT;
uint64_t RevocationDate = 0;
std::string UserId;
if (StorageService()->SubTokenDB().GetToken(CallToken, WT, UserId, RevocationDate)) {
if (RevocationDate != 0) {
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}",
TID, Utils::SanitizeToken(CallToken)));
return false;
}
auto now = Utils::Now();
Expired = (WT.created_ + WT.expires_in_) < now;
if (StorageService()->SubDB().GetUserById(UserId, UInfo.userinfo)) {
UInfo.webtoken = WT;
SessionToken = CallToken;
poco_debug(Logger(), fmt::format("TokenValidation success for TID={} Token={}",
TID, Utils::SanitizeToken(CallToken)));
return true;
}
}
} catch (const Poco::Exception &E) {
Logger().log(E);
}
poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID,
Utils::SanitizeToken(CallToken)));
return false;
}
void AuthService::RevokeToken(std::string &Token) {
StorageService()->UserTokenDB().RevokeToken(Token);
}
void AuthService::RevokeSubToken(std::string &Token) {
StorageService()->SubTokenDB().RevokeToken(Token);
}
bool AuthService::DeleteUserFromCache(const std::string &Id) {
return StorageService()->UserTokenDB().DeleteRecordsFromCache("userName", Id);
}
bool AuthService::DeleteSubUserFromCache(const std::string &Id) {
return StorageService()->SubTokenDB().DeleteRecordsFromCache("userName", Id);
}
bool AuthService::RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo) {
return (UInfo.userinfo.userTypeProprietaryInfo.mfa.enabled &&
MFAServer::MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method));
}
bool AuthService::ValidatePassword(const std::string &Password) {
return std::regex_match(Password, PasswordValidation_);
}
bool AuthService::ValidateSubPassword(const std::string &Password) {
return std::regex_match(Password, SubPasswordValidation_);
}
void AuthService::RemoveTokenSystemWide(const std::string &token) {
try {
if (KafkaManager()->Enabled()) {
Poco::JSON::Object Obj;
Obj.set("event", "remove-token");
Obj.set("id", MicroServiceID());
Obj.set("token", token);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,
MicroServicePrivateEndPoint(), Obj, false);
}
} catch (const Poco::Exception &E) {
Logger().log(E);
}
}
void AuthService::Logout(const std::string &Token, [[maybe_unused]] bool EraseFromCache) {
std::lock_guard Guard(Mutex_);
try {
auto tToken{Token};
StorageService()->UserTokenDB().DeleteRecord("token", tToken);
StorageService()->LoginDB().AddLogout(Token);
} catch (const Poco::Exception &E) {
Logger().log(E);
}
}
void AuthService::SubLogout(const std::string &Token, [[maybe_unused]] bool EraseFromCache) {
std::lock_guard Guard(Mutex_);
try {
auto tToken{Token};
StorageService()->SubTokenDB().DeleteRecord("token", tToken);
StorageService()->SubLoginDB().AddLogout(Token);
} catch (const Poco::Exception &E) {
Logger().log(E);
}
}
[[nodiscard]] std::string AuthService::GenerateTokenHMAC(const std::string &UserName,
[[maybe_unused]] ACCESS_TYPE Type) {
std::string Identity(UserName + ":" + fmt::format("{}", Utils::Now()) + ":" +
std::to_string(rand()));
HMAC_.update(Identity);
return Poco::DigestEngine::digestToHex(HMAC_.digest());
}
std::string AuthService::GenerateTokenJWT(const std::string &Identity, ACCESS_TYPE Type) {
std::lock_guard Guard(Mutex_);
Poco::JWT::Token T;
T.setType("JWT");
switch (Type) {
case USERNAME:
T.setSubject("usertoken");
break;
case SERVER:
T.setSubject("servertoken");
break;
case CUSTOM:
T.setSubject("customtoken");
break;
}
T.payload().set("identity", Identity);
T.setIssuedAt(Poco::Timestamp());
T.setExpiration(Poco::Timestamp() + (long long)TokenAging_);
std::string JWT = MicroServiceSign(T, Poco::JWT::Signer::ALGO_RS256);
return JWT;
}
void AuthService::CreateToken(const std::string &UserName,
SecurityObjects::UserInfoAndPolicy &UInfo) {
std::lock_guard Guard(Mutex_);
SecurityObjects::AclTemplate ACL;
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
UInfo.webtoken.acl_template_ = ACL;
UInfo.webtoken.expires_in_ = TokenAging_;
UInfo.webtoken.idle_timeout_ = 5 * 60;
UInfo.webtoken.token_type_ = "Bearer";
UInfo.webtoken.access_token_ = GenerateTokenHMAC(UInfo.userinfo.id, USERNAME);
UInfo.webtoken.id_token_ = GenerateTokenHMAC(UInfo.userinfo.id, USERNAME);
UInfo.webtoken.refresh_token_ = GenerateTokenHMAC(UInfo.userinfo.id, CUSTOM);
UInfo.webtoken.created_ = time(nullptr);
UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false;
StorageService()->UserDB().SetLastLogin(UInfo.userinfo.id);
StorageService()->UserTokenDB().AddToken(
UInfo.userinfo.id, UInfo.webtoken.access_token_, UInfo.webtoken.refresh_token_,
UInfo.webtoken.token_type_, UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
StorageService()->LoginDB().AddLogin(UInfo.userinfo.id, UInfo.userinfo.email,
UInfo.webtoken.access_token_);
}
void AuthService::CreateSubToken(const std::string &UserName,
SecurityObjects::UserInfoAndPolicy &UInfo) {
std::lock_guard Guard(Mutex_);
SecurityObjects::AclTemplate ACL;
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
UInfo.webtoken.acl_template_ = ACL;
UInfo.webtoken.expires_in_ = TokenAging_;
UInfo.webtoken.idle_timeout_ = 5 * 60;
UInfo.webtoken.token_type_ = "Bearer";
UInfo.webtoken.access_token_ = GenerateTokenHMAC(UInfo.userinfo.id, USERNAME);
UInfo.webtoken.id_token_ = GenerateTokenHMAC(UInfo.userinfo.id, USERNAME);
UInfo.webtoken.refresh_token_ = GenerateTokenHMAC(UInfo.userinfo.id, CUSTOM);
UInfo.webtoken.created_ = time(nullptr);
UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false;
StorageService()->SubDB().SetLastLogin(UInfo.userinfo.id);
StorageService()->SubTokenDB().AddToken(
UInfo.userinfo.id, UInfo.webtoken.access_token_, UInfo.webtoken.refresh_token_,
UInfo.webtoken.token_type_, UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
StorageService()->SubLoginDB().AddLogin(UInfo.userinfo.id, UInfo.userinfo.email,
UInfo.webtoken.access_token_);
}
bool AuthService::SetPassword(const std::string &NewPassword,
SecurityObjects::UserInfo &UInfo) {
std::lock_guard G(Mutex_);
Poco::toLowerInPlace(UInfo.email);
for (const auto &i : UInfo.lastPasswords) {
auto Tokens = Poco::StringTokenizer(i, "|");
if (Tokens.count() == 2) {
const auto &Salt = Tokens[0];
for (const auto &j : UInfo.lastPasswords) {
auto OldTokens = Poco::StringTokenizer(j, "|");
if (OldTokens.count() == 2) {
SHA2_.update(Salt + NewPassword + UInfo.email);
if (OldTokens[1] == Utils::ToHex(SHA2_.digest()))
return false;
}
}
} else {
SHA2_.update(NewPassword + UInfo.email);
if (Tokens[0] == Utils::ToHex(SHA2_.digest()))
return false;
}
}
if (UInfo.lastPasswords.size() == HowManyOldPassword_) {
UInfo.lastPasswords.erase(UInfo.lastPasswords.begin());
}
auto NewHash = ComputeNewPasswordHash(UInfo.email, NewPassword);
UInfo.lastPasswords.push_back(NewHash);
UInfo.currentPassword = NewHash;
UInfo.changePassword = false;
return true;
}
bool AuthService::SetSubPassword(const std::string &NewPassword,
SecurityObjects::UserInfo &UInfo) {
std::lock_guard G(Mutex_);
Poco::toLowerInPlace(UInfo.email);
for (const auto &i : UInfo.lastPasswords) {
auto Tokens = Poco::StringTokenizer(i, "|");
if (Tokens.count() == 2) {
const auto &Salt = Tokens[0];
for (const auto &j : UInfo.lastPasswords) {
auto OldTokens = Poco::StringTokenizer(j, "|");
if (OldTokens.count() == 2) {
SHA2_.update(Salt + NewPassword + UInfo.email);
if (OldTokens[1] == Utils::ToHex(SHA2_.digest()))
return false;
}
}
} else {
SHA2_.update(NewPassword + UInfo.email);
if (Tokens[0] == Utils::ToHex(SHA2_.digest()))
return false;
}
}
if (UInfo.lastPasswords.size() == HowManyOldPassword_) {
UInfo.lastPasswords.erase(UInfo.lastPasswords.begin());
}
auto NewHash = ComputeNewPasswordHash(UInfo.email, NewPassword);
UInfo.lastPasswords.push_back(NewHash);
UInfo.currentPassword = NewHash;
UInfo.changePassword = false;
return true;
}
static std::string GetMeSomeSalt() {
auto start = std::chrono::high_resolution_clock::now();
return std::to_string(start.time_since_epoch().count());
}
std::string AuthService::ComputeNewPasswordHash(const std::string &UserName,
const std::string &Password) {
std::string UName = Poco::trim(Poco::toLower(UserName));
auto Salt = GetMeSomeSalt();
SHA2_.update(Salt + Password + UName);
return Salt + "|" + Utils::ToHex(SHA2_.digest());
}
bool AuthService::ValidatePasswordHash(const std::string &UserName, const std::string &Password,
const std::string &StoredPassword) {
std::lock_guard G(Mutex_);
std::string UName = Poco::trim(Poco::toLower(UserName));
auto Tokens = Poco::StringTokenizer(StoredPassword, "|");
if (Tokens.count() == 1) {
SHA2_.update(Password + UName);
if (Tokens[0] == Utils::ToHex(SHA2_.digest()))
return true;
} else if (Tokens.count() == 2) {
SHA2_.update(Tokens[0] + Password + UName);
if (Tokens[1] == Utils::ToHex(SHA2_.digest()))
return true;
}
return false;
}
bool AuthService::ValidateSubPasswordHash(const std::string &UserName,
const std::string &Password,
const std::string &StoredPassword) {
std::lock_guard G(Mutex_);
std::string UName = Poco::trim(Poco::toLower(UserName));
auto Tokens = Poco::StringTokenizer(StoredPassword, "|");
if (Tokens.count() == 1) {
SHA2_.update(Password + UName);
if (Tokens[0] == Utils::ToHex(SHA2_.digest()))
return true;
} else if (Tokens.count() == 2) {
SHA2_.update(Tokens[0] + Password + UName);
if (Tokens[1] == Utils::ToHex(SHA2_.digest()))
return true;
}
return false;
}
UNAUTHORIZED_REASON AuthService::Authorize(std::string &UserName, const std::string &Password,
const std::string &NewPassword,
SecurityObjects::UserInfoAndPolicy &UInfo,
[[maybe_unused]] bool &Expired) {
std::lock_guard Guard(Mutex_);
Poco::toLowerInPlace(UserName);
if (StorageService()->UserDB().GetUserByEmail(UserName, UInfo.userinfo)) {
if (UInfo.userinfo.suspended) {
return ACCOUNT_SUSPENDED;
}
if (UInfo.userinfo.waitingForEmailCheck) {
return USERNAME_PENDING_VERIFICATION;
}
if (!ValidatePasswordHash(UserName, Password, UInfo.userinfo.currentPassword)) {
return INVALID_CREDENTIALS;
}
if (UInfo.userinfo.changePassword && NewPassword.empty()) {
UInfo.webtoken.userMustChangePassword = true;
return PASSWORD_CHANGE_REQUIRED;
}
if (!NewPassword.empty() && !ValidatePassword(NewPassword)) {
return PASSWORD_INVALID;
}
if (UInfo.userinfo.changePassword || !NewPassword.empty()) {
if (!SetPassword(NewPassword, UInfo.userinfo)) {
UInfo.webtoken.errorCode = 1;
return PASSWORD_ALREADY_USED;
}
UInfo.userinfo.lastPasswordChange = Utils::Now();
UInfo.userinfo.changePassword = false;
UInfo.userinfo.modified = Utils::Now();
StorageService()->UserDB().UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.id,
UInfo.userinfo);
}
// so we have a good password, password up date has taken place if need be, now
// generate the token.
UInfo.userinfo.lastLogin = Utils::Now();
StorageService()->UserDB().SetLastLogin(UInfo.userinfo.id);
CreateToken(UserName, UInfo);
return SUCCESS;
}
return INVALID_CREDENTIALS;
}
UNAUTHORIZED_REASON AuthService::AuthorizeSub(std::string &UserName,
const std::string &Password,
const std::string &NewPassword,
SecurityObjects::UserInfoAndPolicy &UInfo,
[[maybe_unused]] bool &Expired) {
std::lock_guard Guard(Mutex_);
Poco::toLowerInPlace(UserName);
if (StorageService()->SubDB().GetUserByEmail(UserName, UInfo.userinfo)) {
if (UInfo.userinfo.suspended) {
return ACCOUNT_SUSPENDED;
}
if (UInfo.userinfo.waitingForEmailCheck) {
return USERNAME_PENDING_VERIFICATION;
}
if (!ValidateSubPasswordHash(UserName, Password, UInfo.userinfo.currentPassword)) {
return INVALID_CREDENTIALS;
}
if (UInfo.userinfo.changePassword && NewPassword.empty()) {
UInfo.webtoken.userMustChangePassword = true;
return PASSWORD_CHANGE_REQUIRED;
}
if (!NewPassword.empty() && !ValidateSubPassword(NewPassword)) {
return PASSWORD_INVALID;
}
if (UInfo.userinfo.changePassword || !NewPassword.empty()) {
if (!SetSubPassword(NewPassword, UInfo.userinfo)) {
UInfo.webtoken.errorCode = 1;
return PASSWORD_ALREADY_USED;
}
UInfo.userinfo.lastPasswordChange = Utils::Now();
UInfo.userinfo.changePassword = false;
UInfo.userinfo.modified = Utils::Now();
StorageService()->SubDB().UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.id,
UInfo.userinfo);
}
// so we have a good password, password update has taken place if need be, now generate
// the token.
UInfo.userinfo.lastLogin = Utils::Now();
StorageService()->SubDB().SetLastLogin(UInfo.userinfo.id);
CreateSubToken(UserName, UInfo);
return SUCCESS;
}
return INVALID_CREDENTIALS;
}
bool AuthService::SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo,
const std::string &Challenge) {
auto OperatorParts = Poco::StringTokenizer(UInfo.userinfo.signingUp, ":");
bool IsSub = UInfo.userinfo.userRole == SecurityObjects::SUBSCRIBER;
if (UInfo.userinfo.signingUp.empty() || OperatorParts.count() != 2) {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email;
Attrs[LOGO] = AuthService::GetLogoAssetURI();
Attrs[SUBJECT] = "Login validation code";
Attrs[CHALLENGE_CODE] = Challenge;
if (!IsSub) {
SMTPMailerService()->AddUserVars(Attrs);
} else {
SMTPMailerService()->AddSubVars(Attrs);
}
return SMTPMailerService()->SendMessage(
UInfo.userinfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::VERIFICATION_CODE), Attrs,
false);
} else {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email;
Attrs[LOGO] = AuthService::GetSubLogoAssetURI();
Attrs[SUBJECT] = "Login validation code";
Attrs[CHALLENGE_CODE] = Challenge;
if (!IsSub) {
SMTPMailerService()->AddUserVars(Attrs);
} else {
SMTPMailerService()->AddSubVars(Attrs);
}
return SMTPMailerService()->SendMessage(
UInfo.userinfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::SUB_VERIFICATION_CODE,
OperatorParts[0]),
Attrs, true);
}
}
bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email,
MessagingTemplates::EMAIL_REASON Reason) {
SecurityObjects::UserInfo UInfo;
if (StorageService()->UserDB().GetUserByEmail(Email, UInfo)) {
switch (Reason) {
case MessagingTemplates::FORGOT_PASSWORD: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetLogoAssetURI();
Attrs[SUBJECT] = "Password reset link";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=password_reset&id=" + LinkId;
Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=password_reset&id=" + LinkId;
SMTPMailerService()->AddUserVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::FORGOT_PASSWORD), Attrs,
false);
} break;
case MessagingTemplates::EMAIL_VERIFICATION: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetLogoAssetURI();
Attrs[SUBJECT] = "e-mail Address Verification";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=email_verification&id=" + LinkId;
Attrs[ACTION_LINK_HTML] =
"/api/v1/actionLink?action=email_verification&id=" + LinkId;
SMTPMailerService()->AddUserVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_VERIFICATION), Attrs,
false);
UInfo.waitingForEmailCheck = true;
} break;
case MessagingTemplates::EMAIL_INVITATION: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetLogoAssetURI();
Attrs[SUBJECT] = "e-mail Invitation";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=email_invitation&id=" + LinkId;
Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_invitation&id=" + LinkId;
SMTPMailerService()->AddUserVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_INVITATION), Attrs,
false);
UInfo.waitingForEmailCheck = true;
} break;
default:
break;
}
return true;
}
return false;
}
bool AuthService::SendEmailToSubUser(const std::string &LinkId, std::string &Email,
MessagingTemplates::EMAIL_REASON Reason,
const std::string &OperatorName) {
SecurityObjects::UserInfo UInfo;
if (StorageService()->SubDB().GetUserByEmail(Email, UInfo)) {
switch (Reason) {
case MessagingTemplates::SUB_FORGOT_PASSWORD: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetSubLogoAssetURI();
Attrs[SUBJECT] = "Password reset link";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=sub_password_reset&id=" + LinkId;
Attrs[ACTION_LINK_HTML] =
"/api/v1/actionLink?action=sub_password_reset&id=" + LinkId;
SMTPMailerService()->AddSubVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::SUB_FORGOT_PASSWORD,
OperatorName),
Attrs, true);
} break;
case MessagingTemplates::SUB_EMAIL_VERIFICATION: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetSubLogoAssetURI();
Attrs[SUBJECT] = "e-mail Address Verification";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=sub_email_verification&id=" + LinkId;
Attrs[ACTION_LINK_HTML] =
"/api/v1/actionLink?action=sub_email_verification&id=" + LinkId;
SMTPMailerService()->AddSubVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::SUB_EMAIL_VERIFICATION,
OperatorName),
Attrs, true);
UInfo.waitingForEmailCheck = true;
} break;
case MessagingTemplates::SUB_SIGNUP_VERIFICATION: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetSubLogoAssetURI();
Attrs[SUBJECT] = "Signup e-mail Address Verification";
Attrs[ACTION_LINK] = MicroServiceGetPublicAPIEndPoint() +
"/actionLink?action=signup_verification&id=" + LinkId;
Attrs[ACTION_LINK_HTML] =
"/api/v1/actionLink?action=signup_verification&id=" + LinkId;
SMTPMailerService()->AddSubVars(Attrs);
SMTPMailerService()->SendMessage(
UInfo.email,
MessagingTemplates::TemplateName(MessagingTemplates::SUB_SIGNUP_VERIFICATION,
OperatorName),
Attrs, true);
UInfo.waitingForEmailCheck = true;
} break;
default:
break;
}
return true;
}
return false;
}
bool AuthService::VerifyEmail(SecurityObjects::UserInfo &UInfo) {
SecurityObjects::ActionLink A;
A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL;
A.userId = UInfo.id;
A.id = MicroServiceCreateUUID();
A.created = Utils::Now();
A.expires = A.created + 24 * 60 * 60;
A.userAction = true;
StorageService()->ActionLinksDB().CreateAction(A);
UInfo.waitingForEmailCheck = true;
UInfo.validated = false;
return true;
}
bool AuthService::VerifySubEmail(SecurityObjects::UserInfo &UInfo) {
SecurityObjects::ActionLink A;
A.action = OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL;
A.userId = UInfo.id;
A.id = MicroServiceCreateUUID();
A.created = Utils::Now();
A.expires = A.created + 24 * 60 * 60;
A.userAction = false;
StorageService()->ActionLinksDB().CreateAction(A);
UInfo.waitingForEmailCheck = true;
UInfo.validated = false;
return true;
}
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken,
SecurityObjects::UserInfo &UserInfo, bool &Expired) {
std::lock_guard G(Mutex_);
Expired = false;
std::string TToken{Token}, UserId;
SecurityObjects::WebToken WT;
uint64_t RevocationDate = 0;
if (StorageService()->UserTokenDB().GetToken(TToken, WT, UserId, RevocationDate)) {
if (RevocationDate != 0)
return false;
Expired = (WT.created_ + WT.expires_in_) < Utils::Now();
if (StorageService()->UserDB().GetUserById(UserId, UserInfo)) {
WebToken = WT;
return true;
}
}
return false;
}
bool AuthService::IsValidSubToken(const std::string &Token, SecurityObjects::WebToken &WebToken,
SecurityObjects::UserInfo &UserInfo, bool &Expired) {
std::lock_guard G(Mutex_);
Expired = false;
std::string TToken{Token}, UserId;
SecurityObjects::WebToken WT;
uint64_t RevocationDate = 0;
if (StorageService()->SubTokenDB().GetToken(TToken, WT, UserId, RevocationDate)) {
if (RevocationDate != 0)
return false;
Expired = (WT.created_ + WT.expires_in_) < Utils::Now();
if (StorageService()->SubDB().GetUserById(UserId, UserInfo)) {
WebToken = WT;
return true;
}
}
return false;
}
bool AuthService::IsValidApiKey(const std::string &ApiKey, SecurityObjects::WebToken &WebToken,
SecurityObjects::UserInfo &UserInfo, bool &Expired,
std::uint64_t &expiresOn, bool &Suspended) {
std::lock_guard G(Mutex_);
Suspended = false;
std::string UserId;
SecurityObjects::WebToken WT;
SecurityObjects::ApiKeyEntry ApiKeyEntry;
if (StorageService()->ApiKeyDB().GetRecord("apiKey", ApiKey, ApiKeyEntry)) {
expiresOn = ApiKeyEntry.expiresOn;
Expired = ApiKeyEntry.expiresOn < Utils::Now();
if (Expired)
return false;
if (StorageService()->UserDB().GetUserById(ApiKeyEntry.userUuid, UserInfo)) {
if (UserInfo.suspended) {
Suspended = true;
return false;
}
WebToken = WT;
ApiKeyEntry.lastUse = Utils::Now();
StorageService()->ApiKeyDB().UpdateRecord("id", ApiKeyEntry.id, ApiKeyEntry);
return true;
}
}
return false;
}
} // namespace OpenWifi