Compare commits

...

24 Commits

Author SHA1 Message Date
TIP Automation User
4b1fbf055f Chg: update image tag in helm values to v2.7.0-RC1 2022-09-16 19:54:50 +00:00
Dmitry Dunaev
8b5c9dd5e9 Merge pull request #70 from Telecominfraproject/feature/wifi-10069--add-wait-postgres-initcontainer
[WIFI-10069] Add: helm - wait-postgres init container
2022-09-02 14:44:45 +03:00
Dmitry Dunaev
02a315ab0d [WIFI-10069] Add: helm - wait-postgres init container
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-09-02 14:44:27 +03:00
Stephane Bourque
1e4d9ea4e8 Merge pull request #69 from Telecominfraproject/WIFI-10245
https://telecominfraproject.atlassian.net/browse/WIFI-10245
2022-08-22 08:52:22 -07:00
stephb9959
0b1d7e39eb https://telecominfraproject.atlassian.net/browse/WIFI-10245
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-08-22 08:51:43 -07:00
Dmitry Dunaev
af190e9967 Merge pull request #67 from Telecominfraproject/fix/wifi-10413--cve-fix
[WIFI-10413] Fix: vulnerable base Docker image version
2022-08-15 13:31:01 +03:00
Dmitry Dunaev
80d3dfb89f [WIFI-10413] Fix: vulnerable base Docker image version
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-08-15 11:15:49 +03:00
Stephane Bourque
62c6b119c9 Merge pull request #66 from Telecominfraproject/WIFI-10245
https://telecominfraproject.atlassian.net/browse/WIFI-10245
2022-08-10 16:32:18 -07:00
stephb9959
4ea8aa9958 https://telecominfraproject.atlassian.net/browse/WIFI-10245
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-08-10 16:31:50 -07:00
Stephane Bourque
6a30353b3a Merge pull request #65 from Telecominfraproject/feature/wifi-10388--versioning
[WIFI-10388] Chg: use Docker build arg to define dependency version
2022-08-08 12:12:25 -07:00
Dmitry Dunaev
b355b41d4f [WIFI-10388] Chg: use Docker build arg to define dependency version
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-08-08 17:38:58 +03:00
Stephane Bourque
19b2afb469 Merge pull request #64 from Telecominfraproject/WIFI-10388
https://telecominfraproject.atlassian.net/browse/WIFI-10388
2022-08-07 22:27:31 -07:00
stephb9959
7d65da3abc https://telecominfraproject.atlassian.net/browse/WIFI-10388
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-08-07 22:27:08 -07:00
Stephane Bourque
c25059e2aa Merge pull request #62 from Telecominfraproject/WIFI-10388
https://telecominfraproject.atlassian.net/browse/WIFI-10388
2022-08-01 09:44:42 -07:00
stephb9959
122a73f35e https://telecominfraproject.atlassian.net/browse/WIFI-10388
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-08-01 09:43:48 -07:00
Stephane Bourque
a454b56c7a Merge pull request #61 from Telecominfraproject/WIFI-10388
https://telecominfraproject.atlassian.net/browse/WIFI-10388
2022-08-01 09:16:54 -07:00
stephb9959
ae82160c7f https://telecominfraproject.atlassian.net/browse/WIFI-10388
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-08-01 09:13:18 -07:00
stephb9959
4d73bbd605 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-26 14:51:32 -07:00
stephb9959
13bec235a1 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-25 23:25:03 -07:00
stephb9959
e6c196cd67 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-25 11:13:48 -07:00
stephb9959
6d9a1cac09 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-24 21:42:23 -07:00
stephb9959
55a43ed40d https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-24 20:04:14 -07:00
stephb9959
3a230e4250 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-24 19:36:40 -07:00
stephb9959
0a6ee4ea47 https://telecominfraproject.atlassian.net/browse/WIFI-10345
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
2022-07-24 12:28:40 -07:00
62 changed files with 1119 additions and 165 deletions

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.13)
project(owsec VERSION 2.6.0)
project(owsec VERSION 2.7.0)
set(CMAKE_CXX_STANDARD 17)
@@ -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

View File

@@ -1,4 +1,11 @@
FROM alpine:3.15 AS build-base
ARG ALPINE_VERSION=3.16.2
ARG POCO_VERSION=poco-tip-v1
ARG FMTLIB_VERSION=9.0.0
ARG CPPKAFKA_VERSION=tip-v1
ARG JSON_VALIDATOR_VERSION=2.1.0
ARG AWS_SDK_VERSION=1.9.315
FROM alpine:$ALPINE_VERSION AS build-base
RUN apk add --update --no-cache \
make cmake g++ git \
@@ -9,8 +16,10 @@ RUN apk add --update --no-cache \
FROM build-base AS poco-build
ADD https://api.github.com/repos/stephb9959/poco/git/refs/heads/master version.json
RUN git clone https://github.com/stephb9959/poco /poco
ARG POCO_VERSION
ADD https://api.github.com/repos/AriliaWireless/poco/git/refs/tags/${POCO_VERSION} version.json
RUN git clone https://github.com/AriliaWireless/poco --branch ${POCO_VERSION} /poco
WORKDIR /poco
RUN mkdir cmake-build
@@ -21,8 +30,10 @@ RUN cmake --build . --target install
FROM build-base AS fmtlib-build
ADD https://api.github.com/repos/fmtlib/fmt/git/refs/heads/master version.json
RUN git clone https://github.com/fmtlib/fmt /fmtlib
ARG FMTLIB_VERSION
ADD https://api.github.com/repos/fmtlib/fmt/git/refs/tags/${FMTLIB_VERSION} version.json
RUN git clone https://github.com/fmtlib/fmt --branch ${FMTLIB_VERSION} /fmtlib
WORKDIR /fmtlib
RUN mkdir cmake-build
@@ -33,8 +44,10 @@ RUN make install
FROM build-base AS cppkafka-build
ADD https://api.github.com/repos/stephb9959/cppkafka/git/refs/heads/master version.json
RUN git clone https://github.com/stephb9959/cppkafka /cppkafka
ARG CPPKAFKA_VERSION
ADD https://api.github.com/repos/AriliaWireless/cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
RUN git clone https://github.com/AriliaWireless/cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
WORKDIR /cppkafka
RUN mkdir cmake-build
@@ -45,8 +58,10 @@ RUN cmake --build . --target install
FROM build-base AS json-schema-validator-build
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/heads/master version.json
RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator
ARG JSON_VALIDATOR_VERSION
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json
RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator
WORKDIR /json-schema-validator
RUN mkdir cmake-build
@@ -57,8 +72,10 @@ RUN make install
FROM build-base AS aws-sdk-cpp-build
ADD https://api.github.com/repos/aws/aws-sdk-cpp/git/refs/heads/main version.json
RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp /aws-sdk-cpp
ARG AWS_SDK_VERSION
ADD https://api.github.com/repos/aws/aws-sdk-cpp/git/refs/tags/${AWS_SDK_VERSION} version.json
RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp --branch ${AWS_SDK_VERSION} /aws-sdk-cpp
WORKDIR /aws-sdk-cpp
RUN mkdir cmake-build
@@ -97,7 +114,7 @@ RUN cmake .. \
-DBUILD_SHARED_LIBS=ON
RUN cmake --build . --config Release -j8
FROM alpine:3.15
FROM alpine:$ALPINE_VERSION
ENV OWSEC_USER=owsec \
OWSEC_ROOT=/owsec-data \

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 @@
60
10

2
helm/.gitignore vendored
View File

@@ -1 +1,3 @@
*.swp
Chart.lock
charts/

View File

@@ -1,4 +1,5 @@
{{- $root := . -}}
{{- $storageType := index .Values.configProperties "storage.type" -}}
---
apiVersion: apps/v1
kind: Deployment
@@ -46,6 +47,39 @@ spec:
- -timeout
- 600s
{{- if eq $storageType "postgresql" }}
- name: wait-postgres
image: "{{ .Values.images.owsec.repository }}:{{ .Values.images.owsec.tag }}"
imagePullPolicy: {{ .Values.images.owsec.pullPolicy }}
command:
- /wait-for-postgres.sh
- {{ index .Values.configProperties "storage.type.postgresql.host" }}
- echo
- "PostgreSQL is ready"
env:
- name: KUBERNETES_DEPLOYED
value: "{{ now }}"
{{- range $key, $value := .Values.public_env_variables }}
- name: {{ $key }}
value: {{ $value | quote }}
{{- end }}
{{- range $key, $value := .Values.secret_env_variables }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ include "owsec.fullname" $root }}-env
key: {{ $key }}
{{- end }}
volumeMounts:
{{- range .Values.volumes.owsec }}
- name: {{ .name }}
mountPath: {{ .mountPath }}
{{- if .subPath }}
subPath: {{ .subPath }}
{{- end }}
{{- end }}
{{- end }}
containers:
- name: owsec

View File

@@ -9,7 +9,7 @@ fullnameOverride: ""
images:
owsec:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec
tag: main
tag: v2.7.0-RC1
pullPolicy: Always
# regcred:
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io

View File

@@ -5,6 +5,7 @@
#include "ActionLinkManager.h"
#include "StorageService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
#include "MessagingTemplates.h"
namespace OpenWifi {
@@ -53,11 +54,15 @@ namespace OpenWifi {
i.action==OpenWifi::SecurityObjects::LinkActions::SUB_SIGNUP ) && !StorageService()->SubDB().GetUserById(i.userId,UInfo)) {
StorageService()->ActionLinksDB().CancelAction(i.id);
continue;
} else if((i.action==OpenWifi::SecurityObjects::LinkActions::EMAIL_INVITATION) &&
(OpenWifi::Now()-i.created)>(24*60*60)) {
StorageService()->ActionLinksDB().CancelAction(i.id);
continue;
}
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);
@@ -65,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);
@@ -81,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);
@@ -89,7 +104,8 @@ 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);

View File

@@ -12,14 +12,6 @@ namespace OpenWifi {
class ActionLinkManager : public SubSystemServer, Poco::Runnable {
public:
/* enum Actions {
FORGOT_PASSWORD,
VERIFY_EMAIL,
SUB_FORGOT_PASSWORD,
SUB_VERIFY_EMAIL,
SUB_SIGNUP
};
*/
static ActionLinkManager * instance() {
static auto instance_ = new ActionLinkManager;
return instance_;

View File

@@ -20,6 +20,7 @@
#include "SMTPMailerService.h"
#include "MFAServer.h"
#include "MessagingTemplates.h"
namespace OpenWifi {
@@ -33,7 +34,6 @@ namespace OpenWifi {
}
}
int AuthService::AccessTypeToInt(ACCESS_TYPE T) {
switch (T) {
case USERNAME: return 1;
@@ -51,13 +51,13 @@ namespace OpenWifi {
RefreshTokenLifeSpan_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.refresh_token.lifespan", 90 * 24 * 60 * 600);
HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5);
AccessPolicy_ = MicroService::instance().ConfigPath("openwifi.document.policy.access", "/wwwassets/access_policy.html");
PasswordPolicy_ = MicroService::instance().ConfigPath("openwifi.document.policy.password", "/wwwassets/password_policy.html");
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().ConfigPath("subscriber.policy.access", "/wwwassets/access_policy.html");
SubPasswordPolicy_ = MicroService::instance().ConfigPath("subscriber.policy.password", "/wwwassets/password_policy.html");
SubAccessPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.access", "/wwwassets/access_policy.html");
SubPasswordPolicy_ = MicroService::instance().ConfigGetString("subscriber.policy.password", "/wwwassets/password_policy.html");
return 0;
}
@@ -514,7 +514,6 @@ namespace OpenWifi {
return SUCCESS;
}
return INVALID_CREDENTIALS;
}
@@ -564,33 +563,66 @@ 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, "password_reset.txt", 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, "email_verification.txt", 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 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;
}
@@ -599,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, "password_reset.txt", 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, "email_verification.txt", 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, "signup_verification.txt", 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,12 +37,6 @@ namespace OpenWifi{
CUSTOM
};
enum EMAIL_REASON {
FORGOT_PASSWORD,
EMAIL_VERIFICATION,
SIGNUP_VERIFICATION
};
static ACCESS_TYPE IntToAccessType(int C);
static int AccessTypeToInt(ACCESS_TYPE T);
@@ -90,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,8 +40,12 @@ 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")
return CompleteEmailInvitation();
else
return DoReturnA404();
}
@@ -199,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);
@@ -238,7 +247,8 @@ namespace OpenWifi {
return SendHTMLFileBack(FormFile, FormVars);
}
Logger_.information(fmt::format("EMAIL-VERIFICATION(%s): For ID={}", Request->clientAddress().toString(), UInfo.email));
Logger_.information(fmt::format("EMAIL-VERIFICATION(%s): For ID={}", Request->clientAddress().toString(),
UInfo.email));
UInfo.waitingForEmailCheck = false;
UInfo.validated = true;
UInfo.lastEmailCheck = OpenWifi::Now();
@@ -262,4 +272,16 @@ namespace OpenWifi {
SendHTMLFileBack(FormFile, FormVars);
}
void RESTAPI_action_links::CompleteEmailInvitation() {
/// TODO:
}
void RESTAPI_action_links::RequestSubResetPassword([[maybe_unused]] SecurityObjects::ActionLink &Link) {
}
void RESTAPI_action_links::DoSubEmailVerification([[maybe_unused]] SecurityObjects::ActionLink &Link) {
}
}

View File

@@ -22,11 +22,14 @@ 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();
void DoGet() final;
void DoPost() final;

View File

@@ -29,7 +29,8 @@
namespace OpenWifi {
Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t TransactionId) {
Poco::Logger & L, RESTAPI_GenericServer & S,
uint64_t TransactionId) {
return RESTAPI_Router<
RESTAPI_oauth2_handler,
RESTAPI_user_handler,

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

@@ -0,0 +1,110 @@
//
// Created by stephane bourque on 2021-08-31.
//
#include "framework/MicroService.h"
using OpenWifi::RESTAPI_utils::field_to_json;
using OpenWifi::RESTAPI_utils::field_from_json;
using OpenWifi::RESTAPI_utils::EmbedDocument;
#include "RESTAPI_OWLSobjects.h"
// SIM -> 0x53/0x073, 0x49/0x69, 0x4d/0x6d
namespace OpenWifi::OWLSObjects {
void SimulationDetails::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"id", id);
field_to_json(Obj,"name", name);
field_to_json(Obj,"gateway", gateway);
field_to_json(Obj,"certificate", certificate);
field_to_json(Obj,"key", key);
field_to_json(Obj,"macPrefix", macPrefix);
field_to_json(Obj,"deviceType", deviceType);
field_to_json(Obj,"devices", devices);
field_to_json(Obj,"healthCheckInterval", healthCheckInterval);
field_to_json(Obj,"stateInterval", stateInterval);
field_to_json(Obj,"minAssociations", minAssociations);
field_to_json(Obj,"maxAssociations", maxAssociations);
field_to_json(Obj,"minClients", minClients);
field_to_json(Obj,"maxClients", maxClients);
field_to_json(Obj,"simulationLength", simulationLength);
field_to_json(Obj,"threads", threads);
field_to_json(Obj,"clientInterval", clientInterval);
field_to_json(Obj,"keepAlive", keepAlive);
field_to_json(Obj,"reconnectInterval", reconnectInterval);
field_to_json(Obj,"concurrentDevices", concurrentDevices);
}
bool SimulationDetails::from_json(const Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"id", id);
field_from_json(Obj,"name", name);
field_from_json(Obj,"gateway", gateway);
field_from_json(Obj,"certificate", certificate);
field_from_json(Obj,"key", key);
field_from_json(Obj,"macPrefix", macPrefix);
field_from_json(Obj,"deviceType", deviceType);
field_from_json(Obj,"devices", devices);
field_from_json(Obj,"healthCheckInterval", healthCheckInterval);
field_from_json(Obj,"stateInterval", stateInterval);
field_from_json(Obj,"minAssociations", minAssociations);
field_from_json(Obj,"maxAssociations", maxAssociations);
field_from_json(Obj,"minClients", minClients);
field_from_json(Obj,"maxClients", maxClients);
field_from_json(Obj,"simulationLength", simulationLength);
field_from_json(Obj,"threads", threads);
field_from_json(Obj,"clientInterval", clientInterval);
field_from_json(Obj,"keepAlive", keepAlive);
field_from_json(Obj,"reconnectInterval", reconnectInterval);
field_from_json(Obj,"concurrentDevices", concurrentDevices);
return true;
} catch(...) {
}
return false;
}
void SimulationDetailsList::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"list", list);
}
bool SimulationDetailsList::from_json(const Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"list", list);
return true;
} catch(...) {
}
return false;
}
void SimulationStatus::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"id", id);
field_to_json(Obj,"simulationId", simulationId);
field_to_json(Obj,"state", state);
field_to_json(Obj,"tx", tx);
field_to_json(Obj,"rx", rx);
field_to_json(Obj,"msgsTx", msgsTx);
field_to_json(Obj,"msgsRx", msgsRx);
field_to_json(Obj,"liveDevices", liveDevices);
field_to_json(Obj,"timeToFullDevices", timeToFullDevices);
field_to_json(Obj,"startTime", startTime);
field_to_json(Obj,"endTime", endTime);
field_to_json(Obj,"errorDevices", errorDevices);
field_to_json(Obj,"owner", owner);
}
void Dashboard::to_json([[maybe_unused]] Poco::JSON::Object &Obj) const {
}
bool Dashboard::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) {
return true;
}
void Dashboard::reset() {
}
}

View File

@@ -0,0 +1,77 @@
//
// Created by stephane bourque on 2021-08-31.
//
#ifndef UCENTRALSIM_RESTAPI_OWLSOBJECTS_H
#define UCENTRALSIM_RESTAPI_OWLSOBJECTS_H
#include <vector>
#include "Poco/JSON/Object.h"
namespace OpenWifi::OWLSObjects {
struct SimulationDetails {
std::string id;
std::string name;
std::string gateway;
std::string certificate;
std::string key;
std::string macPrefix;
std::string deviceType;
uint64_t devices = 5;
uint64_t healthCheckInterval = 60;
uint64_t stateInterval = 60 ;
uint64_t minAssociations = 1;
uint64_t maxAssociations = 3;
uint64_t minClients = 1 ;
uint64_t maxClients = 3;
uint64_t simulationLength = 60 * 60;
uint64_t threads = 16;
uint64_t clientInterval = 1;
uint64_t keepAlive = 300;
uint64_t reconnectInterval = 30 ;
uint64_t concurrentDevices = 5;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(const Poco::JSON::Object::Ptr &Obj);
};
struct SimulationDetailsList {
std::vector<SimulationDetails> list;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(const Poco::JSON::Object::Ptr &Obj);
};
struct SimulationStatus {
std::string id;
std::string simulationId;
std::string state;
uint64_t tx;
uint64_t rx;
uint64_t msgsTx;
uint64_t msgsRx;
uint64_t liveDevices;
uint64_t timeToFullDevices;
uint64_t startTime;
uint64_t endTime;
uint64_t errorDevices;
std::string owner;
void to_json(Poco::JSON::Object &Obj) const;
};
struct Dashboard {
int O;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(const Poco::JSON::Object::Ptr &Obj);
void reset();
};
}
#endif //UCENTRALSIM_RESTAPI_OWLSOBJECTS_H

View File

@@ -251,7 +251,8 @@ namespace OpenWifi {
VERIFY_EMAIL,
SUB_FORGOT_PASSWORD,
SUB_VERIFY_EMAIL,
SUB_SIGNUP
SUB_SIGNUP,
EMAIL_INVITATION
};
struct ActionLink {

View File

@@ -12,6 +12,7 @@
#include "Poco/Exception.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/Context.h"
#include "Poco/Net/NetException.h"
#include "SMTPMailerService.h"
#include "framework/MicroService.h"
@@ -31,7 +32,9 @@ namespace OpenWifi {
TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir());
MailRetry_ = MicroService::instance().ConfigGetInt("mailer.retry",2*60);
MailAbandon_ = MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60);
UseHTML_ = MicroService::instance().ConfigGetBool("mailer.html",false);
Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty());
EmailLogo_ = TemplateDir_ + "/" + MicroService::instance().ConfigGetString("mailer.logo","logo.jpg");
}
}
@@ -58,7 +61,7 @@ namespace OpenWifi {
PendingMessages_.push_back(MessageEvent{.Posted= OpenWifi::Now(),
.LastTry=0,
.Sent=0,
.File=Poco::File(TemplateDir_ + "/" +Name),
.TemplateName=Name,
.Attrs=Attrs});
return true;
}
@@ -83,12 +86,20 @@ namespace OpenWifi {
auto Recipient = i->Attrs.find(RECIPIENT_EMAIL)->second;
uint64_t now = OpenWifi::Now();
if((i->LastTry==0 || (now-i->LastTry)>MailRetry_)) {
if (SendIt(*i)) {
Logger().information(fmt::format("Attempting to deliver for mail '{}'.", Recipient));
i = Messages_.erase(i);
} else {
i->LastTry = now;
++i;
switch(SendIt(*i)) {
case MessageSendStatus::msg_sent: {
Logger().information(fmt::format("Attempting to deliver for mail '{}'.", Recipient));
i = Messages_.erase(i);
} break;
case MessageSendStatus::msg_not_sent_but_resend: {
Logger().information(fmt::format("Mail for '{}' was not. We will retry later.", Recipient));
i->LastTry = now;
++i;
} break;
case MessageSendStatus::msg_not_sent_but_do_not_resend: {
Logger().information(fmt::format("Mail for '{}' will not be sent. Check email address", Recipient));
i = Messages_.erase(i);
} break;
}
} else if ((now-i->Posted)>MailAbandon_) {
Logger().information(fmt::format("Mail for '{}' has timed out and will not be sent.", Recipient));
@@ -106,14 +117,12 @@ namespace OpenWifi {
}
}
bool SMTPMailerService::SendIt(const MessageEvent &Msg) {
MessageSendStatus SMTPMailerService::SendIt(const MessageEvent &Msg) {
std::string Recipient;
try
{
Poco::Net::MailMessage Message;
Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second;
auto H1 = Msg.Attrs.find(SENDER);
std::string TheSender;
if(H1!=Msg.Attrs.end()) {
@@ -121,32 +130,38 @@ namespace OpenWifi {
} else {
TheSender = Sender_ ;
}
Message.setSender( TheSender );
auto Message = std::make_unique<Poco::Net::MailMessage>();
Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second;
Message->setSender( TheSender );
Message->addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient));
Message->setSubject(Msg.Attrs.find(SUBJECT)->second);
Logger().information(fmt::format("Sending message to:{} from {}",Recipient,TheSender));
Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient));
Message.setSubject(Msg.Attrs.find(SUBJECT)->second);
if(Msg.Attrs.find(TEXT) != Msg.Attrs.end()) {
std::string Content = Msg.Attrs.find(TEXT)->second;
Message.addContent(new Poco::Net::StringPartSource(Content));
Message->addContent(new Poco::Net::StringPartSource(Content));
} else {
std::string Content = Utils::LoadFile(Msg.File);
// std::cout << "Mailing " << Content << std::endl;
Types::StringPairVec Variables;
FillVariables(Msg.Attrs, Variables);
Utils::ReplaceVariables(Content, Variables);
// std::cout << "Mailing " << Content << std::endl;
Message.addContent(new Poco::Net::StringPartSource(Content));
for(const auto &format:{"html","txt"}) {
std::string Content = Utils::LoadFile(TemplateDir_ + Msg.TemplateName + "." + format );
Types::StringPairVec Variables;
FillVariables(Msg.Attrs, Variables);
Utils::ReplaceVariables(Content, Variables);
Message->addContent(
new Poco::Net::StringPartSource(Content, (strcmp(format,"html") == 0 ? "text/html" : "text/plain") ));
}
}
auto Logo = Msg.Attrs.find(LOGO);
if(Logo!=Msg.Attrs.end()) {
try {
Poco::File LogoFile(AuthService::GetLogoAssetFileName());
Poco::File LogoFile(EmailLogo_);
std::ifstream IF(LogoFile.path());
std::ostringstream OS;
Poco::StreamCopier::copyStream(IF, OS);
Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png"));
Message->addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png"));
} catch (...) {
Logger().warning(fmt::format("Cannot add '{}' logo in email",AuthService::GetLogoAssetFileName()));
}
@@ -169,18 +184,23 @@ namespace OpenWifi {
SenderLoginUserName_,
SenderLoginPassword_
);
session.sendMessage(Message);
session.sendMessage(*Message);
session.close();
return true;
return MessageSendStatus::msg_sent;
}
catch (const Poco::Net::SMTPException &S) {
Logger().log(S);
return MessageSendStatus::msg_not_sent_but_do_not_resend;
}
catch (const Poco::Exception& E)
{
Logger().log(E);
return MessageSendStatus::msg_not_sent_but_resend;
}
catch (const std::exception &E) {
Logger().warning(fmt::format("Cannot send message to:{}, error: {}",Recipient, E.what()));
return MessageSendStatus::msg_not_sent_but_do_not_resend;
}
return false;
}
}

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) {
@@ -56,6 +58,12 @@ namespace OpenWifi {
}
typedef std::map<MESSAGE_ATTRIBUTES, std::string> MessageAttributes;
enum class MessageSendStatus {
msg_sent,
msg_not_sent_but_resend,
msg_not_sent_but_do_not_resend
};
class SMTPMailerService : public SubSystemServer, Poco::Runnable {
public:
static SMTPMailerService *instance() {
@@ -67,7 +75,7 @@ namespace OpenWifi {
uint64_t Posted=0;
uint64_t LastTry=0;
uint64_t Sent=0;
Poco::File File;
std::string TemplateName;
MessageAttributes Attrs;
};
@@ -76,7 +84,7 @@ namespace OpenWifi {
void Stop() override;
bool SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs);
bool SendIt(const MessageEvent &Msg);
MessageSendStatus SendIt(const MessageEvent &Msg);
void LoadMyConfig();
void reinitialize(Poco::Util::Application &self) override;
bool Enabled() const { return Enabled_; }
@@ -96,6 +104,8 @@ namespace OpenWifi {
Poco::Thread SenderThr_;
std::atomic_bool Running_=false;
bool Enabled_=false;
bool UseHTML_=false;
std::string EmailLogo_{"logo.jpg"};
SMTPMailerService() noexcept:
SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer")

View File

@@ -51,7 +51,7 @@ namespace OpenWifi {
Archivercallback_ = std::make_unique<Poco::TimerCallback<Archiver>>(Archiver_,&Archiver::onTimer);
Timer_.setStartInterval( 5 * 60 * 1000); // first run in 5 minutes
Timer_.setPeriodicInterval(1 * 60 * 60 * 1000); // 1 hours
Timer_.start(*Archivercallback_);
Timer_.start(*Archivercallback_, MicroService::instance().TimerPool());
return 0;
}
@@ -63,7 +63,7 @@ namespace OpenWifi {
}
void Archiver::onTimer([[maybe_unused]] Poco::Timer &timer) {
Utils::SetThreadName("archiver");
Utils::SetThreadName("strg-arch");
Poco::Logger &logger = Poco::Logger::get("STORAGE-ARCHIVER");
logger.information("Squiggy the DB: removing old tokens.");
StorageService()->SubTokenDB().CleanExpiredTokens();

View File

@@ -703,6 +703,19 @@ namespace OpenWifi::Utils {
return (std::all_of(UUID.begin(),UUID.end(),[&](auto i){ if(i=='-') dashes++; return i=='-' || std::isxdigit(i);})) && (dashes>0);
}
template <typename ...Args> std::string ComputeHash(Args&&... args) {
Poco::SHA2Engine E;
auto as_string = [](auto p) {
if constexpr(std::is_arithmetic_v<decltype(p)>) {
return std::to_string(p);
} else {
return p;
}
};
(E.update(as_string(args)),...);
return Poco::SHA2Engine::digestToHex(E.digest());
}
[[nodiscard]] inline std::vector<std::string> Split(const std::string &List, char Delimiter=',' ) {
std::vector<std::string> ReturnList;
@@ -1399,13 +1412,14 @@ namespace OpenWifi {
[[nodiscard]] inline const std::string &Address() const { return address_; };
[[nodiscard]] inline uint32_t Port() const { return port_; };
[[nodiscard]] inline const std::string &KeyFile() const { return key_file_; };
[[nodiscard]] inline const std::string &CertFile() const { return cert_file_; };
[[nodiscard]] inline const std::string &RootCA() const { return root_ca_; };
[[nodiscard]] inline const std::string &KeyFilePassword() const { return key_file_password_; };
[[nodiscard]] inline const std::string &IssuerCertFile() const { return issuer_cert_file_; };
[[nodiscard]] inline const std::string &Name() const { return name_; };
[[nodiscard]] inline auto KeyFile() const { return key_file_; };
[[nodiscard]] inline auto CertFile() const { return cert_file_; };
[[nodiscard]] inline auto RootCA() const { return root_ca_; };
[[nodiscard]] inline auto KeyFilePassword() const { return key_file_password_; };
[[nodiscard]] inline auto IssuerCertFile() const { return issuer_cert_file_; };
[[nodiscard]] inline auto Name() const { return name_; };
[[nodiscard]] inline int Backlog() const { return backlog_; }
[[nodiscard]] inline auto Cas() const { return cas_; }
[[nodiscard]] inline Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const {
Poco::Net::Context::Params P;
@@ -1885,8 +1899,8 @@ namespace OpenWifi {
Request = &RequestIn;
Response = &ResponseIn;
std::string th_name = "restsvr_" + std::to_string(TransactionId_);
Utils::SetThreadName(th_name.c_str());
// std::string th_name = "restsvr_" + std::to_string(TransactionId_);
// Utils::SetThreadName(th_name.c_str());
if(Request->getContentLength()>0) {
if(Request->getContentType().find("application/json")!=std::string::npos) {
@@ -2432,6 +2446,7 @@ namespace OpenWifi {
Poco::Net::HTTPServerResponse *Response= nullptr;
SecurityObjects::UserInfoAndPolicy UserInfo_;
QueryBlock QB_;
const std::string & Requester() const { return REST_Requester_; }
protected:
BindingMap Bindings_;
Poco::URI::QueryParameters Parameters_;
@@ -2448,6 +2463,7 @@ namespace OpenWifi {
RateLimit MyRates_;
uint64_t TransactionId_;
Poco::JSON::Object::Ptr ParsedBody_;
std::string REST_Requester_;
};
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
@@ -2712,7 +2728,7 @@ namespace OpenWifi {
inline void run() override {
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
Utils::SetThreadName("kafka-dispatch");
Utils::SetThreadName("kafka:dispatch");
while(Note && Running_) {
auto Msg = dynamic_cast<KafkaMessage*>(Note.get());
if(Msg!= nullptr) {
@@ -2735,12 +2751,12 @@ namespace OpenWifi {
}
private:
std::recursive_mutex Mutex_;
Types::NotifyTable Notifiers_;
Poco::Thread Worker_;
mutable std::atomic_bool Running_=false;
uint64_t FunctionId_=1;
Poco::NotificationQueue Queue_;
std::recursive_mutex Mutex_;
Types::NotifyTable Notifiers_;
Poco::Thread Worker_;
mutable std::atomic_bool Running_=false;
uint64_t FunctionId_=1;
Poco::NotificationQueue Queue_;
};
class KafkaManager : public SubSystemServer {
@@ -3022,7 +3038,7 @@ namespace OpenWifi {
}
int Start() override;
inline void Stop() override {
Logger().information("Stopping ");
Logger().information("Stopping...");
for( const auto & svr : RESTServers_ )
svr->stop();
Pool_.stopAll();
@@ -3030,22 +3046,23 @@ namespace OpenWifi {
RESTServers_.clear();
}
inline void reinitialize(Poco::Util::Application &self) override;
inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) {
RESTAPIHandler::BindingMap Bindings;
Utils::SetThreadName(fmt::format("rest_ext_{}",Id).c_str());
Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str());
return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id);
}
const Poco::ThreadPool & Pool() { return Pool_; }
private:
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
Poco::ThreadPool Pool_;
Poco::ThreadPool Pool_{"x-rest",2,32};
RESTAPI_GenericServer Server_;
RESTAPI_ExtServer() noexcept:
SubSystemServer("RESTAPI_ExtServer", "RESTAPIServer", "openwifi.restapi"),
Pool_("RESTAPI_ExtServer",4,50,120)
SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi")
{
}
};
@@ -3058,7 +3075,7 @@ namespace OpenWifi {
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override {
try {
Poco::URI uri(Request.getURI());
Utils::SetThreadName(fmt::format("rest_ext_{}",TransactionId_).c_str());
Utils::SetThreadName(fmt::format("x-rest:{}",TransactionId_).c_str());
return RESTAPI_ExtServer()->CallServer(uri.getPath(), TransactionId_++);
} catch (...) {
@@ -3156,7 +3173,7 @@ namespace OpenWifi {
inline int Start() override;
inline void Stop() override {
Logger().information("Stopping ");
Logger().information("Stopping...");
for( const auto & svr : RESTServers_ )
svr->stop();
Pool_.stopAll();
@@ -3167,17 +3184,18 @@ namespace OpenWifi {
inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) {
RESTAPIHandler::BindingMap Bindings;
Utils::SetThreadName(fmt::format("rest_int_{}",Id).c_str());
Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str());
return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id);
}
const Poco::ThreadPool & Pool() { return Pool_; }
private:
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
Poco::ThreadPool Pool_;
Poco::ThreadPool Pool_{"i-rest",2,16};
RESTAPI_GenericServer Server_;
RESTAPI_IntServer() noexcept:
SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi"),
Pool_("RESTAPI_IntServer",4,50,120)
SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi")
{
}
};
@@ -3188,6 +3206,7 @@ namespace OpenWifi {
public:
inline IntRequestHandlerFactory() = default;
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override {
Utils::SetThreadName(fmt::format("i-rest:{}",TransactionId_).c_str());
Poco::URI uri(Request.getURI());
return RESTAPI_IntServer()->CallServer(uri.getPath(), TransactionId_);
}
@@ -3231,7 +3250,6 @@ namespace OpenWifi {
}
[[nodiscard]] std::string Version() { return Version_; }
// [[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; }
[[nodiscard]] inline const std::string & DataDir() { return DataDir_; }
[[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; }
[[nodiscard]] bool Debug() const { return DebugMode_; }
@@ -3264,7 +3282,12 @@ namespace OpenWifi {
return Poco::Logger::get(Name);
}
static inline void Exit(int Reason);
virtual void GetExtraConfiguration(Poco::JSON::Object & Cfg) {
Cfg.set("additionalConfiguration",false);
}
static inline void Exit(int Reason);
inline void BusMessageReceived(const std::string &Key, const std::string & Payload);
inline MicroServiceMetaVec GetServices(const std::string & Type);
inline MicroServiceMetaVec GetServices();
@@ -3300,7 +3323,6 @@ namespace OpenWifi {
inline std::string ConfigPath(const std::string &Key);
inline std::string Encrypt(const std::string &S);
inline std::string Decrypt(const std::string &S);
inline std::string CreateHash(const std::string &S);
inline std::string MakeSystemEventMessage( const std::string & Type ) const;
[[nodiscard]] inline bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request);
inline static void SavePID();
@@ -3330,6 +3352,9 @@ namespace OpenWifi {
return Signer_.sign(T,Algo);
}
}
inline Poco::ThreadPool & TimerPool() { return TimerPool_; }
private:
static MicroService * instance_;
bool HelpRequested_ = false;
@@ -3343,7 +3368,6 @@ namespace OpenWifi {
std::string WWWAssetsDir_;
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
Poco::Crypto::Cipher * Cipher_ = nullptr;
Poco::SHA2Engine SHA2_;
MicroServiceMetaMap Services_;
std::string MyHash_;
std::string MyPrivateEndPoint_;
@@ -3364,6 +3388,7 @@ namespace OpenWifi {
bool NoBuiltInCrypto_=false;
Poco::JWT::Signer Signer_;
Poco::Logger &Logger_;
Poco::ThreadPool TimerPool_{"timer:pool",2,16};
};
inline void MicroService::Exit(int Reason) {
@@ -3513,7 +3538,7 @@ namespace OpenWifi {
MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private");
MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public");
UIURI_ = ConfigGetString("openwifi.system.uri.ui");
MyHash_ = CreateHash(MyPublicEndPoint_);
MyHash_ = Utils::ComputeHash(MyPublicEndPoint_);
}
void MicroServicePostInitialization();
@@ -3576,8 +3601,6 @@ namespace OpenWifi {
void DaemonPostInitialization(Poco::Util::Application &self);
inline void MicroService::initialize(Poco::Util::Application &self) {
// Utils::SetThreadName("microservice");
// add the default services
LoadConfigurationFile();
InitializeLoggingSystem();
@@ -3852,11 +3875,6 @@ namespace OpenWifi {
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
}
inline std::string MicroService::CreateHash(const std::string &S) {
SHA2_.update(S);
return Utils::ToHex(SHA2_.digest());
}
inline std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const {
Poco::JSON::Object Obj;
Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type);
@@ -3919,6 +3937,7 @@ namespace OpenWifi {
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
Params->setName("ws:xrest");
std::unique_ptr<Poco::Net::HTTPServer> NewServer;
if(MicroService::instance().NoAPISecurity()) {
@@ -3955,6 +3974,7 @@ namespace OpenWifi {
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
Params->setName("ws:irest");
std::unique_ptr<Poco::Net::HTTPServer> NewServer;
if(MicroService::instance().NoAPISecurity()) {
@@ -3972,8 +3992,6 @@ namespace OpenWifi {
}
inline int MicroService::main([[maybe_unused]] const ArgVec &args) {
// Utils::SetThreadName("main");
MyErrorHandler ErrorHandler(*this);
Poco::ErrorHandler::set(&ErrorHandler);
@@ -4080,6 +4098,7 @@ namespace OpenWifi {
Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015);
Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_);
auto Params = new Poco::Net::HTTPServerParams;
Params->setName("ws:alb");
Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger()), *Socket_, Params);
Server_->start();
}
@@ -4089,7 +4108,7 @@ namespace OpenWifi {
inline void BusEventManager::run() {
Running_ = true;
Utils::SetThreadName("BusEventManager");
Utils::SetThreadName("fmwk:EventMgr");
auto Msg = MicroService::instance().MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroService::instance().PrivateEndPoint(),Msg, false);
while(Running_) {
@@ -4176,7 +4195,7 @@ namespace OpenWifi {
inline void KafkaProducer::run() {
Utils::SetThreadName("KafkaProducer");
Utils::SetThreadName("Kafka:Prod");
cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }
@@ -4215,7 +4234,7 @@ namespace OpenWifi {
}
inline void KafkaConsumer::run() {
Utils::SetThreadName("KafkaConsumer");
Utils::SetThreadName("Kafka:Cons");
cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
@@ -4355,6 +4374,11 @@ namespace OpenWifi {
Answer.set("certificates", Certificates);
return ReturnObject(Answer);
}
if(GetBoolParameter("extraConfiguration")) {
Poco::JSON::Object Answer;
MicroService::instance().GetExtraConfiguration(Answer);
return ReturnObject(Answer);
}
BadRequest(RESTAPI::Errors::InvalidCommand);
}
@@ -4727,6 +4751,7 @@ namespace OpenWifi {
inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) {
if(Internal_ && Request->has("X-INTERNAL-NAME")) {
auto Allowed = MicroService::instance().IsValidAPIKEY(*Request);
Contacted = true;
if(!Allowed) {
if(Server_.LogBadTokens(false)) {
Logger_.debug(fmt::format("I-REQ-DENIED({}): Method={} Path={}",
@@ -4735,6 +4760,7 @@ namespace OpenWifi {
}
} else {
auto Id = Request->get("X-INTERNAL-NAME", "unknown");
REST_Requester_ = Id;
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(fmt::format("I-REQ-ALLOWED({}): User='{}' Method={} Path={}",
Utils::FormatIPv6(Request->clientAddress().toString()), Id,
@@ -4758,6 +4784,7 @@ namespace OpenWifi {
#else
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Contacted, Sub)) {
#endif
REST_Requester_ = UserInfo_.userinfo.email;
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(fmt::format("X-REQ-ALLOWED({}): User='{}@{}' Method={} Path={}",
UserInfo_.userinfo.email,
@@ -4876,7 +4903,7 @@ namespace OpenWifi {
void SendToAll(const std::string &Payload);
private:
mutable std::atomic_bool Running_ = false;
Poco::Thread Thr_;
Poco::Thread Thr_;
// std::unique_ptr<MyParallelSocketReactor> ReactorPool_;
Poco::Net::SocketReactor Reactor_;
Poco::Thread ReactorThread_;
@@ -4966,13 +4993,13 @@ namespace OpenWifi {
[[nodiscard]] inline bool SendToUser(const std::string &userName, const std::string &Payload);
inline WebSocketClientServer::WebSocketClientServer() noexcept:
SubSystemServer("WebSocketClientServer", "WSCLNT-SVR", "websocketclients")
SubSystemServer("WebSocketClientServer", "UI-WSCLNT-SVR", "websocketclients")
{
}
inline void WebSocketClientServer::run() {
Running_ = true ;
Utils::SetThreadName("ws:clnt-svr");
Utils::SetThreadName("ws:uiclnt-svr");
while(Running_) {
Poco::Thread::trySleep(2000);
@@ -5065,7 +5092,7 @@ namespace OpenWifi {
case Poco::Net::WebSocket::FRAME_OP_PONG: {
} break;
case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
Logger().warning(Poco::format("CLOSE(%s): Client is closing its connection.", Id_));
Logger().warning(Poco::format("CLOSE(%s): UI Client is closing its connection.", Id_));
Done = true;
} break;
case Poco::Net::WebSocket::FRAME_OP_TEXT: {
@@ -5204,7 +5231,7 @@ namespace OpenWifi {
try
{
Poco::Net::WebSocket WS(*Request, *Response);
Logger().information("WebSocket connection established.");
Logger().information("UI-WebSocket connection established.");
auto Id = MicroService::CreateUUID();
WebSocketClientServer()->NewClient(WS,Id);
}

View File

@@ -29,7 +29,7 @@ namespace OpenWifi {
}
static std::string MakeSessionId(const std::string & token) {
return MicroService::instance().CreateHash(token);
return Utils::ComputeHash(token);
}
void LoginDB::AddLogin( const std::string & userId, const std::string & email, const std::string &token) {

View File

@@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>eMail Invitation</title>
</head>
<body>
</body>
</html>

View File

@@ -0,0 +1,7 @@
Dear ${RECIPIENT_EMAIL},
You have been invited to join the OpenWifi system. Please click below and follow the instructions
${ACTION_LINK}
Thank you!

View File

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

View File

@@ -2,7 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<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>

View File

View File

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