Compare commits

...

72 Commits

Author SHA1 Message Date
Dmitry Dunaev
95ea70d5c2 [WIFI-10236] Chg: helm image to the working one
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-07-22 14:57:49 +03:00
TIP Automation User
8b40de4631 Chg: update image tag in helm values to v2.4.2 2022-07-21 15:08:53 +00:00
Dmitry Dunaev
b1740575a5 [WIFI-1998] Add: gracefull ingress deprecationush
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-07-21 17:42:49 +03:00
TIP Automation User
02057624de Chg: update image tag in helm values to v2.4.1 2022-07-21 13:50:28 +00:00
Dmitry Dunaev
2e394d0513 [WIFI-9174] Fix: switch from deprecated bitnami charts to mirrored ones
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2022-07-21 16:41:24 +03:00
TIP Automation User
24b022fa60 Chg: update image tag in helm values to v2.4.0 2021-12-17 02:36:54 +00:00
Johann Hoffmann
88922786ff [WIFI-6170] Add OpenWifi Docker Compose deployment with PostgreSQL (#30)
* Add wait-for-postgres.sh wrapper script

Signed-off-by: Johann Hoffmann <johann.hoffmann@mailbox.org>

* Copy wait-for-postgres.sh into Docker image

Signed-off-by: Johann Hoffmann <johann.hoffmann@mailbox.org>
2021-12-16 17:30:30 +01:00
TIP Automation User
4510cd034f Chg: update image tag in helm values to v2.4.0-RC5 2021-12-11 08:12:29 +00:00
stephb9959
af5774ce36 Fix for https://telecominfraproject.atlassian.net/browse/WIFI-6149 2021-12-10 10:29:24 -08:00
stephb9959
2573b8cd4f Merge remote-tracking branch 'origin/release/v2.4.0' into release/v2.4.0
# Conflicts:
#	Dockerfile
2021-12-10 10:25:06 -08:00
stephb9959
9c5b18a536 Fix for https://telecominfraproject.atlassian.net/browse/WIFI-6149 2021-12-10 10:23:15 -08:00
TIP Automation User
768c428a67 Chg: update image tag in helm values to v2.4.0-RC4 2021-12-08 07:42:41 +00:00
Dmitry Dunaev
389ceb8b7d Add: .git dir to build image to expose git hash for version
Signed-off-by: Dmitry Dunaev <dmitry@opsfleet.com>
2021-12-08 10:28:53 +03:00
stephb9959
157f18c117 Merge remote-tracking branch 'origin/release/v2.4.0' into release/v2.4.0 2021-12-06 09:13:49 -08:00
stephb9959
2538f9c768 Adding Git Hash to version numbers. No Jira. 2021-12-06 09:13:41 -08:00
TIP Automation User
85d998ad76 Chg: update image tag in helm values to v2.4.0-RC3 2021-12-06 15:58:02 +00:00
stephb9959
a407f2e38d Fix for https://telecominfraproject.atlassian.net/browse/WIFI-6048 2021-12-05 12:10:04 -08:00
Dmitry Dunaev
d0d2be0870 Chg: helm image in values to new release candidate 2021-11-22 14:57:54 +03:00
stephb9959
78cba562e6 Solving https://telecominfraproject.atlassian.net/browse/WIFI-5780 2021-11-19 11:02:29 -08:00
Leonid Mirsky
350df38c3f Update Helm values to v2.4.0-RC1
Signed-off-by: Leonid Mirsky <leonid@opsfleet.com>
2021-11-16 23:34:10 +02:00
stephb9959
9e79b73e20 Adding proper default for mailer and sms. 2021-11-15 15:04:48 -08:00
stephb9959
eb4dfc25f2 Adding proper default for mailer and sms. 2021-11-15 14:54:23 -08:00
stephb9959
bedec254c5 Mail refactoring. 2021-11-15 14:38:56 -08:00
stephb9959
96a566a2b5 Sendmail crash fix. 2021-11-15 13:46:44 -08:00
stephb9959
ad2eb1711e ACL Fix. 2021-11-15 13:34:54 -08:00
stephb9959
7244bcb455 ACL Fix. 2021-11-15 13:22:03 -08:00
stephb9959
1db5201418 ACL Fix. 2021-11-15 13:16:34 -08:00
stephb9959
1bc635f553 ACL Fix. 2021-11-15 12:05:14 -08:00
stephb9959
257ac42d7c ACL Fix. 2021-11-15 11:51:07 -08:00
stephb9959
acb38e5313 New UUIDv4 generator 2021-11-15 11:19:31 -08:00
stephb9959
7940f0bd85 New UUIDv4 generator 2021-11-15 10:56:27 -08:00
stephb9959
62c06d0bad Filtering credentials. 2021-11-15 09:26:46 -08:00
stephb9959
494a199610 WiFi 5617 and ACL Error 2021-11-15 09:16:23 -08:00
stephb9959
5307b0b35a WiFi 5617 and ACL Error 2021-11-15 09:09:17 -08:00
stephb9959
c58728f38e WiFi 5617 and ACL Error 2021-11-15 08:29:08 -08:00
stephb9959
1f09c3b619 WiFi 5617 and ACL Error 2021-11-15 08:24:57 -08:00
stephb9959
d9c6388502 Merge remote-tracking branch 'origin/main' 2021-11-14 14:18:11 -08:00
stephb9959
5e35906aec Fixing shutdown crash 2021-11-14 14:18:02 -08:00
Dmitry Dunaev
773618ae07 [WIFI-5702] Add: README note on owgw-ui password change ability 2021-11-14 22:39:45 +03:00
stephb9959
cca4441ac7 Rate limiting log error. 2021-11-13 21:33:51 -08:00
stephb9959
730ca7b292 Proper error returning for rate limiting. 2021-11-13 17:35:15 -08:00
stephb9959
5b4dbb088f Final token cache/revocation fix. 2021-11-13 17:23:19 -08:00
stephb9959
bc11a19ee4 Fixing token revocation/ 2021-11-13 17:17:49 -08:00
stephb9959
c835e4d0b9 Fixing token revocatiopn/ 2021-11-13 17:12:39 -08:00
stephb9959
f1a2ba90f6 Fixing token revocatiopn/ 2021-11-13 17:06:52 -08:00
stephb9959
5b96ef396f Fixing token revocatiopn/ 2021-11-13 17:01:49 -08:00
stephb9959
c204d34bf4 Fixing token revocatiopn/ 2021-11-13 16:44:51 -08:00
stephb9959
4b982bf64b Fixing token revocatiopn/ 2021-11-13 16:42:20 -08:00
stephb9959
37298cc600 Fixing token revocatiopn/ 2021-11-13 16:38:18 -08:00
stephb9959
03619cc900 Fixing token revocatiopn/ 2021-11-13 16:24:13 -08:00
stephb9959
f4fc6975e1 Fixing token revocatiopn/ 2021-11-13 16:18:42 -08:00
stephb9959
1f1d596c5a Fixing token cache. 2021-11-13 16:03:58 -08:00
stephb9959
a5802bf631 Adding token expiry detection and reporting. 2021-11-13 15:24:24 -08:00
stephb9959
6471eabc82 Unitialized MFA in user record. 2021-11-13 10:24:21 -08:00
stephb9959
ab6fbaca11 Removing expired links and avatars. 2021-11-13 09:05:16 -08:00
stephb9959
1e8e5c18b2 Removing expired tokens periodically. 2021-11-13 08:43:57 -08:00
stephb9959
3cf23af068 Fix the fix...arg 2021-11-13 08:18:13 -08:00
stephb9959
1a0b549731 Fix the fix...arg 2021-11-13 07:19:52 -08:00
stephb9959
a835d2e571 Framework update. 2021-11-12 23:35:43 -08:00
stephb9959
ff7455af24 improving ACL processing. 2021-11-12 22:25:29 -08:00
stephb9959
48610bac5d Hardening SMS code. 2021-11-12 08:59:45 -08:00
stephb9959
7bd5b4d4e6 Fixing MFA UUID issue. 2021-11-12 08:49:03 -08:00
stephb9959
e1a51c2a91 Fixing MFA UUID issue. 2021-11-12 08:37:17 -08:00
stephb9959
cd0222f765 Fixing MFA UUID issue. 2021-11-12 08:21:44 -08:00
stephb9959
12fddd8bc4 Fixing MFA UUID issue. 2021-11-12 08:20:16 -08:00
stephb9959
9095d831db Merge remote-tracking branch 'origin/main' 2021-11-12 08:14:23 -08:00
stephb9959
4e8f97df9b Fixing logo problem in email 2021-11-12 08:14:15 -08:00
Dmitry Dunaev
28808eae93 Merge pull request #25 from Telecominfraproject/feature/wifi-5702--add-change-password-cli-command-on-startup
[WIFI-5702] Add: README note on changing default password
2021-11-12 19:12:47 +03:00
stephb9959
6c24a23863 Fixing logo problem in email 2021-11-12 08:10:32 -08:00
stephb9959
5931c91054 Fixing logo problem in email 2021-11-12 08:06:21 -08:00
stephb9959
9d956c13f7 Fixing logo problem in email 2021-11-12 07:53:49 -08:00
Dmitry Dunaev
ea1adde361 [WIFI-5702] Add: README note on changing default password 2021-11-12 14:40:56 +03:00
44 changed files with 3307 additions and 439 deletions

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ _deps
*.csr
/cmake-build/
/smake-build-debug/
test_scripts/curl/result.json

View File

@@ -30,9 +30,20 @@ else()
file(WRITE build ${BUILD_NUM})
endif()
set(BUILD_SHARED_LIBS 1)
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_RESULT
OUTPUT_VARIABLE GIT_HASH)
if(NOT GIT_RESULT EQUAL "0")
message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}")
endif()
string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
endif()
add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT)
add_definitions(-DAPP_VERSION="${CMAKE_PROJECT_VERSION}" -DBUILD_NUMBER="${BUILD_NUM}")
set(BUILD_SHARED_LIBS 1)
add_definitions(-DTIP_SECURITY_SERVICE="1")
set(Boost_USE_STATIC_LIBS OFF)
@@ -50,8 +61,11 @@ find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataS
include_directories(/usr/local/include /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include)
configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY)
add_executable( owsec
build
src/ow_version.h.in
src/framework/CountryCodes.h
src/framework/KafkaTopics.h
src/framework/MicroService.h
@@ -90,7 +104,7 @@ add_executable( owsec
src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h
src/storage/storage_tokens.h
src/ActionLinkManager.cpp src/ActionLinkManager.h
)
src/ACLProcessor.h)
if(NOT SMALL_BUILD)
target_link_libraries(owsec PUBLIC

View File

@@ -53,9 +53,12 @@ RUN cmake ..
RUN make
RUN make install
ADD CMakeLists.txt build /owsec/
ADD cmake /owsec/cmake
ADD src /owsec/src
ADD .git /owsec/.git
WORKDIR /owsec
RUN mkdir cmake-build
@@ -75,7 +78,7 @@ RUN addgroup -S "$OWSEC_USER" && \
RUN mkdir /openwifi
RUN mkdir -p "$OWSEC_ROOT" "$OWSEC_CONFIG" && \
chown "$OWSEC_USER": "$OWSEC_ROOT" "$OWSEC_CONFIG"
RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates libcurl curl-dev bash jq curl
RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates libcurl curl-dev bash jq curl postgresql-client
COPY --from=builder /owsec/cmake-build/owsec /openwifi/owsec
COPY --from=builder /cppkafka/cmake-build/src/lib/* /lib/
COPY --from=builder /poco/cmake-build/lib/* /lib/
@@ -87,10 +90,12 @@ COPY owsec.properties.tmpl /
COPY wwwassets /dist/wwwassets
COPY templates /dist/templates
COPY docker-entrypoint.sh /
COPY wait-for-postgres.sh /
RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
COPY readiness_check /readiness_check
COPY test_scripts/curl/cli /cli
EXPOSE 16001 17001 16101

View File

@@ -98,6 +98,40 @@ to get a sample. The default is
### `authentication.oldpasswords`
The number of older passwords to keep. Default is 5.
### Changing default password
On the first startup of the service new user will be created with the default credentials from properties `authentication.default.username` and `authentication.default.password`, but **you will have to change the password** before making any real requests.
You can this using [owgw-ui](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui/) on first login or using the following script:
```
export OWSEC=openwifi.wlan.local:16001 # endpoint to your owsec RESTAPI endpoint
#export FLAGS="-k" # uncomment and add curl flags that you would like to pass for the request (for example '-k' may be used to pass errors with self-signed certificates)
export OWSEC_DEFAULT_USERNAME=root@system.com # default username that you've set in property 'authentication.default.username'
export OWSEC_DEFAULT_PASSWORD=weLoveWifi # default password __in cleartext__ from property 'authentication.default.password'
export OWSEC_NEW_PASSWORD=NewPass123% # new password that must be set for the user (must comply with 'authentication.validation.expression')
test_scripts/curl/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD
```
CLI is also included in Docker image if you want to run it this way:
```
export OWSEC=openwifi.wlan.local:16001
#export FLAGS="-k"
export OWSEC_DEFAULT_USERNAME=root@system.com
export OWSEC_DEFAULT_PASSWORD=weLoveWifi
export OWSEC_NEW_PASSWORD=NewPass123%
docker run --rm -ti \
--network=host \
--env OWSEC \
--env FLAGS \
--env OWSEC_DEFAULT_USERNAME \
--env OWSEC_DEFAULT_PASSWORD \
--env OWSEC_NEW_PASSWORD \
tip-tip-wlan-cloud-ucentral.jfrog.io/owsec:main \
/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD
```
### Kafka integration
This security service uses Kafka to coordinate security with other services that are part of the system. You must have a Kafka service running
in order to use this. You can find several examples of Kafka services available with Docker. Here are the values you need to configure.

2
build
View File

@@ -1 +1 @@
47
109

1
helm/.gitignore vendored
View File

@@ -1 +1,2 @@
*.swp
charts

View File

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

View File

@@ -30,3 +30,13 @@ Create chart name and version as used by the chart label.
{{- define "owsec.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "owsec.ingress.apiVersion" -}}
{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" -}}
{{- print "networking.k8s.io/v1" -}}
{{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}}
{{- print "networking.k8s.io/v1beta1" -}}
{{- else -}}
{{- print "extensions/v1beta1" -}}
{{- end -}}
{{- end -}}

View File

@@ -2,7 +2,7 @@
{{- range $ingress, $ingressValue := .Values.ingresses }}
{{- if $ingressValue.enabled }}
---
apiVersion: extensions/v1beta1
apiVersion: {{ include "owsec.ingress.apiVersion" $root }}
kind: Ingress
metadata:
name: {{ include "owsec.fullname" $root }}-{{ $ingress }}
@@ -36,11 +36,25 @@ spec:
paths:
{{- range $ingressValue.paths }}
- path: {{ .path }}
{{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
pathType: {{ .pathType | default "ImplementationSpecific" }}
{{- end }}
backend:
{{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }}
service:
name: {{ include "owsec.fullname" $root }}-{{ .serviceName }}
port:
{{- if kindIs "string" .servicePort }}
name: {{ .servicePort }}
{{- else }}
number: {{ .servicePort }}
{{- end }}
{{- else }}
serviceName: {{ include "owsec.fullname" $root }}-{{ .serviceName }}
servicePort: {{ .servicePort }}
{{- end }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -8,7 +8,7 @@ fullnameOverride: ""
images:
owsec:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec
tag: main
tag: v2.4.0
pullPolicy: Always
# regcred:
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
@@ -50,6 +50,7 @@ ingresses:
- restapi.chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
serviceName: owsec
servicePort: restapi

View File

@@ -61,6 +61,8 @@ components:
- 6 # INTERNAL_ERROR,
- 7 # ACCESS_DENIED,
- 8 # INVALID_TOKEN
- 9 # expired token
- 10 # rate limit exceeded
ErrorDetails:
type: string
ErrorDescription:

View File

@@ -40,6 +40,7 @@ openwifi.system.commandchannel = /tmp/app.ucentralsec
openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem
openwifi.service.key.password = mypassword
smssender.enabled = false
smssender.provider = aws
smssender.aws.secretkey = ***************************************
smssender.aws.accesskey = ***************************************
@@ -53,6 +54,7 @@ smssender.aws.region = **************
#
# Security Microservice Specific Section
#
mailer.enabled = false
mailer.hostname = smtp.gmail.com
mailer.username = ************************
mailer.password = ************************

45
src/ACLProcessor.h Normal file
View File

@@ -0,0 +1,45 @@
//
// Created by stephane bourque on 2021-11-12.
//
#ifndef OWSEC_ACLPROCESSOR_H
#define OWSEC_ACLPROCESSOR_H
#include "RESTObjects/RESTAPI_SecurityObjects.h"
namespace OpenWifi {
class ACLProcessor {
public:
enum ACL_OPS {
READ,
MODIFY,
DELETE,
CREATE
};
static inline bool Can( const SecurityObjects::UserInfo & User, const SecurityObjects::UserInfo & Target, ACL_OPS Op) {
if(User.Id == Target.Id && Op==DELETE)
return false;
if(User.userRole==SecurityObjects::ROOT)
return true;
if(User.Id == Target.Id)
return true;
if(User.userRole!=SecurityObjects::ADMIN && User.userRole!=SecurityObjects::ROOT && Op!=READ)
return false;
if(Target.userRole==SecurityObjects::ROOT && Op!=READ)
return false;
return true;
}
private:
};
}
#endif //OWSEC_ACLPROCESSOR_H

View File

@@ -18,8 +18,8 @@ namespace OpenWifi {
};
static ActionLinkManager * instance() {
static ActionLinkManager instance;
return &instance;
static auto * instance_ = new ActionLinkManager;
return instance_;
}
int Start() final;

View File

@@ -56,9 +56,10 @@ namespace OpenWifi {
Logger_.notice("Stopping...");
}
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo )
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired )
{
std::lock_guard Guard(Mutex_);
Expired = false;
try {
std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request);
@@ -67,27 +68,29 @@ namespace OpenWifi {
}
if(!CallToken.empty()) {
if(StorageService()->IsTokenRevoked(CallToken))
auto Client = UserCache_.get(CallToken);
if( Client.isNull() ) {
SecurityObjects::UserInfoAndPolicy UInfo2;
uint64_t RevocationDate=0;
if(StorageService()->GetToken(CallToken,UInfo2,RevocationDate)) {
if(RevocationDate!=0)
return false;
auto Client = UserCache_.find(CallToken);
if( Client == UserCache_.end() ) {
if(StorageService()->GetToken(SessionToken,UInfo)) {
if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) {
UserCache_[UInfo.webtoken.access_token_] = UInfo;
return true;
}
}
return false;
}
if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) {
Expired = (UInfo2.webtoken.created_ + UInfo2.webtoken.expires_in_) < time(nullptr);
if(StorageService()->GetUserById(UInfo2.userinfo.Id,UInfo.userinfo)) {
UInfo.webtoken = UInfo2.webtoken;
UserCache_.update(CallToken, UInfo);
SessionToken = CallToken;
UInfo = Client->second ;
return true;
}
UserCache_.erase(Client);
StorageService()->RevokeToken(CallToken);
}
return false;
}
if(!Expired) {
SessionToken = CallToken;
UInfo = *Client ;
return true;
}
RevokeToken(CallToken);
return false;
}
} catch(const Poco::Exception &E) {
@@ -96,16 +99,24 @@ namespace OpenWifi {
return false;
}
void AuthService::RevokeToken(std::string & Token) {
UserCache_.remove(Token);
StorageService()->RevokeToken(Token);
}
bool AuthService::DeleteUserFromCache(const std::string &UserName) {
std::lock_guard Guard(Mutex_);
for(auto i=UserCache_.begin();i!=UserCache_.end();) {
if (i->second.userinfo.email==UserName) {
Logout(i->first, false);
i = UserCache_.erase(i);
} else {
++i;
}
std::vector<std::string> OldTokens;
UserCache_.forEach([&OldTokens,UserName](const std::string &token, const SecurityObjects::UserInfoAndPolicy& O) -> void
{ if(O.userinfo.email==UserName)
OldTokens.push_back(token);
});
for(const auto &i:OldTokens) {
Logout(i,false);
UserCache_.remove(i);
}
return true;
}
@@ -121,9 +132,6 @@ namespace OpenWifi {
void AuthService::Logout(const std::string &token, bool EraseFromCache) {
std::lock_guard Guard(Mutex_);
if(EraseFromCache)
UserCache_.erase(token);
try {
Poco::JSON::Object Obj;
Obj.set("event", "remove-token");
@@ -132,7 +140,7 @@ namespace OpenWifi {
std::stringstream ResultText;
Poco::JSON::Stringifier::stringify(Obj, ResultText);
std::string Tmp{token};
StorageService()->RevokeToken(Tmp);
RevokeToken(Tmp);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(),
false);
} catch (const Poco::Exception &E) {
@@ -183,9 +191,9 @@ namespace OpenWifi {
UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false;
UserCache_[UInfo.webtoken.access_token_] = UInfo;
UserCache_.update(UInfo.webtoken.access_token_,UInfo);
StorageService()->SetLastLogin(UInfo.userinfo.Id);
StorageService()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_,
StorageService()->AddToken(UInfo.userinfo.Id, UInfo.webtoken.access_token_,
UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_,
UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
}
@@ -253,7 +261,7 @@ namespace OpenWifi {
return false;
}
UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo )
UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired )
{
std::lock_guard Guard(Mutex_);
@@ -291,6 +299,7 @@ namespace OpenWifi {
UInfo.userinfo.lastLogin=std::time(nullptr);
StorageService()->SetLastLogin(UInfo.userinfo.Id);
CreateToken(UserName, UInfo );
return SUCCESS;
}
@@ -337,7 +346,7 @@ namespace OpenWifi {
A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL;
A.userId = UInfo.email;
A.id = MicroService::instance().CreateUUID();
A.id = MicroService::CreateUUID();
A.created = std::time(nullptr);
A.expires = A.created + 24*60*60;
StorageService()->CreateAction(A);
@@ -345,16 +354,39 @@ namespace OpenWifi {
return true;
}
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo) {
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired) {
std::lock_guard G(Mutex_);
auto It = UserCache_.find(Token);
if(It==UserCache_.end())
return false;
WebToken = It->second.webtoken;
UserInfo = It->second.userinfo;
Expired = false;
auto Client = UserCache_.get(Token);
if(!Client.isNull()) {
Expired = (Client->webtoken.created_ + Client->webtoken.expires_in_) < std::time(nullptr);
WebToken = Client->webtoken;
UserInfo = Client->userinfo;
return true;
}
std::string TToken{Token};
if(StorageService()->IsTokenRevoked(TToken)) {
return false;
}
// get the token from disk...
SecurityObjects::UserInfoAndPolicy UInfo;
uint64_t RevocationDate=0;
if(StorageService()->GetToken(TToken, UInfo, RevocationDate)) {
if(RevocationDate!=0)
return false;
Expired = (UInfo.webtoken.created_ + UInfo.webtoken.expires_in_) < std::time(nullptr);
if(StorageService()->GetUserById(UInfo.userinfo.Id,UInfo.userinfo)) {
WebToken = UInfo.webtoken;
UserCache_.update(UInfo.webtoken.access_token_, UInfo);
return true;
}
}
return false;
}
} // end of namespace

View File

@@ -18,6 +18,7 @@
#include "Poco/SHA2Engine.h"
#include "Poco/Crypto/DigestEngine.h"
#include "Poco/HMACEngine.h"
#include "Poco/ExpireLRUCache.h"
#include "framework/MicroService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
@@ -44,15 +45,15 @@ namespace OpenWifi{
static int AccessTypeToInt(ACCESS_TYPE T);
static AuthService *instance() {
static AuthService instance;
return &instance;
static auto * instance_ = new AuthService;
return instance_;
}
int Start() override;
void Stop() override;
[[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo );
[[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo );
[[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired);
[[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired );
void CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
[[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo);
[[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;};
@@ -60,8 +61,7 @@ namespace OpenWifi{
bool ValidatePassword(const std::string &pwd);
[[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo);
[[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request);
[[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired);
[[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type);
[[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type);
@@ -75,19 +75,21 @@ namespace OpenWifi{
[[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason);
[[nodiscard]] bool DeleteUserFromCache(const std::string &UserName);
[[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo);
void RevokeToken(std::string & Token);
[[nodiscard]] static inline const std::string GetLogoAssetURI() {
return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png";
}
[[nodiscard]] static inline const std::string GetLogoAssetFileName() {
return MicroService::instance().DataDir() + "/wwwassets/the_logo.png";
return MicroService::instance().WWWAssetsDir() + "/the_logo.png";
}
private:
Poco::JWT::Signer Signer_;
Poco::SHA2Engine SHA2_;
SecurityObjects::UserInfoCache UserCache_;
Poco::ExpireLRUCache<std::string,SecurityObjects::UserInfoAndPolicy> UserCache_{2048,1200000};
// SecurityObjects::UserInfoCache UserCache_;
std::string PasswordValidationStr_;
std::regex PasswordValidation_;
uint64_t TokenAging_ = 30 * 24 * 60 * 60;
@@ -119,8 +121,8 @@ namespace OpenWifi{
inline AuthService * AuthService() { return AuthService::instance(); }
[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) {
return AuthService()->IsAuthorized(Request, SessionToken, UInfo );
[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired) {
return AuthService()->IsAuthorized(Request, SessionToken, UInfo, Expired );
}
} // end of namespace

View File

@@ -26,7 +26,7 @@ namespace OpenWifi {
return false;
std::string Challenge = MakeChallenge();
std::string uuid = MicroService::instance().CreateUUID();
std::string uuid = MicroService::CreateUUID();
uint64_t Created = std::time(nullptr);
ChallengeStart.set("uuid",uuid);
@@ -71,8 +71,9 @@ namespace OpenWifi {
auto uuid = ChallengeResponse->get("uuid").toString();
auto Hint = Cache_.find(uuid);
if(Hint == end(Cache_))
if(Hint == end(Cache_)) {
return false;
}
auto answer = ChallengeResponse->get("answer").toString();
if(Hint->second.Answer!=answer) {

View File

@@ -24,8 +24,8 @@ namespace OpenWifi {
int Start() override;
void Stop() override;
static MFAServer *instance() {
static MFAServer instance;
return &instance;
static auto * instance_ = new MFAServer;
return instance_;
}
bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge);
@@ -35,7 +35,7 @@ namespace OpenWifi {
static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge);
static inline std::string MakeChallenge() {
return std::to_string(rand() % 999999);
return std::to_string(MicroService::instance().Random(1,999999));
}
private:

View File

@@ -20,7 +20,7 @@ namespace OpenWifi {
Server,
Internal,
false,
true, RateLimit{.Interval=1000,.MaxCalls=5}) {}
true, RateLimit{.Interval=1000,.MaxCalls=10}) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; };
void RequestResetPassword(SecurityObjects::ActionLink &Link);
void CompleteResetPassword();

View File

@@ -17,23 +17,38 @@
#include "StorageService.h"
namespace OpenWifi {
static void FilterCredentials(SecurityObjects::UserInfo & U) {
U.currentPassword.clear();
U.lastPasswords.clear();
U.oauthType.clear();
}
void RESTAPI_oauth2Handler::DoGet() {
if (!IsAuthorized()) {
bool Expired = false;
if (!IsAuthorized(Expired)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation);
}
bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false);
if(GetMe) {
Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email));
Poco::JSON::Object Me;
UserInfo_.userinfo.to_json(Me);
SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo;
FilterCredentials(ReturnedUser);
ReturnedUser.to_json(Me);
return ReturnObject(Me);
}
BadRequest(RESTAPI::Errors::UnrecognizedRequest);
}
void RESTAPI_oauth2Handler::DoDelete() {
if (!IsAuthorized()) {
return UnAuthorized("Not authorized.");
bool Expired = false;
if (!IsAuthorized(Expired)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation);
}
auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "...");
@@ -71,7 +86,7 @@ namespace OpenWifi {
SecurityObjects::ActionLink NewLink;
NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD;
NewLink.id = MicroService::instance().CreateUUID();
NewLink.id = MicroService::CreateUUID();
NewLink.userId = UInfo1.Id;
NewLink.created = std::time(nullptr);
NewLink.expires = NewLink.created + (24*60*60);
@@ -93,8 +108,8 @@ namespace OpenWifi {
if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) {
Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId));
if(Obj->has(RESTAPI::Protocol::UUID)) {
auto uuid = Obj->get(RESTAPI::Protocol::UUID).toString();
if(Obj->has("uuid")) {
auto uuid = Obj->get("uuid").toString();
if(MFAServer().ResendCode(uuid))
return OK();
}
@@ -103,7 +118,7 @@ namespace OpenWifi {
if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) {
Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId));
if(Obj->has(RESTAPI::Protocol::UUID)) {
if(Obj->has("uuid")) {
SecurityObjects::UserInfoAndPolicy UInfo;
if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) {
Poco::JSON::Object ReturnObj;
@@ -115,7 +130,8 @@ namespace OpenWifi {
}
SecurityObjects::UserInfoAndPolicy UInfo;
auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo);
bool Expired=false;
auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo, Expired);
if (Code==SUCCESS) {
Poco::JSON::Object ReturnObj;
if(AuthService()->RequiresMFA(UInfo)) {

View File

@@ -21,7 +21,7 @@ namespace OpenWifi {
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal, false, true , RateLimit{.Interval=2000,.MaxCalls=5}) {}
Internal, false, true , RateLimit{.Interval=1000,.MaxCalls=10}) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };
void DoGet() final;
void DoPost() final;

View File

@@ -7,8 +7,16 @@
#include "Poco/JSON/Parser.h"
#include "framework/RESTAPI_errors.h"
#include "SMSSender.h"
#include "ACLProcessor.h"
namespace OpenWifi {
static void FilterCredentials(SecurityObjects::UserInfo & U) {
U.currentPassword.clear();
U.lastPasswords.clear();
U.oauthType.clear();
}
void RESTAPI_user_handler::DoGet() {
std::string Id = GetBinding("id", "");
if(Id.empty()) {
@@ -27,9 +35,7 @@ namespace OpenWifi {
}
Poco::JSON::Object UserInfoObject;
UInfo.currentPassword.clear();
UInfo.lastPasswords.clear();
UInfo.oauthType.clear();
FilterCredentials(UInfo);
UInfo.to_json(UserInfoObject);
ReturnObject(UserInfoObject);
}
@@ -40,20 +46,12 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::MissingUserID);
}
if(UserInfo_.userinfo.userRole!= SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
if(UserInfo_.userinfo.Id == Id) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
SecurityObjects::UserInfo UInfo;
if(!StorageService()->GetUserById(Id,UInfo)) {
return NotFound();
}
if(UInfo.userRole==SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::DELETE)) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
@@ -64,6 +62,9 @@ namespace OpenWifi {
if(AuthService()->DeleteUserFromCache(UInfo.email)) {
// nothing to do
}
StorageService()->DeleteAvatar(UserInfo_.userinfo.email,Id);
Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email));
StorageService()->RevokeAllTokens(UInfo.email);
Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email));
@@ -76,57 +77,52 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::IdMustBe0);
}
SecurityObjects::UserInfo UInfo;
RESTAPI_utils::from_request(UInfo,*Request);
SecurityObjects::UserInfo NewUser;
RESTAPI_utils::from_request(NewUser,*Request);
if(UInfo.userRole == SecurityObjects::UNKNOWN) {
if(NewUser.userRole == SecurityObjects::UNKNOWN) {
return BadRequest(RESTAPI::Errors::InvalidUserRole);
}
if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
if(!ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
if(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && UInfo.userRole == SecurityObjects::ROOT) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
Poco::toLowerInPlace(UInfo.email);
if(!Utils::ValidEMailAddress(UInfo.email)) {
Poco::toLowerInPlace(NewUser.email);
if(!Utils::ValidEMailAddress(NewUser.email)) {
return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
}
if(!UInfo.currentPassword.empty()) {
if(!AuthService()->ValidatePassword(UInfo.currentPassword)) {
if(!NewUser.currentPassword.empty()) {
if(!AuthService()->ValidatePassword(NewUser.currentPassword)) {
return BadRequest(RESTAPI::Errors::InvalidPassword);
}
}
if(UInfo.name.empty())
UInfo.name = UInfo.email;
if(NewUser.name.empty())
NewUser.name = NewUser.email;
if(!StorageService()->CreateUser(UInfo.email,UInfo)) {
Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email));
if(!StorageService()->CreateUser(NewUser.email,NewUser)) {
Logger_.information(Poco::format("Could not add user '%s'.",NewUser.email));
return BadRequest(RESTAPI::Errors::RecordNotCreated);
}
if(GetParameter("email_verification","false")=="true") {
if(AuthService::VerifyEmail(UInfo))
Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email));
StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo);
if(AuthService::VerifyEmail(NewUser))
Logger_.information(Poco::format("Verification e-mail requested for %s",NewUser.email));
StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,NewUser.Id,NewUser);
}
if(!StorageService()->GetUserByEmail(UInfo.email, UInfo)) {
Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email));
if(!StorageService()->GetUserByEmail(NewUser.email, NewUser)) {
Logger_.information(Poco::format("User '%s' but not retrieved.",NewUser.email));
return NotFound();
}
Poco::JSON::Object UserInfoObject;
UInfo.to_json(UserInfoObject);
FilterCredentials(NewUser);
NewUser.to_json(UserInfoObject);
ReturnObject(UserInfoObject);
Logger_.information(Poco::format("User '%s' has been added by '%s')",UInfo.email, UserInfo_.userinfo.email));
Logger_.information(Poco::format("User '%s' has been added by '%s')",NewUser.email, UserInfo_.userinfo.email));
}
void RESTAPI_user_handler::DoPut() {
@@ -140,12 +136,8 @@ namespace OpenWifi {
return NotFound();
}
if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
if(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && Existing.userRole == SecurityObjects::ROOT) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
if(!ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
SecurityObjects::UserInfo NewUser;
@@ -224,15 +216,23 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::NeedMobileNumber);
}
if(NewUser.userTypeProprietaryInfo.mfa.method=="email") {
if(!NewUser.userTypeProprietaryInfo.mfa.method.empty()) {
if(NewUser.userTypeProprietaryInfo.mfa.method!="email" && NewUser.userTypeProprietaryInfo.mfa.method!="sms" ) {
return BadRequest("Unknown MFA method");
}
Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method;
}
if(Existing.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mfa.method.empty()) {
return BadRequest("Illegal MFA method");
}
}
if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) {
SecurityObjects::UserInfo NewUserInfo;
StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
Poco::JSON::Object ModifiedObject;
FilterCredentials(NewUserInfo);
NewUserInfo.to_json(ModifiedObject);
return ReturnObject(ModifiedObject);
}

View File

@@ -13,7 +13,8 @@ namespace OpenWifi {
if (i.first == "token") {
// can we find this token?
SecurityObjects::UserInfoAndPolicy SecObj;
if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo)) {
bool Expired = false;
if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo, Expired)) {
Poco::JSON::Object Obj;
SecObj.to_json(Obj);
return ReturnObject(Obj);

View File

@@ -562,7 +562,7 @@ namespace OpenWifi::ProvObjects {
}
I.notes = N;
I.modified = I.created = Now;
I.id = MicroService::instance().CreateUUID();
I.id = MicroService::CreateUUID();
return true;
}

View File

@@ -59,15 +59,15 @@ namespace OpenWifi::SecurityObjects {
struct MobilePhoneNumber {
std::string number;
bool verified;
bool primary;
bool verified = false;
bool primary = false;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct MfaAuthInfo {
bool enabled;
bool enabled = false;
std::string method;
void to_json(Poco::JSON::Object &Obj) const;
@@ -86,7 +86,7 @@ namespace OpenWifi::SecurityObjects {
std::string uuid;
std::string question;
std::string method;
uint64_t created;
uint64_t created = std::time(nullptr);
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);

View File

@@ -16,13 +16,16 @@
namespace OpenWifi {
int SMSSender::Start() {
Provider_ = MicroService::instance().ConfigGetString("sms.provider","aws");
Enabled_ = MicroService::instance().ConfigGetBool("smssender.enabled",false);
if(Enabled_) {
Provider_ = MicroService::instance().ConfigGetString("smssender.provider","aws");
if(Provider_=="aws") {
ProviderImpl_ = std::make_unique<SMS_provider_aws>(Logger_);
} else if(Provider_=="twilio") {
ProviderImpl_ = std::make_unique<SMS_provider_twilio>(Logger_);
}
Enabled_ = ProviderImpl_->Initialize();
}
return 0;
}

View File

@@ -18,15 +18,15 @@ namespace OpenWifi {
std::string Number;
std::string Code;
std::string UserName;
uint64_t Created;
bool Validated=false;
uint64_t Created = std::time(nullptr);
bool Validated = false;
};
class SMSSender : public SubSystemServer {
public:
static SMSSender *instance() {
static SMSSender instance;
return &instance;
static auto *instance_ = new SMSSender;
return instance_;
}
int Start() final;

View File

@@ -43,6 +43,7 @@ namespace OpenWifi {
if(!Running_)
return false;
try {
Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_);
Aws::SNS::Model::PublishRequest psms_req;
psms_req.SetMessage(Message.c_str());
@@ -56,6 +57,11 @@ namespace OpenWifi {
std::string ErrMsg{psms_out.GetError().GetMessage()};
Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg));
return false;
} catch (...) {
}
Logger_.debug(Poco::format("SMS NOT sent to %s: failure in SMS service",PhoneNumber));
return false;
}
}

View File

@@ -9,12 +9,9 @@
#include "Poco/Net/SMTPClientSession.h"
#include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h"
#include "Poco/Path.h"
#include "Poco/Exception.h"
#include "Poco/Net/SSLManager.h"
#include "Poco/Net/Context.h"
#include "Poco/Net/InvalidCertificateHandler.h"
#include "Poco/Net/AcceptCertificateHandler.h"
#include "SMTPMailerService.h"
#include "framework/MicroService.h"
@@ -23,6 +20,8 @@
namespace OpenWifi {
void SMTPMailerService::LoadMyConfig() {
Enabled_ = MicroService::instance().ConfigGetBool("mailer.enabled",false);
if(Enabled_) {
MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname");
SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username");
SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password");
@@ -30,8 +29,11 @@ namespace OpenWifi {
LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod");
MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port");
TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir());
MailRetry_ = (int) MicroService::instance().ConfigGetInt("mailer.retry",2*60);
MailAbandon_ = (int) MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60);
Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty());
}
}
int SMTPMailerService::Start() {
LoadMyConfig();
@@ -53,57 +55,46 @@ namespace OpenWifi {
bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) {
std::lock_guard G(Mutex_);
/*
uint64_t Now = std::time(nullptr);
std::string RecipientLower = Poco::toLower(Recipient);
auto CE = Cache_.find(RecipientLower);
if(CE!=Cache_.end()) {
// only allow messages to the same user within 2 minutes
if(!((CE->second.LastRequest-Now)<30 && CE->second.HowManyRequests<10))
return false;
if(CE->second.LastRequest-Now>30) {
CE->second.LastRequest = Now;
CE->second.HowManyRequests=0;
} else {
CE->second.HowManyRequests++;
}
} else {
Cache_[RecipientLower] = MessageCacheEntry{.LastRequest=Now, .HowManyRequests=0};
}
*/
Messages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr),
PendingMessages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr),
.LastTry=0,
.Sent=0,
.File=Poco::File(TemplateDir_ + "/" +Name),
.Attrs=Attrs});
return true;
}
void SMTPMailerService::run() {
Running_ = true;
while(Running_) {
Poco::Thread::trySleep(10000);
if(!Running_)
break;
{
std::lock_guard G(Mutex_);
Messages_.splice(Messages_.end(),PendingMessages_);
}
for(auto i=Messages_.begin();i!=Messages_.end();) {
if(!Running_)
break;
auto Recipient = i->Attrs.find(RECIPIENT_EMAIL)->second;
uint64_t Now = std::time(nullptr);
for(auto &i:Messages_) {
if(i.Sent==0 && (i.LastTry==0 || (Now-i.LastTry)>120)) {
if (SendIt(i)) {
i.LastTry = i.Sent = std::time(nullptr);
} else
i.LastTry = std::time(nullptr);
if((i->LastTry==0 || (Now-i->LastTry)>MailRetry_)) {
if (SendIt(*i)) {
Logger_.information(Poco::format("Attempting to deliver for mail '%s'.", Recipient));
i = Messages_.erase(i);
} else {
i->LastTry = Now;
++i;
}
} else if ((Now-i->Posted)>MailAbandon_) {
Logger_.information(Poco::format("Mail for '%s' has timed out and will not be sent.", Recipient));
i = Messages_.erase(i);
} else {
++i;
}
// Clean the list
std::remove_if(Messages_.begin(),Messages_.end(),[Now](MessageEvent &E){ return (E.Sent!=0 || ((Now-E.LastTry)>(15*60)));});
}
}
}
@@ -115,10 +106,12 @@ namespace OpenWifi {
}
bool SMTPMailerService::SendIt(const MessageEvent &Msg) {
std::string Recipient;
try
{
Poco::Net::MailMessage Message;
std::string Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second;
Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second;
auto H1 = Msg.Attrs.find(SENDER);
std::string TheSender;
@@ -129,7 +122,6 @@ namespace OpenWifi {
}
Message.setSender( TheSender );
Logger_.information(Poco::format("Sending message to:%s from %s",Recipient,TheSender));
Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient));
Message.setSubject(Msg.Attrs.find(SUBJECT)->second);
@@ -146,21 +138,26 @@ namespace OpenWifi {
auto Logo = Msg.Attrs.find(LOGO);
if(Logo!=Msg.Attrs.end()) {
try {
Poco::File LogoFile(AuthService::GetLogoAssetFileName());
std::ifstream IF(LogoFile.path());
std::ostringstream OS;
Poco::StreamCopier::copyStream(IF, OS);
Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/jpeg"));
Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png"));
} catch (...) {
Logger_.warning(Poco::format("Cannot add '%s' logo in email",AuthService::GetLogoAssetFileName()));
}
}
Poco::SharedPtr<Poco::Net::AcceptCertificateHandler> ptrHandler_ = new Poco::Net::AcceptCertificateHandler(false);
Poco::Net::SecureSMTPClientSession session(MailHost_,MailHostPort_);
Poco::Net::Context::Params P;
auto ptrContext = Poco::AutoPtr<Poco::Net::Context>
(new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "",
Poco::Net::Context::VERIFY_RELAXED, 9, true,
"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"));
Poco::Net::SSLManager::instance().initializeClient(nullptr,
&ptrHandler_,
ptrHandler_,
ptrContext);
session.login();
session.startTLS(ptrContext);
@@ -177,6 +174,9 @@ namespace OpenWifi {
{
Logger_.log(E);
}
catch (const std::exception &E) {
Logger_.warning(Poco::format("Cannot send message to:%s, error: %s",Recipient, E.what()));
}
return false;
}

View File

@@ -59,8 +59,8 @@ namespace OpenWifi {
class SMTPMailerService : public SubSystemServer, Poco::Runnable {
public:
static SMTPMailerService *instance() {
static SMTPMailerService instance;
return & instance;
static auto * instance_ = new SMTPMailerService;
return instance_;
}
struct MessageEvent {
@@ -71,41 +71,35 @@ namespace OpenWifi {
MessageAttributes Attrs;
};
struct MessageCacheEntry {
uint64_t LastRequest=0;
uint64_t HowManyRequests=0;
};
void run() override;
int Start() override;
void Stop() override;
bool SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs);
bool SendIt(const MessageEvent &Msg);
void LoadMyConfig();
void reinitialize(Poco::Util::Application &self) override;
bool Enabled() const { return Enabled_; }
private:
std::string MailHost_;
std::string Sender_;
int MailHostPort_=25;
int MailRetry_=2*60;
int MailAbandon_=2*60*20;
std::string SenderLoginUserName_;
std::string SenderLoginPassword_;
std::string LoginMethod_ = "login";
std::string LogoFileName_;
std::string TemplateDir_;
std::list<MessageEvent> Messages_;
std::map<std::string,MessageCacheEntry> Cache_;
std::list<MessageEvent> PendingMessages_;
Poco::Thread SenderThr_;
std::atomic_bool Running_=false;
bool Enabled_=false;
Poco::Net::AcceptCertificateHandler ptrHandler_;
SMTPMailerService() noexcept:
SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer"),
ptrHandler_(false)
SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer")
{
std::string E{"SHA512"};
}
};

View File

@@ -16,12 +16,28 @@ namespace OpenWifi {
StorageClass::Start();
Create_Tables();
InitializeDefaultUser();
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_);
return 0;
}
void Storage::Stop() {
Logger_.notice("Stopping.");
Timer_.stop();
StorageClass::Stop();
}
void Archiver::onTimer(Poco::Timer &timer) {
Poco::Logger &logger = Poco::Logger::get("STORAGE-ARCHIVER");
logger.information("Squiggy the DB: removing old tokens.");
StorageService()->CleanExpiredTokens();
logger.information("Squiggy the DB: removing old actionLinks.");
StorageService()->CleanOldActionLinks();
}
}
// namespace

View File

@@ -13,6 +13,8 @@
#include "framework/StorageClass.h"
#include "AuthService.h"
#include "Poco/Timer.h"
namespace OpenWifi {
static const std::string AllEmailTemplatesFieldsForCreation {
@@ -27,6 +29,12 @@ namespace OpenWifi {
};
class Archiver {
public:
void onTimer(Poco::Timer & timer);
private:
};
class Storage : public StorageClass {
public:
@@ -76,8 +84,8 @@ namespace OpenWifi {
}
static Storage *instance() {
static Storage instance;
return &instance;
static auto * instance_ = new Storage;
return instance_;
}
int Start() override;
@@ -104,12 +112,12 @@ namespace OpenWifi {
bool GetAvatar(const std::string & Admin, std::string &Id, Poco::TemporaryFile &FileName, std::string &Type, std::string & Name);
bool DeleteAvatar(const std::string & Admin, std::string &Id);
bool AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut);
bool AddToken(std::string &UserId, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut);
bool RevokeToken( std::string & Token );
bool IsTokenRevoked( std::string & Token );
bool CleanRevokedTokens( uint64_t Oldest );
bool CleanExpiredTokens();
bool RevokeAllTokens( std::string & UserName );
bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo);
bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate);
/*
* All ActionLinks functions
@@ -121,6 +129,7 @@ namespace OpenWifi {
bool SentAction(std::string &ActionId);
bool GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A);
bool GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max=200);
void CleanOldActionLinks();
private:
int Create_Tables();
@@ -128,6 +137,13 @@ namespace OpenWifi {
int Create_AvatarTable();
int Create_TokensTable();
int Create_ActionLinkTable();
Poco::Timer Timer_;
Archiver Archiver_;
std::unique_ptr<Poco::TimerCallback<Archiver>> Archivercallback_;
/// This is to support a mistake that was deployed...
void ReplaceOldDefaultUUID();
};
inline Storage * StorageService() { return Storage::instance(); };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
//
// Created by stephane bourque on 2021-09-14.
//
#ifndef OWPROV_CONFIGURATIONVALIDATOR_H
#define OWPROV_CONFIGURATIONVALIDATOR_H
#include <nlohmann/json-schema.hpp>
#include "framework/MicroService.h"
using nlohmann::json;
using nlohmann::json_schema::json_validator;
namespace OpenWifi {
class ConfigurationValidator : public SubSystemServer {
public:
static ConfigurationValidator *instance() {
if(instance_== nullptr)
instance_ = new ConfigurationValidator;
return instance_;
}
bool Validate(const std::string &C, std::string &Error);
static void my_format_checker(const std::string &format, const std::string &value);
int Start() override;
void Stop() override;
void reinitialize(Poco::Util::Application &self) override;
private:
static ConfigurationValidator * instance_;
bool Initialized_=false;
bool Working_=false;
void Init();
std::unique_ptr<json_validator> Validator_=std::make_unique<json_validator>(nullptr, my_format_checker);
ConfigurationValidator():
SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") {
}
};
inline ConfigurationValidator * ConfigurationValidator() { return ConfigurationValidator::instance(); }
inline bool ValidateUCentralConfiguration(const std::string &C, std::string &Error) { return ConfigurationValidator::instance()->Validate(C, Error); }
}
#endif //OWPROV_CONFIGURATIONVALIDATOR_H

View File

@@ -69,6 +69,8 @@ using namespace std::chrono_literals;
#include "RESTObjects/RESTAPI_SecurityObjects.h"
#include "nlohmann/json.hpp"
#include "ow_version.h"
namespace OpenWifi {
enum UNAUTHORIZED_REASON {
@@ -80,7 +82,9 @@ namespace OpenWifi {
PASSWORD_INVALID,
INTERNAL_ERROR,
ACCESS_DENIED,
INVALID_TOKEN
INVALID_TOKEN,
EXPIRED_TOKEN,
RATE_LIMIT_EXCEEDED
};
class AppServiceRegistry {
@@ -88,8 +92,8 @@ namespace OpenWifi {
inline AppServiceRegistry();
static AppServiceRegistry & instance() {
static AppServiceRegistry instance;
return instance;
static AppServiceRegistry *instance_= new AppServiceRegistry;
return *instance_;
}
inline ~AppServiceRegistry() {
@@ -1433,8 +1437,8 @@ namespace OpenWifi {
};
static RESTAPI_RateLimiter *instance() {
static RESTAPI_RateLimiter instance;
return &instance;
static RESTAPI_RateLimiter * instance_ = new RESTAPI_RateLimiter;
return instance_;
}
inline int Start() final { return 0;};
@@ -1444,18 +1448,18 @@ namespace OpenWifi {
Poco::URI uri(R.getURI());
auto H = str_hash(uri.getPath() + R.clientAddress().host().toString());
auto E = Cache_.get(H);
const auto p1 = std::chrono::system_clock::now();
auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(p1.time_since_epoch()).count();
auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
if(E.isNull()) {
Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1});
Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString()));
return false;
}
if((Now-E->Start)<Period) {
E->Count++;
Cache_.update(H,E);
if(E->Count > MaxCalls)
if(E->Count > MaxCalls) {
Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString()));
return true;
}
return false;
}
E->Start = Now;
@@ -1523,20 +1527,23 @@ namespace OpenWifi {
Request = &RequestIn;
Response = &ResponseIn;
if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls))
return;
if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) {
return UnAuthorized("Rate limit exceeded.",RATE_LIMIT_EXCEEDED);
}
if (!ContinueProcessing())
return;
if (AlwaysAuthorize_ && !IsAuthorized()) {
return;
bool Expired=false;
if (AlwaysAuthorize_ && !IsAuthorized(Expired)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken, EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::InvalidCredentials, ACCESS_DENIED);
}
std::string Reason;
if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) {
UnAuthorized(Reason, ACCESS_DENIED);
return;
return UnAuthorized(Reason, ACCESS_DENIED);
}
ParseParameters();
@@ -1874,7 +1881,7 @@ namespace OpenWifi {
return true;
}
inline bool IsAuthorized();
inline bool IsAuthorized(bool & Expired);
inline void ReturnObject(Poco::JSON::Object &Object) {
PrepareResponse();
@@ -1895,7 +1902,7 @@ namespace OpenWifi {
QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0);
QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0);
QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 1);
QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0);
QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100);
QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, "");
QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, "");
@@ -1907,7 +1914,7 @@ namespace OpenWifi {
QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false);
if(QB_.Offset<1)
QB_.Offset=1;
QB_.Offset=0;
return true;
}
@@ -2076,6 +2083,50 @@ namespace OpenWifi {
Poco::JSON::Object Body_;
};
class KafkaProducer : public Poco::Runnable {
public:
inline void run();
void Start() {
if(!Running_) {
Running_=true;
Worker_.start(*this);
}
}
void Stop() {
if(Running_) {
Running_=false;
Worker_.wakeUp();
Worker_.join();
}
}
private:
std::mutex Mutex_;
Poco::Thread Worker_;
std::atomic_bool Running_=false;
};
class KafkaConsumer : public Poco::Runnable {
public:
inline void run();
void Start() {
if(!Running_) {
Running_=true;
Worker_.start(*this);
}
}
void Stop() {
if(Running_) {
Running_=false;
Worker_.wakeUp();
Worker_.join();
}
}
private:
std::mutex Mutex_;
Poco::Thread Worker_;
std::atomic_bool Running_=false;
};
class KafkaManager : public SubSystemServer {
public:
struct KMessage {
@@ -2084,33 +2135,32 @@ namespace OpenWifi {
PayLoad;
};
friend class KafkaConsumer;
friend class KafkaProducer;
inline void initialize(Poco::Util::Application & self) override;
static KafkaManager *instance() {
static KafkaManager instance;
return &instance;
static KafkaManager * instance_ = new KafkaManager;
return instance_;
}
inline int Start() override {
if(!KafkaEnabled_)
return 0;
ProducerThr_ = std::make_unique<std::thread>([this]() { this->ProducerThr(); });
ConsumerThr_ = std::make_unique<std::thread>([this]() { this->ConsumerThr(); });
ConsumerThr_.Start();
ProducerThr_.Start();
return 0;
}
inline void Stop() override {
if(KafkaEnabled_) {
ProducerRunning_ = ConsumerRunning_ = false;
ProducerThr_->join();
ConsumerThr_->join();
ProducerThr_.Stop();
ConsumerThr_.Stop();
return;
}
}
inline void ProducerThr();
inline void ConsumerThr();
inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true ) {
if(KafkaEnabled_) {
std::lock_guard G(Mutex_);
@@ -2163,18 +2213,13 @@ namespace OpenWifi {
// void WakeUp();
private:
std::mutex ProducerMutex_;
std::mutex ConsumerMutex_;
bool KafkaEnabled_ = false;
std::atomic_bool ProducerRunning_ = false;
std::atomic_bool ConsumerRunning_ = false;
std::queue<KMessage> Queue_;
std::string SystemInfoWrapper_;
std::unique_ptr<std::thread> ConsumerThr_;
std::unique_ptr<std::thread> ProducerThr_;
int FunctionId_=1;
Types::NotifyTable Notifiers_;
std::unique_ptr<cppkafka::Configuration> Config_;
KafkaProducer ProducerThr_;
KafkaConsumer ConsumerThr_;
inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) {
Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition()));
@@ -2199,8 +2244,8 @@ namespace OpenWifi {
}
static AuthClient *instance() {
static AuthClient instance;
return &instance;
static AuthClient * instance_ = new AuthClient;
return instance_;
}
inline int Start() override {
@@ -2208,25 +2253,20 @@ namespace OpenWifi {
}
inline void Stop() override {
Cache_.clear();
}
inline void RemovedCachedToken(const std::string &Token) {
std::lock_guard G(Mutex_);
UserCache_.erase(Token);
Cache_.remove(Token);
}
inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) {
return ((T.expires_in_+T.created_)<std::time(nullptr));
}
inline bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) {
std::lock_guard G(Mutex_);
auto User = UserCache_.find(SessionToken);
if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) {
UInfo = User->second;
return true;
} else {
inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) {
try {
Types::StringPairVec QueryData;
QueryData.push_back(std::make_pair("token",SessionToken));
OpenAPIRequestGet Req( uSERVICE_SECURITY,
@@ -2236,49 +2276,39 @@ namespace OpenWifi {
Poco::JSON::Object::Ptr Response;
if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) {
if(Response->has("tokenInfo") && Response->has("userInfo")) {
SecurityObjects::UserInfoAndPolicy P;
P.from_json(Response);
UserCache_[SessionToken] = P;
UInfo = P;
UInfo.from_json(Response);
if(IsTokenExpired(UInfo.webtoken)) {
Expired = true;
return false;
}
Expired = false;
Cache_.update(SessionToken, UInfo);
return true;
}
}
} catch (...) {
}
Expired = false;
return false;
}
inline bool IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) {
std::lock_guard G(Mutex_);
auto User = UserCache_.find(SessionToken);
if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) {
UInfo = User->second;
return true;
} else {
Types::StringPairVec QueryData;
QueryData.push_back(std::make_pair("token",SessionToken));
OpenAPIRequestGet Req(uSERVICE_SECURITY,
"/api/v1/validateToken",
QueryData,
5000);
Poco::JSON::Object::Ptr Response;
if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) {
if(Response->has("tokenInfo") && Response->has("userInfo")) {
SecurityObjects::UserInfoAndPolicy P;
P.from_json(Response);
UserCache_[SessionToken] = P;
UInfo = P;
}
return true;
}
}
inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) {
auto User = Cache_.get(SessionToken);
if(!User.isNull()) {
if(IsTokenExpired(User->webtoken)) {
Expired = true;
return false;
}
Expired = false;
UInfo = *User;
return true;
}
return RetrieveTokenInformation(SessionToken, UInfo, Expired);
}
private:
OpenWifi::SecurityObjects::UserInfoCache UserCache_;
Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy> Cache_{1024,1200000 };
};
inline AuthClient * AuthClient() { return AuthClient::instance(); }
@@ -2338,14 +2368,14 @@ namespace OpenWifi {
}
static ALBHealthCheckServer *instance() {
static ALBHealthCheckServer instance;
return &instance;
static ALBHealthCheckServer * instance = new ALBHealthCheckServer;
return instance;
}
inline int Start() override;
inline void Stop() override {
if(Server_)
if(Running_)
Server_->stop();
}
@@ -2353,6 +2383,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;
};
inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); }
@@ -2364,23 +2395,24 @@ namespace OpenWifi {
Poco::Logger & L, RESTAPI_GenericServer & S);
class RESTAPI_server : public SubSystemServer {
class RESTAPI_ExtServer : public SubSystemServer {
public:
static RESTAPI_server *instance() {
static RESTAPI_server instance;
return &instance;
static RESTAPI_ExtServer *instance() {
static RESTAPI_ExtServer *instance_ = new RESTAPI_ExtServer;
return instance_;
}
int Start() override;
inline void Stop() override {
Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ )
svr->stop();
Pool_.joinAll();
RESTServers_.clear();
}
inline void reinitialize(Poco::Util::Application &self) override;
inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) {
inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path, uint64_t Id) {
RESTAPIHandler::BindingMap Bindings;
return RESTAPI_external_server(Path, Bindings, Logger_, Server_);
}
@@ -2390,18 +2422,18 @@ namespace OpenWifi {
Poco::ThreadPool Pool_;
RESTAPI_GenericServer Server_;
RESTAPI_server() noexcept:
RESTAPI_ExtServer() noexcept:
SubSystemServer("RESTAPIServer", "RESTAPIServer", "openwifi.restapi")
{
}
};
inline RESTAPI_server * RESTAPI_server() { return RESTAPI_server::instance(); };
inline RESTAPI_ExtServer * RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); };
class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
RequestHandlerFactory(RESTAPI_GenericServer & Server) :
Logger_(RESTAPI_server::instance()->Logger()),
explicit ExtRequestHandlerFactory(RESTAPI_GenericServer & Server) :
Logger_(RESTAPI_ExtServer::instance()->Logger()),
Server_(Server)
{
@@ -2410,15 +2442,16 @@ namespace OpenWifi {
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override {
Poco::URI uri(Request.getURI());
auto *Path = uri.getPath().c_str();
return RESTAPI_server()->CallServer(Path);
return RESTAPI_ExtServer()->CallServer(Path, TransactionId_++);
}
private:
static inline std::atomic_uint64_t TransactionId_ = 1;
Poco::Logger &Logger_;
RESTAPI_GenericServer &Server_;
};
inline int RESTAPI_server::Start() {
inline int RESTAPI_ExtServer::Start() {
Logger_.information("Starting.");
Server_.InitLogging();
@@ -2432,12 +2465,12 @@ namespace OpenWifi {
if(!Svr.RootCA().empty())
Svr.LogCas(Logger_);
auto Params = new Poco::Net::HTTPServerParams;
Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams;
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new RequestHandlerFactory(Server_), Pool_, Sock, Params);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory(Server_), Pool_, Sock, Params);
NewServer->start();
RESTServers_.push_back(std::move(NewServer));
}
@@ -2445,12 +2478,12 @@ namespace OpenWifi {
return 0;
}
class RESTAPI_InternalServer : public SubSystemServer {
class RESTAPI_IntServer : public SubSystemServer {
public:
static RESTAPI_InternalServer *instance() {
static RESTAPI_InternalServer instance;
return &instance;
static RESTAPI_IntServer *instance() {
static RESTAPI_IntServer *instance_ = new RESTAPI_IntServer;
return instance_;
}
inline int Start() override;
@@ -2458,12 +2491,13 @@ namespace OpenWifi {
Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ )
svr->stop();
Pool_.joinAll();
RESTServers_.clear();
}
inline void reinitialize(Poco::Util::Application &self) override;
inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) {
inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path, uint64_t Id) {
RESTAPIHandler::BindingMap Bindings;
return RESTAPI_internal_server(Path, Bindings, Logger_, Server_);
}
@@ -2472,31 +2506,31 @@ namespace OpenWifi {
Poco::ThreadPool Pool_;
RESTAPI_GenericServer Server_;
RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi")
RESTAPI_IntServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi")
{
}
};
inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); };
inline RESTAPI_IntServer * RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); };
class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
InternalRequestHandlerFactory(RESTAPI_GenericServer & Server) :
Logger_(RESTAPI_InternalServer()->Logger()),
explicit IntRequestHandlerFactory(RESTAPI_GenericServer & Server) :
Logger_(RESTAPI_IntServer()->Logger()),
Server_(Server){}
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override {
Poco::URI uri(Request.getURI());
auto *Path = uri.getPath().c_str();
return RESTAPI_InternalServer()->CallServer(Path);
return RESTAPI_IntServer()->CallServer(Path, TransactionId_++);
}
private:
static inline std::atomic_uint64_t TransactionId_ = 1;
Poco::Logger & Logger_;
RESTAPI_GenericServer &Server_;
RESTAPI_GenericServer & Server_;
};
inline int RESTAPI_InternalServer::Start() {
inline int RESTAPI_IntServer::Start() {
Logger_.information("Starting.");
Server_.InitLogging();
@@ -2514,7 +2548,7 @@ namespace OpenWifi {
Params->setMaxQueued(200);
Params->setKeepAlive(true);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new InternalRequestHandlerFactory(Server_), Pool_, Sock, Params);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory(Server_), Pool_, Sock, Params);
NewServer->start();
RESTServers_.push_back(std::move(NewServer));
}
@@ -2554,11 +2588,13 @@ namespace OpenWifi {
DAEMON_BUS_TIMER(BusTimer),
SubSystems_(std::move(Subsystems)) {
instance_ = this;
RandomEngine_.seed(std::chrono::steady_clock::now().time_since_epoch().count());
}
[[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_; }
[[nodiscard]] uint64_t ID() const { return ID_; }
[[nodiscard]] std::string Hash() const { return MyHash_; };
@@ -2571,6 +2607,13 @@ namespace OpenWifi {
static inline uint64_t GetPID() { return Poco::Process::id(); };
[[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; };
[[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;};
[[nodiscard]] inline uint64_t Random(uint64_t ceiling) {
return (RandomEngine_() % ceiling);
}
[[nodiscard]] inline uint64_t Random(uint64_t min, uint64_t max) {
return ((RandomEngine_() % (max-min)) + min);
}
inline void Exit(int Reason);
inline void BusMessageReceived(const std::string &Key, const std::string & Message);
@@ -2592,7 +2635,7 @@ namespace OpenWifi {
inline void InitializeSubSystemServers();
inline void StartSubSystemServers();
inline void StopSubSystemServers();
[[nodiscard]] inline std::string CreateUUID();
[[nodiscard]] static inline std::string CreateUUID();
inline bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level);
inline void Reload(const std::string &Sub);
inline Types::StringVec GetSubSystems() const;
@@ -2622,9 +2665,10 @@ namespace OpenWifi {
std::string ConfigFileName_;
Poco::UUIDGenerator UUIDGenerator_;
uint64_t ID_ = 1;
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_ = nullptr;
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_;
bool DebugMode_ = false;
std::string DataDir_;
std::string WWWAssetsDir_;
SubSystemVec SubSystems_;
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
Poco::Crypto::Cipher * Cipher_ = nullptr;
@@ -2634,9 +2678,10 @@ namespace OpenWifi {
std::string MyPrivateEndPoint_;
std::string MyPublicEndPoint_;
std::string UIURI_;
std::string Version_{std::string(APP_VERSION) + "("+ BUILD_NUMBER + ")"};
std::string Version_{ OW_VERSION::VERSION + "("+ OW_VERSION::BUILD + ")" + " - " + OW_VERSION::HASH };
BusEventManager BusEventManager_;
std::mutex InfraMutex_;
std::default_random_engine RandomEngine_;
std::string DAEMON_PROPERTIES_FILENAME;
std::string DAEMON_ROOT_ENV_VAR;
@@ -2784,8 +2829,8 @@ namespace OpenWifi {
// add the default services
SubSystems_.push_back(KafkaManager());
SubSystems_.push_back(ALBHealthCheckServer());
SubSystems_.push_back(RESTAPI_server());
SubSystems_.push_back(RESTAPI_InternalServer());
SubSystems_.push_back(RESTAPI_ExtServer());
SubSystems_.push_back(RESTAPI_IntServer());
Poco::Net::initializeSSL();
Poco::Net::HTTPStreamFactory::registerFactory();
@@ -2813,6 +2858,9 @@ namespace OpenWifi {
logger().log(E);
}
}
WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets","");
if(WWWAssetsDir_.empty())
WWWAssetsDir_ = DataDir_;
LoadMyConfig();
@@ -2919,12 +2967,41 @@ namespace OpenWifi {
inline void MicroService::StopSubSystemServers() {
BusEventManager_.Stop();
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) {
(*i)->Stop();
}
}
[[nodiscard]] inline std::string MicroService::CreateUUID() {
return UUIDGenerator_.create().toString();
static std::random_device rd;
static std::mt19937_64 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::stringstream ss;
int i;
ss << std::hex;
for (i = 0; i < 8; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 4; i++) {
ss << dis(gen);
}
ss << "-4";
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
ss << dis2(gen);
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 12; i++) {
ss << dis(gen);
};
return ss.str();
}
inline bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
@@ -2938,7 +3015,6 @@ namespace OpenWifi {
}
return true;
} else {
// std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl;
for (auto i : SubSystems_) {
if (Sub == Poco::toLower(i->Name())) {
i->Logger().setLevel(P);
@@ -3088,7 +3164,6 @@ namespace OpenWifi {
StartSubSystemServers();
waitForTerminationRequest();
StopSubSystemServers();
logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME));
}
@@ -3168,6 +3243,7 @@ namespace OpenWifi {
inline int ALBHealthCheckServer::Start() {
if(MicroService::instance().ConfigGetBool("alb.enable",false)) {
Running_=true;
Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015);
Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_);
auto Params = new Poco::Net::HTTPServerParams;
@@ -3212,41 +3288,42 @@ namespace OpenWifi {
KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false);
}
inline void KafkaManager::ProducerThr() {
inline void KafkaProducer::run() {
cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }
});
SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
std::to_string(MicroService::instance().ID()) +
R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() +
R"lit(" } , "payload" : )lit" ;
cppkafka::Producer Producer(Config);
ProducerRunning_ = true;
while(ProducerRunning_) {
Running_ = true;
while(Running_) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
try
{
std::lock_guard G(ProducerMutex_);
std::lock_guard G(Mutex_);
auto Num=0;
while (!Queue_.empty()) {
const auto M = Queue_.front();
while (!KafkaManager()->Queue_.empty()) {
const auto M = KafkaManager()->Queue_.front();
Producer.produce(
cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad));
Queue_.pop();
KafkaManager()->Queue_.pop();
Num++;
}
if(Num)
Producer.flush();
} catch (const cppkafka::HandleException &E ) {
Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()}));
KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) {
Logger_.log(E);
KafkaManager()->Logger_.log(E);
}
}
Producer.flush();
}
inline void KafkaManager::ConsumerThr() {
inline void KafkaConsumer::run() {
cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") },
@@ -3266,13 +3343,13 @@ namespace OpenWifi {
cppkafka::Consumer Consumer(Config);
Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) {
if(!partitions.empty()) {
Logger_.information(Poco::format("Partition assigned: %Lu...",
KafkaManager()->Logger_.information(Poco::format("Partition assigned: %Lu...",
(uint64_t)partitions.front().get_partition()));
}
});
Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) {
if(!partitions.empty()) {
Logger_.information(Poco::format("Partition revocation: %Lu...",
KafkaManager()->Logger_.information(Poco::format("Partition revocation: %Lu...",
(uint64_t)partitions.front().get_partition()));
}
});
@@ -3281,13 +3358,13 @@ namespace OpenWifi {
auto BatchSize = MicroService::instance().ConfigGetInt("openwifi.kafka.consumer.batchsize",20);
Types::StringVec Topics;
for(const auto &i:Notifiers_)
for(const auto &i:KafkaManager()->Notifiers_)
Topics.push_back(i.first);
Consumer.subscribe(Topics);
ConsumerRunning_ = true;
while(ConsumerRunning_) {
Running_ = true;
while(Running_) {
try {
std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200));
for(auto const &Msg:MsgVec) {
@@ -3295,14 +3372,14 @@ namespace OpenWifi {
continue;
if (Msg.get_error()) {
if (!Msg.is_eof()) {
Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
KafkaManager()->Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
}if(!AutoCommit)
Consumer.async_commit(Msg);
continue;
}
std::lock_guard G(ConsumerMutex_);
auto It = Notifiers_.find(Msg.get_topic());
if (It != Notifiers_.end()) {
std::lock_guard G(Mutex_);
auto It = KafkaManager()->Notifiers_.find(Msg.get_topic());
if (It != KafkaManager()->Notifiers_.end()) {
Types::TopicNotifyFunctionList &FL = It->second;
std::string Key{Msg.get_key()};
std::string Payload{Msg.get_payload()};
@@ -3315,21 +3392,22 @@ namespace OpenWifi {
Consumer.async_commit(Msg);
}
} catch (const cppkafka::HandleException &E) {
Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()}));
KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) {
Logger_.log(E);
KafkaManager()->Logger_.log(E);
}
}
Consumer.unsubscribe();
}
inline void RESTAPI_server::reinitialize(Poco::Util::Application &self) {
inline void RESTAPI_ExtServer::reinitialize(Poco::Util::Application &self) {
MicroService::instance().LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
Start();
}
void RESTAPI_InternalServer::reinitialize(Poco::Util::Application &self) {
void RESTAPI_IntServer::reinitialize(Poco::Util::Application &self) {
MicroService::instance().LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
@@ -3540,11 +3618,9 @@ namespace OpenWifi {
if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response OK" << std::endl;
} else {
Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response: " << Response.getStatus() << std::endl;
}
return Response.getStatus();
}
@@ -3590,11 +3666,9 @@ namespace OpenWifi {
if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response OK" << std::endl;
} else {
Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response: " << Response.getStatus() << std::endl;
}
return Response.getStatus();
}
@@ -3620,9 +3694,9 @@ namespace OpenWifi {
}
#ifdef TIP_SECURITY_SERVICE
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo );
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired );
#endif
inline bool RESTAPIHandler::IsAuthorized() {
inline bool RESTAPIHandler::IsAuthorized( bool & Expired ) {
if(Internal_) {
auto Allowed = MicroService::instance().IsValidAPIKEY(*Request);
if(!Allowed) {
@@ -3652,12 +3726,12 @@ namespace OpenWifi {
}
}
#ifdef TIP_SECURITY_SERVICE
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_)) {
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired)) {
#else
if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) {
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired)) {
#endif
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s",
Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s'",
UserInfo_.userinfo.email,
Utils::FormatIPv6(Request->clientAddress().toString()),
Request->clientAddress().toString(),
@@ -3667,11 +3741,10 @@ namespace OpenWifi {
return true;
} else {
if(Server_.LogBadTokens(true)) {
Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s",
Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s'",
Utils::FormatIPv6(Request->clientAddress().toString()),
Request->getMethod(), Request->getURI()));
}
UnAuthorized("Invalid token", INVALID_TOKEN);
}
return false;
}

View File

@@ -59,6 +59,7 @@ namespace OpenWifi::RESTAPI::Errors {
static const std::string UnrecognizedRequest{"Ill-formed request. Please consult documentation."};
static const std::string MissingAuthenticationInformation{"Missing authentication information."};
static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."};
static const std::string ExpiredToken{"Token has expired, user must login."};
}
#endif //OWPROV_RESTAPI_ERRORS_H

View File

@@ -2,8 +2,7 @@
// Created by stephane bourque on 2021-10-06.
//
#ifndef OPENWIFI_STORAGE_H
#define OPENWIFI_STORAGE_H
#pragma once
#include "Poco/Data/Session.h"
#include "Poco/Data/SessionPool.h"
@@ -26,13 +25,6 @@ namespace OpenWifi {
class StorageClass : public SubSystemServer {
public:
/* static StorageClass *instance() {
if (instance_ == nullptr) {
instance_ = new StorageClass;
}
return instance_;
}
*/
StorageClass() noexcept:
SubSystemServer("StorageClass", "STORAGE-SVR", "storage")
{
@@ -56,18 +48,18 @@ namespace OpenWifi {
}
void Stop() override {
Pool_->shutdown();
}
[[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) {
if(dbType_==sqlite) {
return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " ";
return " LIMIT " + std::to_string(From) + ", " + std::to_string(HowMany) + " ";
} else if(dbType_==pgsql) {
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " ";
} else if(dbType_==mysql) {
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " ";
}
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " ";
}
inline std::string ConvertParams(const std::string & S) const {
@@ -96,10 +88,10 @@ namespace OpenWifi {
inline int Setup_PostgreSQL();
protected:
std::unique_ptr<Poco::Data::SessionPool> Pool_;
std::unique_ptr<Poco::Data::SQLite::Connector> SQLiteConn_;
std::unique_ptr<Poco::Data::PostgreSQL::Connector> PostgresConn_;
std::unique_ptr<Poco::Data::MySQL::Connector> MySQLConn_;
Poco::SharedPtr<Poco::Data::SessionPool> Pool_;
Poco::Data::SQLite::Connector SQLiteConn_;
Poco::Data::PostgreSQL::Connector PostgresConn_;
Poco::Data::MySQL::Connector MySQLConn_;
DBType dbType_ = sqlite;
};
@@ -114,9 +106,8 @@ namespace OpenWifi {
auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db");
auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64);
auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60);
SQLiteConn_ = std::make_unique<Poco::Data::SQLite::Connector>();
SQLiteConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime);
SQLiteConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 4, NumSessions, IdleTime));
return 0;
}
@@ -139,9 +130,8 @@ namespace OpenWifi {
";port=" + Port +
";compress=true;auto-reconnect=true";
MySQLConn_ = std::make_unique<Poco::Data::MySQL::Connector>();
MySQLConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
MySQLConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(MySQLConn_.name(), ConnectionStr, 4, NumSessions, IdleTime));
return 0;
}
@@ -166,14 +156,11 @@ namespace OpenWifi {
" port=" + Port +
" connect_timeout=" + ConnectionTimeout;
PostgresConn_ = std::make_unique<Poco::Data::PostgreSQL::Connector>();
PostgresConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
PostgresConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(PostgresConn_.name(), ConnectionStr, 4, NumSessions, IdleTime));
return 0;
}
#endif
}
#endif //OPENWIFI_STORAGE_H

13
src/ow_version.h.in Normal file
View File

@@ -0,0 +1,13 @@
//
// Created by stephane bourque on 2021-12-06.
//
#pragma once
#include <string>
namespace OW_VERSION {
inline static const std::string VERSION{"@CMAKE_PROJECT_VERSION@"};
inline static const std::string BUILD{"@BUILD_NUM@"};
inline static const std::string HASH{"@GIT_HASH@"};
}

View File

@@ -183,4 +183,19 @@ namespace OpenWifi {
return false;
}
void Storage::CleanOldActionLinks() {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
uint64_t CutOff = std::time(nullptr) - (30 * 24 * 60 * 60);
std::string St{"DELETE from ActionLinks where Created<=?"};
Delete << ConvertParams(St),
Poco::Data::Keywords::use(CutOff);
Delete.execute();
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
}

View File

@@ -15,7 +15,7 @@ namespace OpenWifi {
"RevocationDate BIGINT "
*/
bool Storage::AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) {
bool Storage::AddToken(std::string &UserID, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
@@ -29,7 +29,7 @@ namespace OpenWifi {
Poco::Data::Keywords::use(Token),
Poco::Data::Keywords::use(RefreshToken),
Poco::Data::Keywords::use(TokenType),
Poco::Data::Keywords::use(UserName),
Poco::Data::Keywords::use(UserID),
Poco::Data::Keywords::use(Now),
Poco::Data::Keywords::use(Expires),
Poco::Data::Keywords::use(TimeOut),
@@ -42,29 +42,24 @@ namespace OpenWifi {
return false;
}
bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo) {
bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
uint32_t RevocationDate = 0 ;
std::string St2{"SELECT " + AllTokensValuesForSelect + " From Tokens WHERE Token=?"};
RevocationDate = 0 ;
std::string St2{"SELECT " + AllTokensFieldsForSelect + " From Tokens WHERE Token=?"};
Select << ConvertParams(St2),
Poco::Data::Keywords::into(UInfo.webtoken.access_token_),
Poco::Data::Keywords::into(UInfo.webtoken.refresh_token_),
Poco::Data::Keywords::into(UInfo.webtoken.token_type_),
Poco::Data::Keywords::into(UInfo.userinfo.email),
Poco::Data::Keywords::into(UInfo.userinfo.Id),
Poco::Data::Keywords::into(UInfo.webtoken.created_),
Poco::Data::Keywords::into(UInfo.webtoken.expires_in_),
Poco::Data::Keywords::into(UInfo.webtoken.idle_timeout_),
Poco::Data::Keywords::into(RevocationDate),
Poco::Data::Keywords::use(Token);
Select.execute();
if(RevocationDate>0)
return false;
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
@@ -116,15 +111,15 @@ namespace OpenWifi {
return false;
}
bool Storage::CleanRevokedTokens(uint64_t Oldest) {
bool Storage::CleanExpiredTokens() {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
uint64_t Now = std::time(nullptr);
std::string St2{"DELETE From Tokens WHERE Created <= ?"};
std::string St2{"DELETE From Tokens WHERE (Created+Expires) <= ?"};
Delete << ConvertParams(St2),
Poco::Data::Keywords::use(Oldest);
Poco::Data::Keywords::use(Now);
Delete.execute();
return true;
} catch (const Poco::Exception &E) {
@@ -133,14 +128,14 @@ namespace OpenWifi {
return false;
}
bool Storage::RevokeAllTokens(std::string & username) {
bool Storage::RevokeAllTokens(std::string & UserId) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
std::string St2{"DELETE From Tokens WHERE Username=?"};
Delete << ConvertParams(St2),
Poco::Data::Keywords::use(username);
Poco::Data::Keywords::use(UserId);
Delete.execute();
return true;
} catch(const Poco::Exception &E) {

View File

@@ -80,7 +80,23 @@ namespace OpenWifi {
return true;
}
std::string DefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"};
std::string OldDefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"};
std::string NewDefaultUseridStockUUID{"11111111-0000-0000-6666-999999999999"};
void Storage::ReplaceOldDefaultUUID() {
try {
Poco::Data::Session Sess = Pool_->get();
std::string St1{"update users set id=? where id=?"};
Poco::Data::Statement Update(Sess);
Update << ConvertParams(St1),
Poco::Data::Keywords::use(NewDefaultUseridStockUUID),
Poco::Data::Keywords::use(OldDefaultUseridStockUUID);
Update.execute();
} catch (...) {
}
}
// if we do not find a default user, then we need to create one based on the
// property file. We must set its flag to "must change password", this user has root privilege.
@@ -89,12 +105,13 @@ namespace OpenWifi {
SecurityObjects::UserInfo U;
bool DefaultUserCreated = false;
ReplaceOldDefaultUUID();
AppServiceRegistry().Get("defaultusercreated",DefaultUserCreated);
if(!GetUserById(DefaultUseridStockUUID,U) && !DefaultUserCreated) {
if(!GetUserById(NewDefaultUseridStockUUID,U) && !DefaultUserCreated) {
U.currentPassword = MicroService::instance().ConfigGetString("authentication.default.password","");
U.lastPasswords.push_back(U.currentPassword);
U.email = MicroService::instance().ConfigGetString("authentication.default.username","");
U.Id = DefaultUseridStockUUID;
U.Id = NewDefaultUseridStockUUID;
U.userRole = SecurityObjects::ROOT;
U.creationDate = std::time(nullptr);
U.validated = true;
@@ -132,7 +149,7 @@ namespace OpenWifi {
return false;
if(!PasswordHashedAlready) {
NewUser.Id = MicroService::instance().CreateUUID();
NewUser.Id = MicroService::CreateUUID();
NewUser.creationDate = std::time(nullptr);
}

View File

@@ -15,23 +15,23 @@ namespace OpenWifi {
"description varchar,"
"avatar varchar,"
"email varchar,"
"validated int,"
"validated boolean,"
"validationEmail varchar,"
"validationDate bigint,"
"creationDate bigint,"
"validationURI varchar,"
"changePassword int,"
"changePassword boolean,"
"lastLogin bigint,"
"currentLoginURI varchar,"
"lastPasswordChange bigint,"
"lastEmailCheck bigint,"
"waitingForEmailCheck int,"
"waitingForEmailCheck boolean,"
"locale varchar,"
"notes text,"
"location varchar,"
"owner varchar,"
"suspended int,"
"blackListed int,"
"suspended boolean,"
"blackListed boolean,"
"userRole varchar,"
"userTypeProprietaryInfo text,"
"securityPolicy text,"
@@ -111,23 +111,23 @@ namespace OpenWifi {
std::string, // description;
std::string, // avatar;
std::string, // email;
uint64_t, // bool validated = false;
bool, // bool validated = false;
std::string, // validationEmail;
uint64_t, // validationDate = 0;
uint64_t, // creationDate = 0;
std::string, // validationURI;
uint64_t, // bool changePassword = true;
bool, // bool changePassword = true;
uint64_t, // lastLogin = 0;
std::string, // currentLoginURI;
uint64_t, // lastPasswordChange = 0;
uint64_t, // lastEmailCheck = 0;
uint64_t, // bool waitingForEmailCheck = false;
bool, // bool waitingForEmailCheck = false;
std::string, // locale;
std::string, // notes;
std::string, // location;
std::string, // owner;
uint64_t, // bool suspended = false;
uint64_t, // bool blackListed = false;
bool, // bool suspended = false;
bool, // bool blackListed = false;
std::string, // userRole;
std::string, // userTypeProprietaryInfo;
std::string, // securityPolicy;

View File

@@ -35,7 +35,7 @@ fi
token=""
result_file=result.json
username="tip@ucentral.com"
password="openwifi"
password="Snoopy99!!!"
#username="stephb@incognito.com"
#password="Snoopy98!"
browser_list=(firefox sensible-browser xdg-open w3m links links2 lynx youtube-dl)

26
wait-for-postgres.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
# wait-for-postgres.sh
set -e
host="$1"
shift
export PGUSER=$(grep 'storage.type.postgresql.username' $OWSEC_CONFIG/owsec.properties | awk -F '= ' '{print $2}')
export PGPASSWORD=$(grep 'storage.type.postgresql.password' $OWSEC_CONFIG/owsec.properties | awk -F '= ' '{print $2}')
until psql -h "$host" -c '\q'; do
>&2 echo "Postgres is unavailable - sleeping"
sleep 1
done
>&2 echo "Postgres is up - executing command"
if [ "$1" = '/openwifi/owsec' -a "$(id -u)" = '0' ]; then
if [ "$RUN_CHOWN" = 'true' ]; then
chown -R "$OWSEC_USER": "$OWSEC_ROOT" "$OWSEC_CONFIG"
fi
exec su-exec "$OWSEC_USER" "$@"
fi
exec "$@"