// // 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 #include "framework/MicroService.h" #include "framework/KafkaTopics.h" #include "Poco/Net/OAuth20Credentials.h" #include "Poco/JWT/Token.h" #include "Poco/JWT/Signer.h" #include "Poco/StringTokenizer.h" #include "StorageService.h" #include "AuthService.h" #include "SMTPMailerService.h" #include "MFAServer.h" #include "MessagingTemplates.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... } static const std::string DefaultPassword_8_u_l_n_1{"^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[\\{\\}\\(\\)~_\\+\\|\\\\\\[\\]\\;\\:\\<\\>\\.\\,\\/\\?\\\"\\'\\`\\=#?!@$%^&*-]).{8,}$"}; int AuthService::Start() { poco_information(Logger(),"Starting..."); TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); RefreshTokenLifeSpan_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.refresh_token.lifespan", 90 * 24 * 60 * 600); HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5); AccessPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); PasswordPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression",DefaultPassword_8_u_l_n_1); SubPasswordValidation_ = SubPasswordValidationStr_ = MicroService::instance().ConfigGetString("subscriber.validation.expression",DefaultPassword_8_u_l_n_1); SubAccessPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.access", "/wwwassets/access_policy.html"); SubPasswordPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.password", "/wwwassets/password_policy.html"); 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 = OpenWifi::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 = OpenWifi::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; } 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, CallToken)); return false; } 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, CallToken)); return false; } auto now=OpenWifi::Now(); Expired = (WT.created_ + WT.expires_in_) < now; if(StorageService()->UserDB().GetUserById(UserId,UInfo.userinfo)) { UInfo.webtoken = WT; SessionToken = CallToken; poco_debug(Logger(), fmt::format("TokenValidation success for TID={} Token={}", TID, CallToken)); return true; } } } catch(const Poco::Exception &E) { Logger().log(E); } poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID, 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, 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, CallToken)); return false; } auto now=OpenWifi::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, CallToken)); return true; } } } catch(const Poco::Exception &E) { Logger().log(E); } poco_debug(Logger(), fmt::format("TokenValidation failed for TID={} Token={}", TID, 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", MicroService::instance().ID()); Obj.set("token", token); std::stringstream ResultText; Poco::JSON::Stringifier::stringify(Obj, ResultText); KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(), 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("{}",OpenWifi::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 = MicroService::instance().Sign(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.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 = OpenWifi::Now(); UInfo.userinfo.changePassword = false; UInfo.userinfo.modified = OpenWifi::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=OpenWifi::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.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 = OpenWifi::Now(); UInfo.userinfo.changePassword = false; UInfo.userinfo.modified = OpenWifi::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=OpenWifi::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,":"); 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; return SMTPMailerService()->SendMessage(UInfo.userinfo.email, MessagingTemplates::TemplateName(MessagingTemplates::VERIFICATION_CODE), Attrs); } else { MessageAttributes Attrs; Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; Attrs[LOGO] = AuthService::GetLogoAssetURI(); Attrs[SUBJECT] = "Login validation code"; Attrs[CHALLENGE_CODE] = Challenge; return SMTPMailerService()->SendMessage(UInfo.userinfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_VERIFICATION_CODE,OperatorParts[0]), Attrs); } } 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] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=password_reset&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::FORGOT_PASSWORD), Attrs); } break; case MessagingTemplates::EMAIL_VERIFICATION: { MessageAttributes Attrs; Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_VERIFICATION), Attrs); 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] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_invitation&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=email_invitation&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::EMAIL_INVITATION), Attrs); 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] = GetLogoAssetURI(); Attrs[SUBJECT] = "Password reset link"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_password_reset&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_password_reset&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_FORGOT_PASSWORD, OperatorName), Attrs); } break; case MessagingTemplates::SUB_EMAIL_VERIFICATION: { MessageAttributes Attrs; Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=sub_email_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=sub_email_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SUB_EMAIL_VERIFICATION, OperatorName), Attrs); UInfo.waitingForEmailCheck = true; } break; case MessagingTemplates::SIGNUP_VERIFICATION: { MessageAttributes Attrs; Attrs[RECIPIENT_EMAIL] = UInfo.email; Attrs[LOGO] = GetLogoAssetURI(); Attrs[SUBJECT] = "Signup e-mail Address Verification"; Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=signup_verification&id=" + LinkId ; Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=signup_verification&id=" + LinkId ; SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::SIGNUP_VERIFICATION, OperatorName), Attrs); 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 = MicroService::CreateUUID(); A.created = OpenWifi::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 = MicroService::CreateUUID(); A.created = OpenWifi::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_) < OpenWifi::Now(); if(StorageService()->UserDB().GetUserById(UserId,UserInfo)) { WebToken = WT; return true; } return false; } // return IsValidSubToken(Token, WebToken, UserInfo, Expired); 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_) < OpenWifi::Now(); if(StorageService()->SubDB().GetUserById(UserId,UserInfo)) { WebToken = WT; return true; } return false; } return false; } } // end of namespace