stephb9959
2022-07-25 23:25:03 -07:00
parent e6c196cd67
commit 13bec235a1
45 changed files with 678 additions and 71 deletions

View File

@@ -124,7 +124,7 @@ add_executable( owsec
src/storage/orm_actionLinks.cpp src/storage/orm_actionLinks.h
src/storage/orm_avatar.cpp src/storage/orm_avatar.h
src/SpecialUserHelpers.h
src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h src/TotpCache.h src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h)
src/RESTAPI/RESTAPI_db_helpers.h src/storage/orm_logins.cpp src/storage/orm_logins.h src/RESTAPI/RESTAPI_totp_handler.cpp src/RESTAPI/RESTAPI_totp_handler.h src/TotpCache.h src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h src/MessagingTemplates.cpp src/MessagingTemplates.h)
if(NOT SMALL_BUILD)
target_link_libraries(owsec PUBLIC

37
OPERATOR.md Normal file
View File

@@ -0,0 +1,37 @@
# Operator Support
In order to support multiple tenants and operators, you must prepare the security service to serve
customized e-mails and messages.
## Structure for `templates`
Any file in the root of the directory will be used as defaults. The following files must be present:
- email_invitation.html/txt : This email message will be sent to a newly added user.
- email_verification.html/txt : This email is sent when an email verification is required.
- password_reset.html/txt : This is sent when a pasword reset is requested.
- verification_code.html/txt : This is used during MFA when email based.
- signup_verification.html/txt : This email is send to a new subscriber who signed up for service.
- sub_email_verification.html/txt : This is sent to a subscriber requiring an email verification.
- sub_verification_code.html/txt : This is used during MFA when email based for a subscriber.
- logo.jpg : The default logo to use in any of these emails.
## Structure for `wwwassets`
Any file in the root of the directory will be used as defaults. The following files must be present:
- email_verification_error.html : Used when email verification has failed.
- email_verification_success.html : Used when emil verification has succeeded.
- invitation_error.html :
- invitation_success.html :
- password_policy.html :
- password_reset.html :
- password_reset_success.html :
- password_reset_error.html :
- signup_verification.html :
- signup_verification_error.html :
- signup_verification_success.html :
- favicon.ico : icon for the application
- 404_error.html : your customized 404 page
- the_logo : the logo to use.
## For tenants
When creating a tenant/operator, you must create a subdirectory inside each `wwwassets` and `templates` and replicate
all the files that appear at the root level. You need to use the short Operator name (also known as RegistrantId in the API). This means
no spaces, all lowercase characters and numbers. No special characters: 0-9 and a-z.

2
build
View File

@@ -1 +1 @@
4
6

View File

@@ -5,6 +5,7 @@
#include "ActionLinkManager.h"
#include "StorageService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
#include "MessagingTemplates.h"
namespace OpenWifi {
@@ -61,7 +62,7 @@ namespace OpenWifi {
switch(i.action) {
case OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD: {
if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::FORGOT_PASSWORD)) {
if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::FORGOT_PASSWORD)) {
Logger().information(fmt::format("Send password reset link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
@@ -69,15 +70,24 @@ namespace OpenWifi {
break;
case OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL: {
if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::EMAIL_VERIFICATION)) {
if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_VERIFICATION)) {
Logger().information(fmt::format("Send email verification link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
}
break;
case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: {
if(AuthService::SendEmailToUser(i.id, UInfo.email, MessagingTemplates::EMAIL_INVITATION)) {
Logger().information(fmt::format("Send new subscriber email invitation link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
}
break;
case OpenWifi::SecurityObjects::LinkActions::SUB_FORGOT_PASSWORD: {
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::FORGOT_PASSWORD)) {
auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
if(AuthService::SendEmailToSubUser(i.id, UInfo.email,MessagingTemplates::SUB_FORGOT_PASSWORD, Signup.count()==1 ? "" : Signup[0])) {
Logger().information(fmt::format("Send subscriber password reset link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
@@ -85,7 +95,8 @@ namespace OpenWifi {
break;
case OpenWifi::SecurityObjects::LinkActions::SUB_VERIFY_EMAIL: {
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::EMAIL_VERIFICATION)) {
auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SUB_EMAIL_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) {
Logger().information(fmt::format("Send subscriber email verification link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
@@ -93,21 +104,14 @@ namespace OpenWifi {
break;
case OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP: {
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::SIGNUP_VERIFICATION)) {
auto Signup = Poco::StringTokenizer(UInfo.signingUp,":");
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, MessagingTemplates::SIGNUP_VERIFICATION, Signup.count()==1 ? "" : Signup[0])) {
Logger().information(fmt::format("Send new subscriber email verification link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
}
break;
case OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION: {
if(AuthService::SendEmailToSubUser(i.id, UInfo.email, AuthService::EMAIL_INVITATION)) {
Logger().information(fmt::format("Send new subscriber email invitation link to {}",UInfo.email));
}
StorageService()->ActionLinksDB().SentAction(i.id);
}
break;
default: {
StorageService()->ActionLinksDB().SentAction(i.id);
}

View File

@@ -20,14 +20,10 @@
#include "SMTPMailerService.h"
#include "MFAServer.h"
#include "MessagingTemplates.h"
namespace OpenWifi {
inline const static std::vector<std::string> EmailTemplateNames = { "password_reset" ,
"email_verification",
"signuo_verification",
"email_invitation" };
AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) {
switch (C) {
case 1: return USERNAME;
@@ -38,7 +34,6 @@ namespace OpenWifi {
}
}
int AuthService::AccessTypeToInt(ACCESS_TYPE T) {
switch (T) {
case USERNAME: return 1;
@@ -519,7 +514,6 @@ namespace OpenWifi {
return SUCCESS;
}
return INVALID_CREDENTIALS;
}
@@ -569,40 +563,62 @@ namespace OpenWifi {
return INVALID_CREDENTIALS;
}
bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) {
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 FORGOT_PASSWORD: {
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 ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs);
Attrs[ACTION_LINK_HTML] = "/api/v1/actionLink?action=password_reset&id=" + LinkId ;
SMTPMailerService()->SendMessage(UInfo.email, MessagingTemplates::TemplateName(MessagingTemplates::FORGOT_PASSWORD), Attrs);
}
break;
case EMAIL_VERIFICATION: {
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 ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs);
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 EMAIL_INVITATION: {
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 ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_INVITATION], Attrs);
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;
@@ -615,40 +631,43 @@ namespace OpenWifi {
return false;
}
bool AuthService::SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) {
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 FORGOT_PASSWORD: {
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=password_reset&id=" + LinkId ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[FORGOT_PASSWORD], Attrs);
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 EMAIL_VERIFICATION: {
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=email_verification&id=" + LinkId ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[EMAIL_VERIFICATION], Attrs);
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 SIGNUP_VERIFICATION: {
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 ;
SMTPMailerService()->SendMessage(UInfo.email, EmailTemplateNames[SIGNUP_VERIFICATION], Attrs);
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;

View File

@@ -22,6 +22,7 @@
#include "framework/MicroService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
#include "MessagingTemplates.h"
namespace OpenWifi{
@@ -36,13 +37,6 @@ namespace OpenWifi{
CUSTOM
};
enum EMAIL_REASON {
FORGOT_PASSWORD = 0,
EMAIL_VERIFICATION,
SIGNUP_VERIFICATION,
EMAIL_INVITATION
};
static ACCESS_TYPE IntToAccessType(int C);
static int AccessTypeToInt(ACCESS_TYPE T);
@@ -91,10 +85,12 @@ namespace OpenWifi{
[[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo);
[[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo);
[[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason);
[[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason);
[[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason);
[[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, MessagingTemplates::EMAIL_REASON Reason, const std::string &OperatorName);
[[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo);
[[nodiscard]] bool SendEmailChallengeCode(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &code);
bool DeleteUserFromCache(const std::string &UserName);
bool DeleteSubUserFromCache(const std::string &UserName);
void RevokeToken(std::string & Token);

View File

@@ -44,12 +44,7 @@ namespace OpenWifi {
std::string Message = "This is your login code: " + Challenge + " Please enter this in your login screen.";
return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number, Message);
} else if(Method==MFAMETHODS::EMAIL && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) {
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, "verification_code.txt", Attrs);
return AuthService()->SendEmailChallengeCode(UInfo,Challenge);
} else if(Method==MFAMETHODS::AUTHENTICATOR && !UInfo.userinfo.userTypeProprietaryInfo.authenticatorSecret.empty()) {
return true;
}

View File

@@ -0,0 +1,8 @@
//
// Created by stephane bourque on 2022-07-25.
//
#include "MessagingTemplates.h"
namespace OpenWifi {
} // OpenWifi

75
src/MessagingTemplates.h Normal file
View File

@@ -0,0 +1,75 @@
//
// Created by stephane bourque on 2022-07-25.
//
#pragma once
#include <string>
#include <vector>
namespace OpenWifi {
class MessagingTemplates {
public:
static MessagingTemplates & instance() {
static auto instance = new MessagingTemplates;
return *instance;
}
enum EMAIL_REASON {
FORGOT_PASSWORD = 0,
EMAIL_VERIFICATION,
SIGNUP_VERIFICATION,
EMAIL_INVITATION,
VERIFICATION_CODE,
SUB_FORGOT_PASSWORD,
SUB_EMAIL_VERIFICATION,
SUB_VERIFICATION_CODE
};
static std::string AddOperator(const std::string & filename, const std::string &OperatorName) {
if(OperatorName.empty())
return "/" + filename;
return "/" + OperatorName + "/" + filename;
}
static std::string TemplateName( EMAIL_REASON r , const std::string &OperatorName="") {
switch (r) {
case FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[FORGOT_PASSWORD],OperatorName);
case EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[EMAIL_VERIFICATION],OperatorName);
case SIGNUP_VERIFICATION: return AddOperator(EmailTemplateNames[SIGNUP_VERIFICATION],OperatorName);
case EMAIL_INVITATION: return AddOperator(EmailTemplateNames[EMAIL_INVITATION],OperatorName);
case VERIFICATION_CODE: return AddOperator(EmailTemplateNames[VERIFICATION_CODE],OperatorName);
case SUB_FORGOT_PASSWORD: return AddOperator(EmailTemplateNames[SUB_FORGOT_PASSWORD],OperatorName);
case SUB_EMAIL_VERIFICATION: return AddOperator(EmailTemplateNames[SUB_EMAIL_VERIFICATION],OperatorName);
case SUB_VERIFICATION_CODE: return AddOperator(EmailTemplateNames[SUB_VERIFICATION_CODE],OperatorName);
default:
return "";
}
}
static std::string Logo(const std::string &OperatorName = "" ) {
return AddOperator("logo.jpg", OperatorName);
}
static std::string SubLogo(const std::string &OperatorName = "" ) {
return AddOperator("sub_logo.jpg", OperatorName);
}
private:
inline const static std::vector<std::string> EmailTemplateNames = {
"password_reset",
"email_verification",
"signup_verification",
"email_invitation",
"verification_code",
"sub_password_reset",
"sub_email_verification",
"sub_verification_code"
};
};
inline MessagingTemplates & MessagingTemplates() { return MessagingTemplates::instance(); }
} // OpenWifi

View File

@@ -23,8 +23,12 @@ namespace OpenWifi {
if(Action=="password_reset")
return RequestResetPassword(Link);
else if(Action=="sub_password_reset")
return RequestSubResetPassword(Link);
else if(Action=="email_verification")
return DoEmailVerification(Link);
else if(Action=="sub_email_verification")
return DoSubEmailVerification(Link);
else if(Action=="signup_verification")
return DoNewSubVerification(Link);
else
@@ -36,6 +40,8 @@ namespace OpenWifi {
if(Action=="password_reset")
return CompleteResetPassword();
else if(Action=="sub_password_reset")
return CompleteResetPassword();
else if(Action=="signup_completion")
return CompleteSubVerification();
else if(Action=="email_invitation")
@@ -201,10 +207,11 @@ namespace OpenWifi {
// Send the update to the provisioning service
Poco::JSON::Object Body;
Body.set("signupUUID", UInfo.signingUp);
auto RawSignup = Poco::StringTokenizer(UInfo.signingUp,":");
Body.set("signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]);
OpenAPIRequestPut ProvRequest(uSERVICE_PROVISIONING,"/api/v1/signup",
{
{"signupUUID", UInfo.signingUp} ,
{"signupUUID", RawSignup.count()==1 ? UInfo.signingUp : RawSignup[1]} ,
{"operation", "emailVerified"}
},
Body,30000);
@@ -269,4 +276,12 @@ namespace OpenWifi {
/// TODO:
}
void RESTAPI_action_links::RequestSubResetPassword(SecurityObjects::ActionLink &Link) {
}
void RESTAPI_action_links::DoSubEmailVerification(SecurityObjects::ActionLink &Link) {
}
}

View File

@@ -22,9 +22,11 @@ namespace OpenWifi {
true, RateLimit{.Interval=1000,.MaxCalls=10}) {}
static auto PathName() { return std::list<std::string>{"/api/v1/actionLink"}; };
void RequestResetPassword(SecurityObjects::ActionLink &Link);
void RequestSubResetPassword(SecurityObjects::ActionLink &Link);
void CompleteResetPassword();
void CompleteSubVerification();
void DoEmailVerification(SecurityObjects::ActionLink &Link);
void DoSubEmailVerification(SecurityObjects::ActionLink &Link);
void DoReturnA404();
void DoNewSubVerification(SecurityObjects::ActionLink &Link);
void CompleteEmailInvitation();

View File

@@ -13,8 +13,9 @@ namespace OpenWifi {
auto UserName = GetParameter("email");
auto signupUUID = GetParameter("signupUUID");
auto owner = GetParameter("owner");
if(UserName.empty() || signupUUID.empty() || owner.empty()) {
Logger().error("Signup requires: email, signupUUID, and owner.");
auto operatorName = GetParameter("operatorName");
if(UserName.empty() || signupUUID.empty() || owner.empty() || operatorName.empty()) {
Logger().error("Signup requires: email, signupUUID, operatorName, and owner.");
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -37,7 +38,7 @@ namespace OpenWifi {
}
SecurityObjects::UserInfo NewSub;
NewSub.signingUp = signupUUID;
NewSub.signingUp = operatorName + ":" + signupUUID;
NewSub.waitingForEmailCheck = true;
NewSub.name = UserName;
NewSub.modified = OpenWifi::Now();

View File

@@ -27,7 +27,8 @@ namespace OpenWifi {
LOGO,
TEXT,
CHALLENGE_CODE,
SENDER
SENDER,
ACTION_LINK_HTML
};
static const std::map<MESSAGE_ATTRIBUTES,const std::string>
@@ -44,7 +45,8 @@ namespace OpenWifi {
{ LOGO, "LOGO"},
{ TEXT, "TEXT"},
{ CHALLENGE_CODE, "CHALLENGE_CODE"},
{ SENDER, "SENDER"}
{ SENDER, "SENDER"},
{ ACTION_LINK_HTML, "ACTION_LINK_HTML"},
};
inline const std::string & MessageAttributeToVar(MESSAGE_ATTRIBUTES Attr) {

View File

@@ -2,7 +2,11 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<title>eMail Invitation</title>
</head>
<body>

View File

@@ -4,6 +4,14 @@
<meta charset="UTF-8">
<title>Password Reset</title>
<form action="${ACTION_LINK_HTML}">
<label for="fname">First name: </label>
<input type="text" id="fname" name="fname"><br><br>
<label for="lname">Last name: </label>
<input type="text" id="lname" name="lname"><br><br>
<input type="submit" value="Submit">
</form>
</head>
<body>

441
templates/sample.html Normal file
View File

@@ -0,0 +1,441 @@
<!doctype html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><meta name="viewport" content="width=device-width">
<title>Project Groups now available to join - Telecom Infra Project</title>
<style>
/* -------------------------------------
GLOBAL RESETS
------------------------------------- */
/*All the styling goes here*/
img {
border: none;
-ms-interpolation-mode: bicubic;
max-width: 100%;
}
body {
color: #414141;
background-color: #f6f6f6;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
font-size: 14px;
line-height: 1.4;
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table {
border-collapse: separate;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
width: 100%;
text-align: center;
}
table td {
font-family: sans-serif;
font-size: 14px;
color: #414141;
vertical-align: top;
}
/* -------------------------------------
BODY & CONTAINER
------------------------------------- */
.body {
background-color: white;
width: 100%;
}
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink down on a phone or something
*/
.container {
display: block;
margin: 0 auto !important;
/* makes it centered */
max-width: 580px;
padding: 10px;
width: 580px;
}
/* This should also be a block element, so that it will fill 100% of the container */
.content {
box-sizing: border-box;
display: block;
margin: 0 auto;
max-width: 580px;
padding: 10px;
}
/* -------------------------------------
HEADER, FOOTER, MAIN
------------------------------------- */
.main {
background: #ffffff;
border-radius: 0px;
width: 600px;
max-width: 100%;
border: 1px solid #d4d4d4;
padding-left: 45px;
padding-right: 45px;
}
.wrapper {
box-sizing: border-box;
padding: 40px 20px;
}
.content-block {
padding-bottom: 10px;
padding-top: 10px;
}
.footer {
clear: both;
margin-top: 10px;
text-align: center;
width: 100%;
}
.footer td,
.footer p,
.footer span,
.footer a {
color: #999999;
font-size: 12px;
text-align: center;
}
/* -------------------------------------
TYPOGRAPHY
------------------------------------- */
h1,
h2,
h3,
h4 {
color: #414141;
font-family: sans-serif;
font-weight: 400;
line-height: 1.4;
margin: 0;
margin-bottom: 30px;
}
h2 {
font-weight: 600;
}
h1 {
font-size: 35px;
font-weight: 300;
text-align: center;
text-transform: capitalize;
}
p,
ul,
ol {
font-family: sans-serif;
font-size: 14px;
font-weight: normal;
margin: 0;
color: #414141;
margin-bottom: 15px;
}
p li,
ul li,
ol li {
list-style-position: inside;
margin-left: 5px;
color: #414141;
}
a {
color: #29818c !important;
text-decoration: none;
border-bottom: 1px solid #d2d2d2;
}
.footer a {
color: #999999 !important;
}
/* -------------------------------------
BUTTONS
------------------------------------- */
.btn {
box-sizing: border-box;
width: 100%;
}
tbody {
text-align: left;
}
.btn > tbody > tr > td {
padding-bottom: 15px;
}
.btn table {
width: auto;
}
.btn table td {
background-color: #ffffff;
border-radius: 20px;
text-align: center;
}
.btn a {
background-color: #ffffff;
border: solid 1px #489e94;
border-radius: 5px;
box-sizing: border-box;
color: #29818c;
cursor: pointer;
display: inline-block;
font-size: 14px;
font-weight: bold;
margin: 0;
padding: 12px 120px;
text-decoration: none;
font-weight:600;
text-transform: uppercase;
}
.btn-primary table td {
/* background-color: #29818c;*/
}
.btn-primary a {
transition: background-color 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
box-shadow 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,
border 250ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
margin: auto;
background-color: #29818c;
border-color: #29818c;
color: #ffffff !important;
}
/* -------------------------------------
OTHER STYLES THAT MIGHT BE USEFUL
------------------------------------- */
.last {
margin-bottom: 0;
}
.first {
margin-top: 0;
}
.align-center {
text-align: center;
}
.align-right {
text-align: right;
}
.align-left {
text-align: left;
}
.clear {
clear: both;
}
.mt0 {
margin-top: 0;
}
.mb0 {
margin-bottom: 0;
}
.preheader {
color: transparent;
display: none;
height: 0;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
mso-hide: all;
visibility: hidden;
width: 0;
}
.powered-by a {
text-decoration: none;
}
hr {
border: 0;
border-bottom: 1px solid #d4d4d4;
margin: 20px 0;
}
.grayFont {
color: #999999;
}
.bold {
font-weight: 600;
}
/* -------------------------------------
RESPONSIVE AND MOBILE FRIENDLY STYLES
------------------------------------- */
@media only screen and (max-width: 620px) {
table[class="body"] h1 {
font-size: 28px !important;
margin-bottom: 10px !important;
}
table[class="body"] p,
table[class="body"] ul,
table[class="body"] ol,
table[class="body"] td,
table[class="body"] span,
table[class="body"] a {
font-size: 16px !important;
}
table[class="body"] .wrapper,
table[class="body"] .article {
padding: 10px !important;
}
table[class="body"] .content {
padding: 0 !important;
}
table[class="body"] .container {
padding: 0 !important;
width: 100% !important;
}
table[class="body"] .main {
border-left-width: 0 !important;
border-radius: 0 !important;
border-right-width: 0 !important;
}
table[class="body"] .btn table {
width: 100% !important;
}
table[class="body"] .btn a {
width: 100% !important;
}
table[class="body"] .img-responsive {
height: auto !important;
max-width: 100% !important;
width: auto !important;
}
}
/* -------------------------------------
PRESERVE THESE STYLES IN THE HEAD
------------------------------------- */
@media all {
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.apple-link a {
color: inherit !important;
font-family: inherit !important;
font-size: inherit !important;
font-weight: inherit !important;
line-height: inherit !important;
text-decoration: none !important;
}
.btn-primary table td:hover {
/* background-color: rgb(50, 110, 103) !important; */
}
.btn-primary a:hover {
background-color: rgb(50, 110, 103) !important;
border-color: rgb(50, 110, 103) !important;
}
}
</style>
</head>
<body class="">
<span class="preheader"></span>
<table d="" role="presentation" border="0" cellpadding="0" cellspacing="0" class="body">
<tr>
<td>&nbsp;</td>
<td class="container">
<div class="content">
<!-- START CENTERED WHITE CONTAINER -->
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100%">
<tbody>
<tr>
<td valign="top" style="padding:0px">
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" style="min-width:100%">
<tbody>
<tr>
<td valign="top" style="padding-right:0px;padding-left:0px;padding-top:0;padding-bottom:0;text-align:center">
<img align="center" alt="Telecom Infra Project" src="https://gallery.mailchimp.com/d068b19c9ce4cd81f132f1844/images/f4ac7418-c41c-4aee-890b-efe01f3302cb.png"
width="600" style="max-width:100%;padding-bottom:0;display:inline!important;vertical-align:bottom">
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<table role="presentation" class="main">
<!-- START MAIN CONTENT AREA -->
<tr>
<td class="wrapper">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>
<h2>Project Groups now available to join - Telecom Infra Project</h2>
<p>Dear Jaspreet Sachdev,</p>
<p>
Facebook is now a member of the following TIP Project Group,
which means they are available for you to join:
</p>
<p>
<ul style="text-align: left;margin: auto;display: inline-block;">
<li><a href="https://urldefense.com/" target="_blank">Wi-Fi</a></li>
</ul>
</p>
<p>
If you would like to participate in any of these groups, you may join
by logging in to your TIP Membership Account and navigating to My Project
Groups.
</p>
</td>
</tr>
<tr>
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
<tbody>
<tr>
<td>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<a href="https://urldefense.com/v3/" target="_blank">Account Login</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</tr>
<tr>
<td>
<p>Questions? <a href="https://urldefense.com/v3/" target="_blank">Contact TIP Support</a> or email support@telecominfraproject.com.</p>
</td>
</tr>
<tr>
<td>
<p>Sincerely,</p>
<p>Telecom Infra Project</p>
</td>
</tr>
</table>
</td>
</tr>
<!-- END MAIN CONTENT AREA -->
</table>
=09
<!-- END CENTERED WHITE CONTAINER -->
<div class="footer">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="min-width:100;margin-top:30px;">
<tbody>
<tr>
<td valign="top" style="padding:0px">
<table align="left" width="100%" border="0" cellpadding="0" cellspacing="0" style="min-width:100%">
<tbody>
<tr>
<tr>
<p style="text-align:center">
Copyright =C2=A9 2021 Telecom Infra Project, All rights reserved.
</p>
<a href="https://urldefense.com/v3/" target="_blank">www.telecominfraproject.com</a>
</tr>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</td>
<td>&nbsp;</td>
</tr>
</table>
<img src="https://mandrillapp.com/track/open.php?u=31039844&amp;id=6424f96bfb88484685c7229756fee3fb" height="1" width="1"></body>
</html>

View File

View File

View File

View File

View File

View File

View File

View File

@@ -96,7 +96,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -103,7 +103,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -103,7 +103,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

BIN
wwwassets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@@ -101,7 +101,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -122,7 +122,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -101,7 +101,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -70,7 +70,7 @@
<body>
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -122,7 +122,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -101,7 +101,7 @@
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

@@ -69,7 +69,7 @@
<body>
<div class="logo-grid">
<div></div>
<div><img src="/wwwassets/the_logo.png" alt="OpenWifi"></div>
<div><img src="/wwwassets/sub_logo.png" alt="OpenWifi"></div>
<div></div>
</div>

View File

View File

View File

BIN
wwwassets/sub_favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

BIN
wwwassets/sub_logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

View File

View File