mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
synced 2025-10-30 02:12:32 +00:00
Compare commits
63 Commits
v2.6.0-RC1
...
v2.7.0-RC1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b1fbf055f | ||
|
|
8b5c9dd5e9 | ||
|
|
02a315ab0d | ||
|
|
1e4d9ea4e8 | ||
|
|
0b1d7e39eb | ||
|
|
af190e9967 | ||
|
|
80d3dfb89f | ||
|
|
62c6b119c9 | ||
|
|
4ea8aa9958 | ||
|
|
6a30353b3a | ||
|
|
b355b41d4f | ||
|
|
19b2afb469 | ||
|
|
7d65da3abc | ||
|
|
c25059e2aa | ||
|
|
122a73f35e | ||
|
|
a454b56c7a | ||
|
|
ae82160c7f | ||
|
|
4d73bbd605 | ||
|
|
13bec235a1 | ||
|
|
e6c196cd67 | ||
|
|
6d9a1cac09 | ||
|
|
55a43ed40d | ||
|
|
3a230e4250 | ||
|
|
0a6ee4ea47 | ||
|
|
a430ad7e71 | ||
|
|
d1c13ad2dd | ||
|
|
b837e41569 | ||
|
|
5e39987e36 | ||
|
|
890eb7311a | ||
|
|
fc509adf01 | ||
|
|
767331f575 | ||
|
|
d26ef6eeba | ||
|
|
8c672f058f | ||
|
|
448563ab06 | ||
|
|
2a22a35e58 | ||
|
|
e745d4efe7 | ||
|
|
701e0b50ff | ||
|
|
df082a969e | ||
|
|
5e1937ec4f | ||
|
|
e679dc7458 | ||
|
|
7e1a962b57 | ||
|
|
8ad2e12f12 | ||
|
|
23d16e619a | ||
|
|
760cad9a14 | ||
|
|
94997a1f9f | ||
|
|
9060fef03d | ||
|
|
2f8eb90c5a | ||
|
|
c0d0435efa | ||
|
|
6942de0475 | ||
|
|
cce2528ec4 | ||
|
|
3be0fd45d9 | ||
|
|
8b1a80ce09 | ||
|
|
5e12f00558 | ||
|
|
1d534cb974 | ||
|
|
a7e9c96f8d | ||
|
|
cb3f7a0872 | ||
|
|
6ad434c02f | ||
|
|
62e3ada15c | ||
|
|
2beef2daba | ||
|
|
4b131465fb | ||
|
|
cafc243e55 | ||
|
|
5c44134f9d | ||
|
|
8ed86d3582 |
8
.github/workflows/cleanup.yml
vendored
8
.github/workflows/cleanup.yml
vendored
@@ -17,4 +17,10 @@ jobs:
|
||||
steps:
|
||||
- run: |
|
||||
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
||||
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owsec/$PR_BRANCH_TAG"
|
||||
|
||||
if [[ ! $PR_BRANCH_TAG =~ (main|master|release-*) ]]; then
|
||||
echo "PR branch is $PR_BRANCH_TAG, deleting Docker image"
|
||||
curl -s -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owsec/$PR_BRANCH_TAG"
|
||||
else
|
||||
echo "PR branch is $PR_BRANCH_TAG, not deleting Docker image"
|
||||
fi
|
||||
|
||||
@@ -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
|
||||
|
||||
41
Dockerfile
41
Dockerfile
@@ -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
37
OPERATOR.md
Normal 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.
|
||||
|
||||
@@ -5,7 +5,7 @@ if [ "$SELFSIGNED_CERTS" = 'true' ]; then
|
||||
update-ca-certificates
|
||||
fi
|
||||
|
||||
if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWSEC_CONFIG"/owsec.properties ]]; then
|
||||
if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
|
||||
RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\$OWSEC_ROOT/certs/restapi-ca.pem"} \
|
||||
RESTAPI_HOST_PORT=${RESTAPI_HOST_PORT:-"16001"} \
|
||||
RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWSEC_ROOT/certs/restapi-cert.pem"} \
|
||||
|
||||
2
helm/.gitignore
vendored
2
helm/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
*.swp
|
||||
Chart.lock
|
||||
charts/
|
||||
|
||||
@@ -5,14 +5,14 @@ name: owsec
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 10.9.2
|
||||
condition: postgresql.enabled
|
||||
- name: mysql
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 8.8.3
|
||||
condition: mysql.enabled
|
||||
- name: mariadb
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 9.4.2
|
||||
condition: mariadb.enabled
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "ActionLinkManager.h"
|
||||
#include "StorageService.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
#include "MessagingTemplates.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -24,6 +25,7 @@ namespace OpenWifi {
|
||||
|
||||
void ActionLinkManager::run() {
|
||||
Running_ = true ;
|
||||
Utils::SetThreadName("action-mgr");
|
||||
|
||||
while(Running_) {
|
||||
Poco::Thread::trySleep(2000);
|
||||
@@ -52,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);
|
||||
@@ -64,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);
|
||||
@@ -80,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);
|
||||
@@ -88,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);
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
#include "Poco/Environment.h"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
8
src/MessagingTemplates.cpp
Normal file
8
src/MessagingTemplates.cpp
Normal 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
75
src/MessagingTemplates.h
Normal 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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -12,6 +12,10 @@ namespace OpenWifi {
|
||||
void OpenWifi::RESTAPI_sms_handler::DoPost() {
|
||||
const auto &Obj = ParsedBody_;
|
||||
|
||||
if(!SMSSender()->Enabled()) {
|
||||
return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
|
||||
}
|
||||
|
||||
std::string Arg;
|
||||
if(HasParameter("validateNumber",Arg) && Arg=="true" && Obj->has("to")) {
|
||||
auto Number = Obj->get("to").toString();
|
||||
|
||||
@@ -76,12 +76,21 @@ namespace OpenWifi {
|
||||
return BadRequest(RESTAPI::Errors::SMSMissingPhoneNumber);
|
||||
}
|
||||
|
||||
if(!SMSSender()->Enabled()) {
|
||||
return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
|
||||
}
|
||||
|
||||
if (SMSSender()->StartValidation(MFC.sms, UserInfo_.userinfo.email)) {
|
||||
return OK();
|
||||
} else {
|
||||
return InternalError(RESTAPI::Errors::SMSTryLater);
|
||||
}
|
||||
} else if (GetBoolParameter("completeValidation", false)) {
|
||||
|
||||
if(!SMSSender()->Enabled()) {
|
||||
return BadRequest(RESTAPI::Errors::SMSMFANotEnabled);
|
||||
}
|
||||
|
||||
auto ChallengeCode = GetParameter("challengeCode", "");
|
||||
if (ChallengeCode.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::SMSMissingChallenge);
|
||||
|
||||
@@ -11,7 +11,6 @@ namespace OpenWifi {
|
||||
|
||||
auto Reset = GetBoolParameter("reset",false);
|
||||
std::string QRCode;
|
||||
|
||||
if(TotpCache()->StartValidation(UserInfo_.userinfo,false,QRCode,Reset)) {
|
||||
return SendFileContent(QRCode, "image/svg+xml","qrcode.svg");
|
||||
}
|
||||
|
||||
@@ -538,8 +538,8 @@ namespace OpenWifi::AnalyticsObjects {
|
||||
|
||||
void WifiClientHistory::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"timestamp",timestamp);
|
||||
field_to_json(Obj,"stationId",stationId);
|
||||
field_to_json(Obj,"bssId",bssId);
|
||||
field_to_json(Obj,"station_id",station_id);
|
||||
field_to_json(Obj,"bssid",bssid);
|
||||
field_to_json(Obj,"ssid",ssid);
|
||||
field_to_json(Obj,"rssi",rssi);
|
||||
field_to_json(Obj,"rx_bitrate",rx_bitrate);
|
||||
@@ -573,13 +573,14 @@ namespace OpenWifi::AnalyticsObjects {
|
||||
field_to_json(Obj,"connected",connected);
|
||||
field_to_json(Obj,"inactive",inactive);
|
||||
field_to_json(Obj,"tx_retries",tx_retries);
|
||||
field_to_json(Obj,"venue_id",venue_id);
|
||||
}
|
||||
|
||||
bool WifiClientHistory::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"timestamp",timestamp);
|
||||
field_from_json(Obj,"stationId",stationId);
|
||||
field_from_json(Obj,"bssId",bssId);
|
||||
field_from_json(Obj,"station_id",station_id);
|
||||
field_from_json(Obj,"bssid",bssid);
|
||||
field_from_json(Obj,"ssid",ssid);
|
||||
field_from_json(Obj,"rssi",rssi);
|
||||
field_from_json(Obj,"rx_bitrate",rx_bitrate);
|
||||
@@ -613,6 +614,7 @@ namespace OpenWifi::AnalyticsObjects {
|
||||
field_from_json(Obj,"connected",connected);
|
||||
field_from_json(Obj,"inactive",inactive);
|
||||
field_from_json(Obj,"tx_retries",tx_retries);
|
||||
field_from_json(Obj,"venue_id",venue_id);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
|
||||
@@ -376,8 +376,8 @@ namespace OpenWifi {
|
||||
|
||||
struct WifiClientHistory {
|
||||
uint64_t timestamp=OpenWifi::Now();
|
||||
std::string stationId;
|
||||
std::string bssId;
|
||||
std::string station_id;
|
||||
std::string bssid;
|
||||
std::string ssid;
|
||||
int64_t rssi=0;
|
||||
uint32_t rx_bitrate=0;
|
||||
@@ -411,6 +411,7 @@ namespace OpenWifi {
|
||||
uint64_t connected=0;
|
||||
uint64_t inactive=0;
|
||||
uint64_t tx_retries=0;
|
||||
std::string venue_id;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
|
||||
@@ -3,176 +3,206 @@
|
||||
//
|
||||
|
||||
#include "RESTAPI_CertObjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
|
||||
namespace OpenWifi {
|
||||
namespace CertObjects {
|
||||
void CertificateEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"type", type);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"certificate", certificate);
|
||||
field_to_json(Obj,"key", key);
|
||||
field_to_json(Obj,"devid", devid);
|
||||
field_to_json(Obj,"cas", cas);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonName", commonName);
|
||||
field_to_json(Obj,"certificateId", certificateId);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"revoked", revoked);
|
||||
field_to_json(Obj,"revokeCount", revokeCount);
|
||||
}
|
||||
namespace OpenWifi::CertObjects {
|
||||
void CertificateEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"type", type);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"certificate", certificate);
|
||||
field_to_json(Obj,"key", key);
|
||||
field_to_json(Obj,"devid", devid);
|
||||
field_to_json(Obj,"cas", cas);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonName", commonName);
|
||||
field_to_json(Obj,"certificateId", certificateId);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"revoked", revoked);
|
||||
field_to_json(Obj,"revokeCount", revokeCount);
|
||||
field_to_json(Obj,"synched", synched);
|
||||
}
|
||||
|
||||
bool CertificateEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"type", type);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"certificate", certificate);
|
||||
field_from_json(Obj,"key", key);
|
||||
field_from_json(Obj,"devid", devid);
|
||||
field_from_json(Obj,"cas", cas);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonName", commonName);
|
||||
field_from_json(Obj,"certificateId", certificateId);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"revoked", revoked);
|
||||
field_from_json(Obj,"revokeCount", revokeCount);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool CertificateEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"type", type);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"certificate", certificate);
|
||||
field_from_json(Obj,"key", key);
|
||||
field_from_json(Obj,"devid", devid);
|
||||
field_from_json(Obj,"cas", cas);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonName", commonName);
|
||||
field_from_json(Obj,"certificateId", certificateId);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"revoked", revoked);
|
||||
field_from_json(Obj,"revokeCount", revokeCount);
|
||||
field_from_json(Obj,"synched", synched);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EntityEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_to_json(Obj,"apiKey", apiKey);
|
||||
field_to_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_to_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_to_json(Obj,"organization", organization);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"suspended", suspended);
|
||||
field_to_json(Obj,"deleted", deleted);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
}
|
||||
void EntityEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_to_json(Obj,"apiKey", apiKey);
|
||||
field_to_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_to_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_to_json(Obj,"organization", organization);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"suspended", suspended);
|
||||
field_to_json(Obj,"deleted", deleted);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
}
|
||||
|
||||
bool EntityEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_from_json(Obj,"apiKey", apiKey);
|
||||
field_from_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_from_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_from_json(Obj,"organization", organization);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"suspended", suspended);
|
||||
field_from_json(Obj,"deleted", deleted);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool EntityEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_from_json(Obj,"apiKey", apiKey);
|
||||
field_from_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_from_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_from_json(Obj,"organization", organization);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"suspended", suspended);
|
||||
field_from_json(Obj,"deleted", deleted);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BatchEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"jobHistory", jobHistory);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
}
|
||||
void BatchEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"jobHistory", jobHistory);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
}
|
||||
|
||||
bool BatchEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"jobHistory", jobHistory);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool BatchEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"jobHistory", jobHistory);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JobEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"completedNames", completedNames);
|
||||
field_to_json(Obj,"errorNames", errorNames);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"command", command);
|
||||
field_to_json(Obj,"parameters", parameters);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
}
|
||||
void JobEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"completedNames", completedNames);
|
||||
field_to_json(Obj,"errorNames", errorNames);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"command", command);
|
||||
field_to_json(Obj,"parameters", parameters);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
}
|
||||
|
||||
bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"completedNames", completedNames);
|
||||
field_from_json(Obj,"errorNames", errorNames);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"command", command);
|
||||
field_from_json(Obj,"parameters", parameters);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"completedNames", completedNames);
|
||||
field_from_json(Obj,"errorNames", errorNames);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"command", command);
|
||||
field_from_json(Obj,"parameters", parameters);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DashBoardYearlyStats::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "year", year);
|
||||
field_to_json(Obj, "activeCerts", activeCerts);
|
||||
field_to_json(Obj, "revokedCerts", revokedCerts);
|
||||
}
|
||||
|
||||
void Dashboard::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"snapshot", snapshot);
|
||||
field_to_json(Obj,"numberOfIssuedCerts", numberOfIssuedCerts);
|
||||
field_to_json(Obj,"numberOfRevokedCerts", numberOfRevokedCerts);
|
||||
field_to_json(Obj,"activeCertsPerOrganization", activeCertsPerOrganization);
|
||||
field_to_json(Obj,"revokedCertsPerOrganization", revokedCertsPerOrganization);
|
||||
field_to_json(Obj,"numberOfRedirectors", numberOfRedirectors);
|
||||
field_to_json(Obj,"deviceTypes", deviceTypes);
|
||||
field_to_json(Obj,"monthlyNumberOfCerts", monthlyNumberOfCerts);
|
||||
field_to_json(Obj,"monthlyNumberOfCertsPerOrgPerYear", monthlyNumberOfCertsPerOrgPerYear);
|
||||
}
|
||||
|
||||
void Dashboard::reset() {
|
||||
snapshot=0;
|
||||
numberOfRevokedCerts = numberOfIssuedCerts = 0;
|
||||
activeCertsPerOrganization.clear();
|
||||
revokedCertsPerOrganization.clear();
|
||||
numberOfRedirectors.clear();
|
||||
deviceTypes.clear();
|
||||
monthlyNumberOfCerts.clear();
|
||||
monthlyNumberOfCertsPerOrgPerYear.clear();
|
||||
}
|
||||
}
|
||||
@@ -5,97 +5,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
namespace OpenWifi::CertObjects {
|
||||
|
||||
namespace CertObjects {
|
||||
struct CertificateEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string type;
|
||||
std::string status;
|
||||
std::string certificate;
|
||||
std::string key;
|
||||
std::string devid;
|
||||
std::string cas;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::string commonName;
|
||||
std::string certificateId;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
uint64_t created = 0;
|
||||
uint64_t modified = 0;
|
||||
uint64_t revoked = 0;
|
||||
uint64_t revokeCount = 0;
|
||||
uint64_t synched = 0;
|
||||
|
||||
struct CertificateEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string type;
|
||||
std::string status;
|
||||
std::string certificate;
|
||||
std::string key;
|
||||
std::string devid;
|
||||
std::string cas;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::string commonName;
|
||||
std::string certificateId;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
uint64_t created = 0;
|
||||
uint64_t modified = 0;
|
||||
uint64_t revoked = 0;
|
||||
uint64_t revokeCount = 0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct EntityEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string defaultRedirector;
|
||||
std::string apiKey;
|
||||
std::string serverEnrollmentProfile;
|
||||
std::string clientEnrollmentProfile;
|
||||
std::string organization;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
bool suspended=false;
|
||||
bool deleted=false;
|
||||
uint64_t created = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
|
||||
struct EntityEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string defaultRedirector;
|
||||
std::string apiKey;
|
||||
std::string serverEnrollmentProfile;
|
||||
std::string clientEnrollmentProfile;
|
||||
std::string organization;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
bool suspended=false;
|
||||
bool deleted=false;
|
||||
uint64_t created = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct BatchEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::vector<std::string> commonNames;
|
||||
std::vector<std::string> jobHistory;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
uint64_t submitted = 0 ;
|
||||
uint64_t started = 0 ;
|
||||
uint64_t completed = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
|
||||
struct BatchEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::vector<std::string> commonNames;
|
||||
std::vector<std::string> jobHistory;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
uint64_t submitted = 0 ;
|
||||
uint64_t started = 0 ;
|
||||
uint64_t completed = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct JobEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
std::string command;
|
||||
OpenWifi::Types::StringVec commonNames;
|
||||
OpenWifi::Types::StringVec completedNames;
|
||||
OpenWifi::Types::StringVec errorNames;
|
||||
Types::StringPairVec parameters;
|
||||
std::string status;
|
||||
uint64_t submitted=0;
|
||||
uint64_t started=0;
|
||||
uint64_t completed=0;
|
||||
|
||||
struct JobEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
std::string command;
|
||||
OpenWifi::Types::StringVec commonNames;
|
||||
OpenWifi::Types::StringVec completedNames;
|
||||
OpenWifi::Types::StringVec errorNames;
|
||||
Types::StringPairVec parameters;
|
||||
std::string status;
|
||||
uint64_t submitted=0;
|
||||
uint64_t started=0;
|
||||
uint64_t completed=0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DashBoardYearlyStats {
|
||||
uint64_t year=0;
|
||||
OpenWifi::Types::Counted3DMapSII activeCerts;
|
||||
OpenWifi::Types::Counted3DMapSII revokedCerts;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct Dashboard {
|
||||
uint64_t snapshot=0;
|
||||
uint64_t numberOfIssuedCerts=0;
|
||||
uint64_t numberOfRevokedCerts=0;
|
||||
OpenWifi::Types::CountedMap activeCertsPerOrganization;
|
||||
OpenWifi::Types::CountedMap revokedCertsPerOrganization;
|
||||
OpenWifi::Types::CountedMap numberOfRedirectors;
|
||||
OpenWifi::Types::CountedMap deviceTypes;
|
||||
OpenWifi::Types::CountedMap monthlyNumberOfCerts;
|
||||
std::vector<DashBoardYearlyStats> monthlyNumberOfCertsPerOrgPerYear;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void reset();
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
}
|
||||
}
|
||||
110
src/RESTObjects/RESTAPI_OWLSobjects.cpp
Normal file
110
src/RESTObjects/RESTAPI_OWLSobjects.cpp
Normal 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() {
|
||||
|
||||
}
|
||||
}
|
||||
77
src/RESTObjects/RESTAPI_OWLSobjects.h
Normal file
77
src/RESTObjects/RESTAPI_OWLSobjects.h
Normal 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
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Data/LOB.h"
|
||||
@@ -27,8 +28,13 @@ namespace OpenWifi {
|
||||
bool Delete_ = true;
|
||||
bool PortalLogin_ = true;
|
||||
|
||||
AclTemplate() noexcept = default;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj); };
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
static_assert( std::is_nothrow_move_constructible_v<AclTemplate> );
|
||||
|
||||
struct WebToken {
|
||||
std::string access_token_;
|
||||
@@ -245,7 +251,8 @@ namespace OpenWifi {
|
||||
VERIFY_EMAIL,
|
||||
SUB_FORGOT_PASSWORD,
|
||||
SUB_VERIFY_EMAIL,
|
||||
SUB_SIGNUP
|
||||
SUB_SIGNUP,
|
||||
EMAIL_INVITATION
|
||||
};
|
||||
|
||||
struct ActionLink {
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace OpenWifi {
|
||||
|
||||
bool SMSSender::StartValidation(const std::string &Number, const std::string &UserName) {
|
||||
std::lock_guard G(Mutex_);
|
||||
if(!Enabled_)
|
||||
return false;
|
||||
CleanCache();
|
||||
uint64_t Now=OpenWifi::Now();
|
||||
auto Challenge = MFAServer::MakeChallenge();
|
||||
@@ -56,6 +58,9 @@ namespace OpenWifi {
|
||||
bool SMSSender::IsNumberValid(const std::string &Number, const std::string &UserName) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
if(!Enabled_)
|
||||
return false;
|
||||
|
||||
for(const auto &i:Cache_) {
|
||||
if(i.Number==Number && i.UserName==UserName)
|
||||
return i.Validated;
|
||||
@@ -66,6 +71,9 @@ namespace OpenWifi {
|
||||
bool SMSSender::CompleteValidation(const std::string &Number, const std::string &Code, const std::string &UserName) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
if(!Enabled_)
|
||||
return false;
|
||||
|
||||
for(auto &i:Cache_) {
|
||||
if(i.Code==Code && i.Number==Number && i.UserName==UserName) {
|
||||
i.Validated=true;
|
||||
|
||||
@@ -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,14 +61,14 @@ namespace OpenWifi {
|
||||
PendingMessages_.push_back(MessageEvent{.Posted= OpenWifi::Now(),
|
||||
.LastTry=0,
|
||||
.Sent=0,
|
||||
.File=Poco::File(TemplateDir_ + "/" +Name),
|
||||
.TemplateName=Name,
|
||||
.Attrs=Attrs});
|
||||
return true;
|
||||
}
|
||||
|
||||
void SMTPMailerService::run() {
|
||||
Running_ = true;
|
||||
|
||||
Utils::SetThreadName("smtp-mailer");
|
||||
while(Running_) {
|
||||
|
||||
Poco::Thread::trySleep(10000);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,6 +63,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void Archiver::onTimer([[maybe_unused]] Poco::Timer &timer) {
|
||||
Utils::SetThreadName("strg-arch");
|
||||
Poco::Logger &logger = Poco::Logger::get("STORAGE-ARCHIVER");
|
||||
logger.information("Squiggy the DB: removing old tokens.");
|
||||
StorageService()->SubTokenDB().CleanExpiredTokens();
|
||||
|
||||
@@ -41,7 +41,6 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
std::string GenerateQRCode(const std::string &Secret, const std::string &email) {
|
||||
|
||||
std::string uri{
|
||||
"otpauth://totp/" + Issuer_ + ":" +
|
||||
email + "?secret=" + Secret + "&issuer=" + Issuer_
|
||||
@@ -55,10 +54,10 @@ namespace OpenWifi {
|
||||
static bool ValidateCode( const std::string &Secret, const std::string &Code, std::string & Expecting) {
|
||||
uint64_t Now = OpenWifi::Now();
|
||||
uint32_t p = CppTotp::totp(CppTotp::Bytes::ByteString{ (const u_char *)Secret.c_str()}, Now, 0, 30, 6);
|
||||
char buffer[16];
|
||||
char buffer[16]{0};
|
||||
sprintf(buffer,"%06u",p);
|
||||
Expecting = buffer;
|
||||
return Code == buffer;
|
||||
Expecting = std::string(buffer);
|
||||
return Code == Expecting;
|
||||
}
|
||||
|
||||
int Start() override {
|
||||
@@ -92,26 +91,13 @@ namespace OpenWifi {
|
||||
auto Secret = GenerateSecret(20, Base32Secret);
|
||||
QRCode = GenerateQRCode(Base32Secret, User.email);
|
||||
|
||||
/*
|
||||
struct Entry {
|
||||
bool Subscriber=false;
|
||||
uint64_t Start = 0;
|
||||
uint64_t Done = 0 ;
|
||||
uint64_t Verifications = 0 ;
|
||||
std::string Secret;
|
||||
std::string QRCode;
|
||||
std::string LastCode;
|
||||
};
|
||||
|
||||
*/
|
||||
|
||||
Entry E{ .Subscriber = Subscriber,
|
||||
.Start = OpenWifi::Now(),
|
||||
.Done = 0,
|
||||
.Verifications = 0,
|
||||
.Secret = Secret,
|
||||
.QRCode = QRCode,
|
||||
.LastCode = 0
|
||||
.LastCode = ""
|
||||
};
|
||||
Cache_[User.id] = E;
|
||||
return true;
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
static const std::string GitUCentralJSONSchemaFile{"https://raw.githubusercontent.com/blogic/ucentral-schema/main/ucentral.schema.json"};
|
||||
static const std::string GitUCentralJSONSchemaFile{
|
||||
"https://raw.githubusercontent.com/blogic/ucentral-schema/main/ucentral.schema.json"};
|
||||
|
||||
static json DefaultUCentralSchema = R"(
|
||||
static json DefaultUCentralSchema = R"(
|
||||
|
||||
{
|
||||
"$id": "https://openwrt.org/ucentral.schema.json",
|
||||
@@ -518,7 +519,7 @@ namespace OpenWifi {
|
||||
"maximum": 4050
|
||||
},
|
||||
"proto": {
|
||||
"decription": "The L2 vlan tag that shall be added (1q,1ad) ",
|
||||
"decription": "The L2 vlan tag that shall be added (1q,1ad ) ",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"802.1ad",
|
||||
@@ -669,6 +670,47 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
},
|
||||
"interface.ipv4.port-forward": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tcp",
|
||||
"udp",
|
||||
"any"
|
||||
],
|
||||
"default": "any"
|
||||
},
|
||||
"external-port": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
},
|
||||
"internal-address": {
|
||||
"type": "string",
|
||||
"format": "ipv4",
|
||||
"example": "0.0.0.120"
|
||||
},
|
||||
"internal-port": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"external-port",
|
||||
"internal-address"
|
||||
]
|
||||
},
|
||||
"interface.ipv4": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -722,6 +764,12 @@ namespace OpenWifi {
|
||||
"items": {
|
||||
"$ref": "#/$defs/interface.ipv4.dhcp-lease"
|
||||
}
|
||||
},
|
||||
"port-forward": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/interface.ipv4.port-forward"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -751,6 +799,96 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
},
|
||||
"interface.ipv6.port-forward": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"tcp",
|
||||
"udp",
|
||||
"any"
|
||||
],
|
||||
"default": "any"
|
||||
},
|
||||
"external-port": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
},
|
||||
"internal-address": {
|
||||
"type": "string",
|
||||
"format": "ipv6",
|
||||
"example": "::1234:abcd"
|
||||
},
|
||||
"internal-port": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"external-port",
|
||||
"internal-address"
|
||||
]
|
||||
},
|
||||
"interface.ipv6.traffic-allow": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"default": "any"
|
||||
},
|
||||
"source-address": {
|
||||
"type": "string",
|
||||
"format": "uc-cidr6",
|
||||
"example": "2001:db8:1234:abcd::/64",
|
||||
"default": "::/0"
|
||||
},
|
||||
"source-ports": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
}
|
||||
},
|
||||
"destination-address": {
|
||||
"type": "string",
|
||||
"format": "ipv6",
|
||||
"example": "::1000"
|
||||
},
|
||||
"destination-ports": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": [
|
||||
"integer",
|
||||
"string"
|
||||
],
|
||||
"minimum": 0,
|
||||
"maximum": 65535,
|
||||
"format": "uc-portrange"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"destination-address"
|
||||
]
|
||||
},
|
||||
"interface.ipv6": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -782,6 +920,18 @@ namespace OpenWifi {
|
||||
},
|
||||
"dhcpv6": {
|
||||
"$ref": "#/$defs/interface.ipv6.dhcpv6"
|
||||
},
|
||||
"port-forward": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/interface.ipv6.port-forward"
|
||||
}
|
||||
},
|
||||
"traffic-allow": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/interface.ipv6.traffic-allow"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -866,7 +1016,7 @@ namespace OpenWifi {
|
||||
},
|
||||
"gateway-fqdn": {
|
||||
"type": "string",
|
||||
"format": "fqdn",
|
||||
"format": "uc-fqdn",
|
||||
"default": "ucentral.splash"
|
||||
},
|
||||
"max-clients": {
|
||||
@@ -901,6 +1051,7 @@ namespace OpenWifi {
|
||||
"psk",
|
||||
"psk2",
|
||||
"psk-mixed",
|
||||
"psk2-radius",
|
||||
"wpa",
|
||||
"wpa2",
|
||||
"wpa-mixed",
|
||||
@@ -961,6 +1112,10 @@ namespace OpenWifi {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"reduced-neighbor-reporting": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"lci": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1527,6 +1682,11 @@ namespace OpenWifi {
|
||||
"decription": "This option allows embedding custom vendor specific IEs inside the beacons of a BSS in AP mode.",
|
||||
"type": "string"
|
||||
},
|
||||
"fils-discovery-interval": {
|
||||
"type": "integer",
|
||||
"default": 20,
|
||||
"maximum": 10000
|
||||
},
|
||||
"encryption": {
|
||||
"$ref": "#/$defs/interface.ssid.encryption"
|
||||
},
|
||||
@@ -2087,6 +2247,10 @@ namespace OpenWifi {
|
||||
"auto-channel": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"ipv6": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -2193,7 +2357,7 @@ namespace OpenWifi {
|
||||
"properties": {
|
||||
"fqdn": {
|
||||
"type": "string",
|
||||
"format": "fqdn"
|
||||
"format": "uc-fqdn"
|
||||
},
|
||||
"suffix-matching": {
|
||||
"type": "boolean",
|
||||
@@ -2444,8 +2608,7 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
)"_json;
|
||||
)"_json;
|
||||
|
||||
class custom_error_handler : public nlohmann::json_schema::basic_error_handler
|
||||
{
|
||||
@@ -2460,9 +2623,18 @@ namespace OpenWifi {
|
||||
void ConfigurationValidator::Init() {
|
||||
if(Initialized_)
|
||||
return;
|
||||
|
||||
std::string GitSchema;
|
||||
if(MicroService::instance().ConfigGetBool("ucentral.datamodel.internal",true)) {
|
||||
RootSchema_ = DefaultUCentralSchema;
|
||||
Logger().information("Using uCentral validation from built-in default.");
|
||||
Initialized_ = Working_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if(Utils::wgets(GitUCentralJSONSchemaFile, GitSchema)) {
|
||||
auto GitURI = MicroService::instance().ConfigGetString("ucentral.datamodel.uri",GitUCentralJSONSchemaFile);
|
||||
if(Utils::wgets(GitURI, GitSchema)) {
|
||||
RootSchema_ = json::parse(GitSchema);
|
||||
Logger().information("Using uCentral validation schema from GIT.");
|
||||
} else {
|
||||
@@ -2528,6 +2700,17 @@ namespace OpenWifi {
|
||||
return IsCIDRv4(value) || IsCIDRv6(value);
|
||||
}
|
||||
|
||||
static inline bool IsPortRangeIsValid(const std::string &r) {
|
||||
const auto ports = Poco::StringTokenizer("-",r,Poco::StringTokenizer::TOK_TRIM);
|
||||
|
||||
for(const auto &port:ports) {
|
||||
uint32_t port_num = std::stoul(port);
|
||||
if(port_num==0 || port_num>65535)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ConfigurationValidator::my_format_checker(const std::string &format, const std::string &value)
|
||||
{
|
||||
static const std::regex host_regex{"^(?=.{1,254}$)((?=[a-z0-9-]{1,63}\\.)(xn--+)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}$"};
|
||||
@@ -2578,6 +2761,14 @@ namespace OpenWifi {
|
||||
} catch (...) {
|
||||
}
|
||||
throw std::invalid_argument(value + " is not a valid URI: should be something like https://hello.world.com.");
|
||||
} else if(format == "uc-portrange") {
|
||||
try {
|
||||
if(IsPortRangeIsValid(value))
|
||||
return;
|
||||
throw std::invalid_argument(value + " is not a valid port range: should an integer between 1-65535 or a port range like post-port.");
|
||||
} catch (...) {
|
||||
}
|
||||
throw std::invalid_argument(value + " is not a valid port range: should an integer between 1-65535 or a port range like post-port.");
|
||||
} else if(format == "ip") {
|
||||
if (IsIP(value))
|
||||
return;
|
||||
|
||||
@@ -27,6 +27,11 @@ namespace OpenWifi {
|
||||
inline uint64_t Now() { return std::time(nullptr); };
|
||||
}
|
||||
|
||||
namespace OpenWifi::Utils {
|
||||
std::vector<unsigned char> base64decode(const std::string& input);
|
||||
std::string base64encode(const unsigned char *input, uint32_t size);
|
||||
}
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
@@ -238,6 +243,11 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
Obj.set(Field,Value);
|
||||
}
|
||||
|
||||
inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Poco::Data::BLOB &Value) {
|
||||
auto Result = Utils::base64encode((const unsigned char *)Value.rawContent(),Value.size());
|
||||
Obj.set(Field,Result);
|
||||
}
|
||||
|
||||
inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) {
|
||||
Poco::JSON::Array Array;
|
||||
for(const auto &i:S) {
|
||||
@@ -334,12 +344,12 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, double & Value) {
|
||||
if(Obj->has(Field) && !Obj->isNull(Field))
|
||||
Value = (double) Obj->get(Field);
|
||||
Value = (double)Obj->get(Field);
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, float & Value) {
|
||||
if(Obj->has(Field) && !Obj->isNull(Field))
|
||||
Value = (float) Obj->get(Field);
|
||||
Value = (float)Obj->get(Field);
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, bool &Value) {
|
||||
@@ -374,7 +384,14 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint64_t &Value) {
|
||||
if(Obj->has(Field) && !Obj->isNull(Field))
|
||||
Value = (uint64_t ) Obj->get(Field);
|
||||
Value = (uint64_t)Obj->get(Field);
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Poco::Data::BLOB &Value) {
|
||||
if(Obj->has(Field) && !Obj->isNull(Field)) {
|
||||
auto Result = Utils::base64decode(Obj->get(Field).toString());
|
||||
Value.assignRaw((const unsigned char *)&Result[0],Result.size());
|
||||
}
|
||||
}
|
||||
|
||||
inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) {
|
||||
@@ -643,6 +660,27 @@ namespace OpenWifi::RESTAPI_utils {
|
||||
|
||||
namespace OpenWifi::Utils {
|
||||
|
||||
inline void SetThreadName(const char *name) {
|
||||
#ifdef __linux__
|
||||
Poco::Thread::current()->setName(name);
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
Poco::Thread::current()->setName(name);
|
||||
pthread_setname_np(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void SetThreadName(Poco::Thread &thr, const char *name) {
|
||||
#ifdef __linux__
|
||||
thr.setName(name);
|
||||
pthread_setname_np(thr.tid(), name);
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
thr.setName(name);
|
||||
#endif
|
||||
}
|
||||
|
||||
enum MediaTypeEncodings {
|
||||
PLAIN,
|
||||
BINARY,
|
||||
@@ -665,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;
|
||||
|
||||
@@ -1167,6 +1218,7 @@ namespace OpenWifi {
|
||||
static const std::string uSERVICE_SUBCRIBER{ "owsub"};
|
||||
static const std::string uSERVICE_INSTALLER{ "owinst"};
|
||||
static const std::string uSERVICE_ANALYTICS{ "owanalytics"};
|
||||
static const std::string uSERVICE_OWRRM{ "owrrm"};
|
||||
|
||||
class ConfigurationEntry {
|
||||
public:
|
||||
@@ -1315,7 +1367,7 @@ namespace OpenWifi {
|
||||
inline void Start();
|
||||
inline void Stop();
|
||||
private:
|
||||
std::atomic_bool Running_ = false;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
Poco::Thread Thread_;
|
||||
};
|
||||
|
||||
@@ -1360,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;
|
||||
@@ -1846,7 +1899,8 @@ namespace OpenWifi {
|
||||
Request = &RequestIn;
|
||||
Response = &ResponseIn;
|
||||
|
||||
Poco::Thread::current()->setName("WebServerThread_" + std::to_string(TransactionId_));
|
||||
// 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) {
|
||||
@@ -1895,36 +1949,32 @@ namespace OpenWifi {
|
||||
[[nodiscard]] inline bool NeedAdditionalInfo() const { return QB_.AdditionalInfo; }
|
||||
[[nodiscard]] inline const std::vector<std::string> & SelectedRecords() const { return QB_.Select; }
|
||||
|
||||
/* [[nodiscard]] inline const Poco::JSON::Object::Ptr ParseStream() {
|
||||
return IncomingParser_.parse(Request->stream()).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
*/
|
||||
inline static bool ParseBindings(const std::string & Request, const std::list<std::string> & EndPoints, BindingMap &bindings) {
|
||||
bindings.clear();
|
||||
auto PathItems = Poco::StringTokenizer(Request, "/");
|
||||
|
||||
inline static bool ParseBindings(const std::string & Request, const std::list<std::string> & EndPoints, BindingMap &bindings) {
|
||||
bindings.clear();
|
||||
std::vector<std::string> PathItems = Utils::Split(Request, '/');
|
||||
for(const auto &EndPoint:EndPoints) {
|
||||
auto ParamItems = Poco::StringTokenizer(EndPoint, "/");
|
||||
if (PathItems.count() != ParamItems.count())
|
||||
continue;
|
||||
|
||||
for(const auto &EndPoint:EndPoints) {
|
||||
std::vector<std::string> ParamItems = Utils::Split(EndPoint, '/');
|
||||
if (PathItems.size() != ParamItems.size())
|
||||
continue;
|
||||
|
||||
bool Matched = true;
|
||||
for (size_t i = 0; i != PathItems.size() && Matched; i++) {
|
||||
if (PathItems[i] != ParamItems[i]) {
|
||||
if (ParamItems[i][0] == '{') {
|
||||
auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2);
|
||||
bindings[Poco::toLower(ParamName)] = PathItems[i];
|
||||
} else {
|
||||
Matched = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Matched)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool Matched = true;
|
||||
for (size_t i = 0; i < PathItems.count(); i++) {
|
||||
if (PathItems[i] != ParamItems[i]) {
|
||||
if (ParamItems[i][0] == '{') {
|
||||
auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2);
|
||||
bindings[Poco::toLower(ParamName)] = PathItems[i];
|
||||
} else {
|
||||
Matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Matched)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void PrintBindings() {
|
||||
for (const auto &[key, value] : Bindings_)
|
||||
@@ -2045,6 +2095,17 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) {
|
||||
if(O->has(Field)) {
|
||||
std::string Content = O->get(Field).toString();
|
||||
auto DecodedBlob = Utils::base64decode(Content);
|
||||
Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T> bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) {
|
||||
if(O->has(Field)) {
|
||||
assignee = value;
|
||||
@@ -2385,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_;
|
||||
@@ -2401,6 +2463,7 @@ namespace OpenWifi {
|
||||
RateLimit MyRates_;
|
||||
uint64_t TransactionId_;
|
||||
Poco::JSON::Object::Ptr ParsedBody_;
|
||||
std::string REST_Requester_;
|
||||
};
|
||||
|
||||
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
|
||||
@@ -2582,7 +2645,7 @@ namespace OpenWifi {
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
std::atomic_bool Running_=false;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
Poco::NotificationQueue Queue_;
|
||||
};
|
||||
|
||||
@@ -2608,7 +2671,7 @@ namespace OpenWifi {
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
std::atomic_bool Running_=false;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
};
|
||||
|
||||
class KafkaDispatcher : public Poco::Runnable {
|
||||
@@ -2665,6 +2728,7 @@ namespace OpenWifi {
|
||||
|
||||
inline void run() override {
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
Utils::SetThreadName("kafka:dispatch");
|
||||
while(Note && Running_) {
|
||||
auto Msg = dynamic_cast<KafkaMessage*>(Note.get());
|
||||
if(Msg!= nullptr) {
|
||||
@@ -2687,12 +2751,12 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Types::NotifyTable Notifiers_;
|
||||
Poco::Thread Worker_;
|
||||
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 {
|
||||
@@ -2885,6 +2949,7 @@ namespace OpenWifi {
|
||||
|
||||
void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override
|
||||
{
|
||||
Utils::SetThreadName("alb-request");
|
||||
try {
|
||||
if((id_ % 100) == 0) {
|
||||
Logger_.debug(fmt::format("ALB-REQUEST({}): ALB Request {}.",
|
||||
@@ -2953,7 +3018,7 @@ namespace OpenWifi {
|
||||
std::unique_ptr<Poco::Net::HTTPServer> Server_;
|
||||
std::unique_ptr<Poco::Net::ServerSocket> Socket_;
|
||||
int Port_ = 0;
|
||||
std::atomic_bool Running_=false;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
};
|
||||
|
||||
inline auto ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); }
|
||||
@@ -2973,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();
|
||||
@@ -2981,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;
|
||||
Poco::Thread::current()->setName(fmt::format("RESTAPI_ExtServer_{}",Id));
|
||||
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")
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -3009,7 +3075,7 @@ namespace OpenWifi {
|
||||
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override {
|
||||
try {
|
||||
Poco::URI uri(Request.getURI());
|
||||
Poco::Thread::current()->setName(fmt::format("ExtWebServer_{}",TransactionId_));
|
||||
Utils::SetThreadName(fmt::format("x-rest:{}",TransactionId_).c_str());
|
||||
return RESTAPI_ExtServer()->CallServer(uri.getPath(), TransactionId_++);
|
||||
} catch (...) {
|
||||
|
||||
@@ -3107,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();
|
||||
@@ -3118,17 +3184,18 @@ namespace OpenWifi {
|
||||
|
||||
inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) {
|
||||
RESTAPIHandler::BindingMap Bindings;
|
||||
Poco::Thread::current()->setName(fmt::format("RESTAPI_IntServer_{}",Id));
|
||||
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")
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -3139,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_);
|
||||
}
|
||||
@@ -3182,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_; }
|
||||
@@ -3215,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();
|
||||
@@ -3251,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();
|
||||
@@ -3281,6 +3352,9 @@ namespace OpenWifi {
|
||||
return Signer_.sign(T,Algo);
|
||||
}
|
||||
}
|
||||
|
||||
inline Poco::ThreadPool & TimerPool() { return TimerPool_; }
|
||||
|
||||
private:
|
||||
static MicroService * instance_;
|
||||
bool HelpRequested_ = false;
|
||||
@@ -3294,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_;
|
||||
@@ -3315,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) {
|
||||
@@ -3464,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();
|
||||
@@ -3527,7 +3601,7 @@ namespace OpenWifi {
|
||||
void DaemonPostInitialization(Poco::Util::Application &self);
|
||||
|
||||
inline void MicroService::initialize(Poco::Util::Application &self) {
|
||||
// add the default services
|
||||
// add the default services
|
||||
LoadConfigurationFile();
|
||||
InitializeLoggingSystem();
|
||||
|
||||
@@ -3801,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);
|
||||
@@ -3868,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()) {
|
||||
@@ -3904,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()) {
|
||||
@@ -3921,7 +3992,6 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline int MicroService::main([[maybe_unused]] const ArgVec &args) {
|
||||
|
||||
MyErrorHandler ErrorHandler(*this);
|
||||
Poco::ErrorHandler::set(&ErrorHandler);
|
||||
|
||||
@@ -4028,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();
|
||||
}
|
||||
@@ -4037,6 +4108,7 @@ namespace OpenWifi {
|
||||
|
||||
inline void BusEventManager::run() {
|
||||
Running_ = true;
|
||||
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_) {
|
||||
@@ -4122,6 +4194,8 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline void KafkaProducer::run() {
|
||||
|
||||
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") }
|
||||
@@ -4160,6 +4234,8 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
inline void KafkaConsumer::run() {
|
||||
Utils::SetThreadName("Kafka:Cons");
|
||||
|
||||
cppkafka::Configuration Config({
|
||||
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
|
||||
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") },
|
||||
@@ -4298,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);
|
||||
}
|
||||
|
||||
@@ -4400,7 +4481,7 @@ namespace OpenWifi {
|
||||
Path,
|
||||
Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
|
||||
Poco::Logger::get("REST-CALLER-GET").debug(fmt::format(" {}", URI.toString()));
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-GET"),fmt::format(" {}", URI.toString()));
|
||||
|
||||
if(BearerToken.empty()) {
|
||||
Request.add("X-API-KEY", Svc.AccessKey);
|
||||
@@ -4458,7 +4539,7 @@ namespace OpenWifi {
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
Poco::Logger::get("REST-CALLER-PUT").debug(fmt::format("{}", URI.toString()));
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-PUT"),fmt::format("{}", URI.toString()));
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
@@ -4537,7 +4618,7 @@ namespace OpenWifi {
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
Poco::Logger::get("REST-CALLER-POST").debug(fmt::format(" {}", URI.toString()));
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-POST"),fmt::format(" {}", URI.toString()));
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
@@ -4613,7 +4694,7 @@ namespace OpenWifi {
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
Poco::Logger::get("REST-CALLER-DELETE").debug(fmt::format(" {}", URI.toString()));
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-DELETE"),fmt::format(" {}", URI.toString()));
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
@@ -4670,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={}",
|
||||
@@ -4678,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,
|
||||
@@ -4701,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,
|
||||
@@ -4818,8 +4902,8 @@ namespace OpenWifi {
|
||||
[[nodiscard]] bool SendToUser(const std::string &userName, const std::string &Payload);
|
||||
void SendToAll(const std::string &Payload);
|
||||
private:
|
||||
std::atomic_bool Running_ = false;
|
||||
Poco::Thread Thr_;
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
Poco::Thread Thr_;
|
||||
// std::unique_ptr<MyParallelSocketReactor> ReactorPool_;
|
||||
Poco::Net::SocketReactor Reactor_;
|
||||
Poco::Thread ReactorThread_;
|
||||
@@ -4909,12 +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:uiclnt-svr");
|
||||
while(Running_) {
|
||||
Poco::Thread::trySleep(2000);
|
||||
|
||||
@@ -4962,8 +5047,12 @@ namespace OpenWifi {
|
||||
|
||||
for(const auto &client:Clients_) {
|
||||
if(client.second.second == UserName) {
|
||||
if(client.second.first->Send(Payload))
|
||||
Sent++;
|
||||
try {
|
||||
if (client.second.first->Send(Payload))
|
||||
Sent++;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Sent>0;
|
||||
@@ -4985,70 +5074,70 @@ namespace OpenWifi {
|
||||
int flags;
|
||||
int n;
|
||||
bool Done=false;
|
||||
Poco::Buffer<char> IncomingFrame(0);
|
||||
n = WS_->receiveFrame(IncomingFrame, flags);
|
||||
auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
|
||||
try {
|
||||
Poco::Buffer<char> IncomingFrame(0);
|
||||
n = WS_->receiveFrame(IncomingFrame, flags);
|
||||
auto Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
|
||||
|
||||
if(n==0) {
|
||||
return delete this;
|
||||
}
|
||||
if (n == 0) {
|
||||
return delete this;
|
||||
}
|
||||
|
||||
switch(Op) {
|
||||
case Poco::Net::WebSocket::FRAME_OP_PING: {
|
||||
WS_->sendFrame("", 0,
|
||||
(int)Poco::Net::WebSocket::FRAME_OP_PONG |
|
||||
(int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
|
||||
}
|
||||
break;
|
||||
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_));
|
||||
Done=true;
|
||||
}
|
||||
break;
|
||||
case Poco::Net::WebSocket::FRAME_OP_TEXT: {
|
||||
IncomingFrame.append(0);
|
||||
if(!Authenticated_) {
|
||||
std::string Frame{IncomingFrame.begin()};
|
||||
auto Tokens = Utils::Split(Frame,':');
|
||||
bool Expired = false, Contacted = false;
|
||||
if(Tokens.size()==2 && AuthClient()->IsAuthorized(Tokens[1], UserInfo_, Expired, Contacted)) {
|
||||
Authenticated_=true;
|
||||
std::string S{"Welcome! Bienvenue! Bienvenidos!"};
|
||||
WS_->sendFrame(S.c_str(),S.size());
|
||||
WebSocketClientServer()->SetUser(Id_,UserInfo_.userinfo.email);
|
||||
} else {
|
||||
std::string S{"Invalid token. Closing connection."};
|
||||
WS_->sendFrame(S.c_str(),S.size());
|
||||
Done=true;
|
||||
}
|
||||
switch (Op) {
|
||||
case Poco::Net::WebSocket::FRAME_OP_PING: {
|
||||
WS_->sendFrame("", 0,
|
||||
(int)Poco::Net::WebSocket::FRAME_OP_PONG |
|
||||
(int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
|
||||
} break;
|
||||
case Poco::Net::WebSocket::FRAME_OP_PONG: {
|
||||
} break;
|
||||
case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
|
||||
Logger().warning(Poco::format("CLOSE(%s): UI Client is closing its connection.", Id_));
|
||||
Done = true;
|
||||
} break;
|
||||
case Poco::Net::WebSocket::FRAME_OP_TEXT: {
|
||||
IncomingFrame.append(0);
|
||||
if (!Authenticated_) {
|
||||
std::string Frame{IncomingFrame.begin()};
|
||||
auto Tokens = Utils::Split(Frame, ':');
|
||||
bool Expired = false, Contacted = false;
|
||||
if (Tokens.size() == 2 &&
|
||||
AuthClient()->IsAuthorized(Tokens[1], UserInfo_, Expired, Contacted)) {
|
||||
Authenticated_ = true;
|
||||
std::string S{"Welcome! Bienvenue! Bienvenidos!"};
|
||||
WS_->sendFrame(S.c_str(), S.size());
|
||||
WebSocketClientServer()->SetUser(Id_, UserInfo_.userinfo.email);
|
||||
} else {
|
||||
std::string S{"Invalid token. Closing connection."};
|
||||
WS_->sendFrame(S.c_str(), S.size());
|
||||
Done = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
try {
|
||||
Poco::JSON::Parser P;
|
||||
auto Obj = P.parse(IncomingFrame.begin())
|
||||
.extract<Poco::JSON::Object::Ptr>();
|
||||
std::string Answer;
|
||||
if(Processor_!= nullptr)
|
||||
Processor_->Processor(Obj, Answer, Done);
|
||||
if (!Answer.empty())
|
||||
WS_->sendFrame(Answer.c_str(), (int) Answer.size());
|
||||
else {
|
||||
WS_->sendFrame("{}", 2);
|
||||
}
|
||||
} catch (const Poco::JSON::JSONException & E) {
|
||||
Logger().log(E);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Poco::JSON::Parser P;
|
||||
auto Obj =
|
||||
P.parse(IncomingFrame.begin()).extract<Poco::JSON::Object::Ptr>();
|
||||
std::string Answer;
|
||||
if (Processor_ != nullptr)
|
||||
Processor_->Processor(Obj, Answer, Done);
|
||||
if (!Answer.empty())
|
||||
WS_->sendFrame(Answer.c_str(), (int)Answer.size());
|
||||
else {
|
||||
WS_->sendFrame("{}", 2);
|
||||
}
|
||||
} catch (const Poco::JSON::JSONException &E) {
|
||||
Logger().log(E);
|
||||
Done=true;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
Done=true;
|
||||
}
|
||||
|
||||
if(Done) {
|
||||
delete this;
|
||||
@@ -5142,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);
|
||||
}
|
||||
|
||||
@@ -133,6 +133,37 @@ namespace ORM {
|
||||
return R;
|
||||
}
|
||||
|
||||
inline std::string WHERE_AND_(std::string Result) {
|
||||
return Result;
|
||||
}
|
||||
|
||||
template <typename T, typename... Args> std::string WHERE_AND_(std::string Result, const char *fieldName, const T &Value, Args... args) {
|
||||
if constexpr(std::is_same_v<T,std::string>)
|
||||
{
|
||||
if(!Value.empty()) {
|
||||
if(!Result.empty())
|
||||
Result += " and ";
|
||||
Result += fieldName;
|
||||
Result += '=';
|
||||
Result += "'";
|
||||
Result += Escape(Value);
|
||||
Result += "'";
|
||||
}
|
||||
} else {
|
||||
if(!Result.empty())
|
||||
Result += " and ";
|
||||
Result += fieldName ;
|
||||
Result += '=';
|
||||
Result += std::to_string(Value);
|
||||
}
|
||||
return WHERE_AND_(Result,args...);
|
||||
}
|
||||
|
||||
template <typename... Args> std::string WHERE_AND(Args... args) {
|
||||
std::string Result;
|
||||
return WHERE_AND_(Result, args...);
|
||||
}
|
||||
|
||||
enum SqlComparison { EQ = 0, NEQ, LT, LTE, GT, GTE };
|
||||
enum SqlBinaryOp { AND = 0 , OR };
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include "Poco/String.h"
|
||||
|
||||
#if defined(__GNUC__)
|
||||
@@ -428,6 +429,7 @@ namespace OpenWifi::uCentralProtocol {
|
||||
static const char *RADIUSACCT = "acct";
|
||||
static const char *RADIUSAUTH = "auth";
|
||||
static const char *RADIUSDST = "dst";
|
||||
static const char *IES = "ies";
|
||||
}
|
||||
|
||||
namespace OpenWifi::uCentralProtocol::Events {
|
||||
@@ -457,25 +459,28 @@ namespace OpenWifi::uCentralProtocol::Events {
|
||||
ET_TELEMETRY
|
||||
};
|
||||
|
||||
inline static EVENT_MSG EventFromString(const std::string & Method) {
|
||||
static std::vector<std::pair<const char *,EVENT_MSG>> Values{
|
||||
{ CFGPENDING , ET_CFGPENDING },
|
||||
{ CONNECT, ET_CONNECT },
|
||||
{ CRASHLOG, ET_CRASHLOG },
|
||||
{ DEVICEUPDATE, ET_DEVICEUPDATE },
|
||||
{ HEALTHCHECK, ET_HEALTHCHECK },
|
||||
{ LOG, ET_LOG },
|
||||
{ PING, ET_PING },
|
||||
{ RECOVERY, ET_RECOVERY },
|
||||
{ STATE, ET_STATE },
|
||||
{ TELEMETRY, ET_TELEMETRY }
|
||||
};
|
||||
|
||||
std::string L = Poco::toLower(Method);
|
||||
auto hint = std::find_if(cbegin(Values),cend(Values),[&](const std::pair<const char *,EVENT_MSG> &v) ->bool { return strcmp(v.first,L.c_str())==0; });
|
||||
if(hint == cend(Values))
|
||||
return ET_UNKNOWN;
|
||||
return hint->second;
|
||||
inline EVENT_MSG EventFromString(const std::string & Method) {
|
||||
if(strcmp(STATE,Method.c_str())==0)
|
||||
return ET_STATE;
|
||||
else if(strcmp(HEALTHCHECK,Method.c_str())==0)
|
||||
return ET_HEALTHCHECK;
|
||||
else if(strcmp(CONNECT,Method.c_str())==0)
|
||||
return ET_CONNECT;
|
||||
else if(strcmp(CFGPENDING,Method.c_str())==0)
|
||||
return ET_CFGPENDING;
|
||||
else if(strcmp(CRASHLOG,Method.c_str())==0)
|
||||
return ET_CRASHLOG;
|
||||
else if(strcmp(DEVICEUPDATE,Method.c_str())==0)
|
||||
return ET_DEVICEUPDATE;
|
||||
else if(strcmp(LOG,Method.c_str())==0)
|
||||
return ET_LOG;
|
||||
else if(strcmp(PING,Method.c_str())==0)
|
||||
return ET_PING;
|
||||
else if(strcmp(RECOVERY,Method.c_str())==0)
|
||||
return ET_RECOVERY;
|
||||
else if(strcmp(TELEMETRY,Method.c_str())==0)
|
||||
return ET_TELEMETRY;
|
||||
return ET_UNKNOWN;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
14
templates/email_invitation.html
Normal file
14
templates/email_invitation.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>eMail Invitation</title>
|
||||
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
7
templates/email_invitation.txt
Normal file
7
templates/email_invitation.txt
Normal 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!
|
||||
@@ -2,7 +2,10 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Title</title>
|
||||
<title>eMail Verification</title>
|
||||
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -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
441
templates/sample.html
Normal 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> </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> </td>
|
||||
</tr>
|
||||
</table>
|
||||
<img src="https://mandrillapp.com/track/open.php?u=31039844&id=6424f96bfb88484685c7229756fee3fb" height="1" width="1"></body>
|
||||
</html>
|
||||
0
templates/sub_email_verification.html
Normal file
0
templates/sub_email_verification.html
Normal file
0
templates/sub_email_verification.txt
Normal file
0
templates/sub_email_verification.txt
Normal file
0
templates/sub_password_reset.html
Normal file
0
templates/sub_password_reset.html
Normal file
0
templates/sub_password_reset.txt
Normal file
0
templates/sub_password_reset.txt
Normal file
0
templates/sub_verification_code.html
Normal file
0
templates/sub_verification_code.html
Normal file
0
templates/sub_verification_code.txt
Normal file
0
templates/sub_verification_code.txt
Normal file
0
templates/verification_code.html
Normal file
0
templates/verification_code.html
Normal file
@@ -1 +0,0 @@
|
||||
null
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
0
wwwassets/invitation_error.html
Normal file
0
wwwassets/invitation_error.html
Normal file
0
wwwassets/invitation_success.html
Normal file
0
wwwassets/invitation_success.html
Normal file
BIN
wwwassets/logo.png
Normal file
BIN
wwwassets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
0
wwwassets/sub_404_error.css
Normal file
0
wwwassets/sub_404_error.css
Normal file
0
wwwassets/sub_404_error.html
Normal file
0
wwwassets/sub_404_error.html
Normal file
0
wwwassets/sub_access_policy.html
Normal file
0
wwwassets/sub_access_policy.html
Normal file
0
wwwassets/sub_email_verification_error.html
Normal file
0
wwwassets/sub_email_verification_error.html
Normal file
0
wwwassets/sub_email_verification_success.html
Normal file
0
wwwassets/sub_email_verification_success.html
Normal file
BIN
wwwassets/sub_favicon.ico
Normal 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
BIN
wwwassets/sub_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
0
wwwassets/sub_password_policy.html
Normal file
0
wwwassets/sub_password_policy.html
Normal file
0
wwwassets/sub_password_reset.html
Normal file
0
wwwassets/sub_password_reset.html
Normal file
0
wwwassets/sub_password_reset_error.html
Normal file
0
wwwassets/sub_password_reset_error.html
Normal file
0
wwwassets/sub_password_reset_success.html
Normal file
0
wwwassets/sub_password_reset_success.html
Normal file
Reference in New Issue
Block a user