Compare commits

..

1 Commits

Author SHA1 Message Date
Dmitry Dunaev
d938c26329 Chg: helm set v2.0.0-RC1 image 2021-10-01 14:44:20 +03:00
143 changed files with 6418 additions and 9029 deletions

View File

@@ -84,7 +84,7 @@ jobs:
env:
OWGW_TAG: ${{ github.sha }}
run: |
docker-compose up -d
docker-compose -f docker-compose.yml -f docker-compose.selfsigned.yml --env-file .env.selfsigned up -d
- name: Wait for OWSec to be alive and kicking
run: |
@@ -116,8 +116,8 @@ jobs:
working-directory: ./wlan-cloud-ucentral-deploy/docker-compose
if: always()
run: |
docker-compose ps -a
docker-compose logs
docker-compose -f docker-compose.yml -f docker-compose.selfsigned.yml --env-file .env.selfsigned ps -a
docker-compose -f docker-compose.yml -f docker-compose.selfsigned.yml --env-file .env.selfsigned logs
# disable until repo is public
#- name: export Docker image

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.13)
project(owgw VERSION 2.4.0)
project(owgw VERSION 2.2.0)
set(CMAKE_CXX_STANDARD 17)
@@ -29,20 +29,7 @@ else()
set(BUILD_NUM 1)
file(WRITE build ${BUILD_NUM})
endif()
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}")
add_definitions(-DTIP_GATEWAY_SERVICE="1")
set(Boost_USE_STATIC_LIBS OFF)
@@ -65,65 +52,58 @@ endif()
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( owgw
build
src/ow_version.h.in
src/framework/CountryCodes.h
src/framework/KafkaTopics.h
src/framework/MicroService.h
src/framework/OpenWifiTypes.h
src/framework/orm.h
src/framework/RESTAPI_errors.h
src/framework/RESTAPI_protocol.h
src/framework/StorageClass.h
src/framework/uCentral_Protocol.h
src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp
src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp
src/RESTAPI/RESTAPI_devices_handler.cpp src/RESTAPI/RESTAPI_devices_handler.h
src/RESTAPI/RESTAPI_device_handler.cpp src/RESTAPI/RESTAPI_device_handler.h
src/RESTAPI/RESTAPI_device_commandHandler.cpp src/RESTAPI/RESTAPI_device_commandHandler.h
src/RESTAPI/RESTAPI_default_configuration.cpp
src/RESTAPI/RESTAPI_default_configuration.h src/RESTAPI/RESTAPI_default_configurations.cpp src/RESTAPI/RESTAPI_default_configurations.h
src/RESTAPI/RESTAPI_commands.cpp src/RESTAPI/RESTAPI_commands.h
src/RESTAPI/RESTAPI_command.cpp src/RESTAPI/RESTAPI_command.h
src/RESTAPI/RESTAPI_file.cpp src/RESTAPI/RESTAPI_file.h
src/RESTAPI/RESTAPI_blacklist.cpp src/RESTAPI/RESTAPI_blacklist.h
src/RESTAPI/RESTAPI_ouis.cpp src/RESTAPI/RESTAPI_ouis.h
src/RESTAPI/RESTAPI_blacklist_list.cpp src/RESTAPI/RESTAPI_blacklist_list.h
src/RESTAPI/RESTAPI_capabilities_handler.cpp src/RESTAPI/RESTAPI_capabilities_handler.h
src/RESTAPI/RESTAPI_RPC.cpp src/RESTAPI/RESTAPI_RPC.h
src/RESTAPI/RESTAPI_deviceDashboardHandler.cpp src/RESTAPI/RESTAPI_deviceDashboardHandler.h
src/RESTAPI/RESTAPI_TelemetryWebSocket.cpp src/RESTAPI/RESTAPI_TelemetryWebSocket.h
src/RESTAPI/RESTAPI_webSocketServer.cpp src/RESTAPI/RESTAPI_webSocketServer.h
src/storage/storage_blacklist.cpp src/storage/storage_tables.cpp src/storage/storage_logs.cpp
src/storage/storage_command.cpp src/storage/storage_healthcheck.cpp src/storage/storage_statistics.cpp
src/storage/storage_device.cpp src/storage/storage_capabilities.cpp src/storage/storage_defconfig.cpp
src/storage/storage_tables.cpp
src/APIServers.cpp
src/Daemon.cpp src/Daemon.h
src/StateProcessor.cpp src/StateProcessor.h
src/storage/storage_lifetime_stats.cpp
src/WebSocketServer.cpp src/WebSocketServer.h
src/StorageService.cpp src/StorageService.h
src/DeviceRegistry.cpp src/DeviceRegistry.h
src/CommandManager.cpp src/CommandManager.h
src/CentralConfig.cpp src/CentralConfig.h
src/FileUploader.cpp src/FileUploader.h
src/OUIServer.cpp src/OUIServer.h
src/StorageArchiver.cpp src/StorageArchiver.h
src/Dashboard.cpp src/Dashboard.h
src/SerialNumberCache.cpp src/SerialNumberCache.h
src/TelemetryStream.cpp src/TelemetryStream.h
src/framework/ConfigurationValidator.cpp src/framework/ConfigurationValidator.h
src/ConfigurationCache.cpp src/ConfigurationCache.h
)
build
src/Daemon.cpp src/Daemon.h
src/RESTAPI_server.cpp src/RESTAPI_server.h
src/WebSocketServer.cpp src/WebSocketServer.h
src/SubSystemServer.cpp src/SubSystemServer.h
src/StorageService.cpp src/StorageService.h
src/RESTAPI_SecurityObjects.cpp src/RESTAPI_SecurityObjects.h
src/DeviceRegistry.cpp src/DeviceRegistry.h
src/RESTAPI_devices_handler.cpp src/RESTAPI_devices_handler.h
src/RESTAPI_device_handler.cpp src/RESTAPI_device_handler.h
src/RESTAPI_handler.cpp src/RESTAPI_handler.h
src/RESTAPI_device_commandHandler.cpp src/RESTAPI_device_commandHandler.h
src/RESTAPI_GWobjects.h src/RESTAPI_GWobjects.cpp
src/CentralConfig.cpp src/CentralConfig.h
src/RESTAPI_default_configuration.cpp
src/RESTAPI_InternalServer.cpp src/RESTAPI_InternalServer.h
src/RESTAPI_default_configuration.h src/RESTAPI_default_configurations.cpp src/RESTAPI_default_configurations.h
src/RESTAPI_commands.cpp src/RESTAPI_commands.h
src/CommandManager.cpp src/CommandManager.h
src/RESTAPI_command.cpp src/RESTAPI_command.h
src/FileUploader.cpp src/FileUploader.h
src/RESTAPI_file.cpp src/RESTAPI_file.h
src/RESTAPI_system_command.cpp src/RESTAPI_system_command.h
src/RESTAPI_BlackList.cpp src/RESTAPI_BlackList.h
src/Utils.h src/Utils.cpp src/storage_blacklist.cpp
src/storage_command.cpp src/storage_healthcheck.cpp src/storage_statistics.cpp src/storage_logs.cpp
src/storage_device.cpp src/storage_capabilities.cpp src/storage_defconfig.cpp
src/storage_tables.cpp
src/storage_setup.cpp
src/StateProcessor.cpp src/StateProcessor.h
src/storage_lifetime_stats.cpp src/uCentralProtocol.h src/RESTAPI_protocol.h
src/ALBHealthCheckServer.h src/Kafka_topics.h
src/OUIServer.cpp src/OUIServer.h
src/RESTAPI_ouis.cpp src/RESTAPI_ouis.h
src/MicroService.cpp src/MicroService.h
src/RESTAPI_RPC.cpp src/RESTAPI_RPC.h
src/AuthClient.cpp src/AuthClient.h
src/OpenAPIRequest.cpp src/OpenAPIRequest.h
src/RESTAPI_utils.h src/RESTAPI_utils.cpp
src/StorageArchiver.cpp src/StorageArchiver.h
src/Dashboard.cpp src/Dashboard.h
src/RESTAPI_deviceDashboardHandler.cpp src/RESTAPI_deviceDashboardHandler.h
src/SerialNumberCache.cpp src/SerialNumberCache.h
src/RESTAPI_webSocketServer.cpp src/RESTAPI_webSocketServer.h
src/OpenWifiTypes.h src/TelemetryStream.cpp src/TelemetryStream.h
src/RESTAPI_GenericServer.cpp src/RESTAPI_GenericServer.h
src/RESTAPI_errors.h src/RESTAPI_TelemetryWebSocket.cpp src/RESTAPI_TelemetryWebSocket.h
src/ConfigurationValidator.cpp src/ConfigurationValidator.h src/ConfigurationCache.cpp src/ConfigurationCache.h)
if(NOT SMALL_BUILD)
target_sources(owgw PUBLIC src/KafkaManager.cpp src/KafkaManager.h)
endif()
INSTALL(TARGETS owgw

View File

@@ -38,7 +38,6 @@ RUN make install
ADD CMakeLists.txt build /owgw/
ADD cmake /owgw/cmake
ADD src /owgw/src
ADD .git /owgw/.git
WORKDIR /owgw
RUN mkdir cmake-build
@@ -58,19 +57,16 @@ RUN addgroup -S "$OWGW_USER" && \
RUN mkdir /openwifi
RUN mkdir -p "$OWGW_ROOT" "$OWGW_CONFIG" && \
chown "$OWGW_USER": "$OWGW_ROOT" "$OWGW_CONFIG"
RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates bash jq curl postgresql-client
RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates
COPY --from=builder /owgw/cmake-build/owgw /openwifi/owgw
COPY --from=builder /cppkafka/cmake-build/src/lib/* /lib/
COPY --from=builder /poco/cmake-build/lib/* /lib/
COPY owgw.properties.tmpl /
COPY owgw.properties.tmpl ${OWGW_CONFIG}/
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
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
EXPOSE 15002 16002 16003 17002 16102

View File

@@ -2,7 +2,7 @@
This document will describe how the API is built and how to use it.
## Where is the OpenAPI.
This uses OpenAPI definition 3.0 and can be found [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
This uses OpenAPI definition 3.0 and can be found [here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/ucentral.yaml).
All endpoints begin with `/api/v1`.
## The flow

View File

@@ -155,22 +155,6 @@ which version it is running. The Controller may decide to send the device a newe
}
```
#### Recovery Event
Device may decide it has to do into recovery mode. This event should be used.
```
{ "jsonrpc" : "2.0" ,
"method" : "recovery" ,
"params" : {
"serial" : <serial number> ,
"uuid" : <the UUID of the configuration that generated the crash log>,
"firmware: <the string describing the current firmware>,
"reboot" : true/false (shoudld the device be instructed to reboot after loggin the information),
"loglines" : [ an array of strings representing the logs from the log file ]
}
}
```
### Controller commands
Most controller commands include a `when` member. This is a UTC clock time asking the AP
to perform the command at that time. This is a suggestion only. The AP may ignore this

View File

@@ -26,9 +26,9 @@ Poco may take several minutes depending on the platform you are building on.
### Ubuntu
These instructions have proven to work on Ubuntu 20.4.
```
sudo apt install git cmake g++ libssl-dev libmariadb-dev
sudo apt install git cmake g++ libssl-dev libmariadb-dev unixodbc-dev
sudo apt install libpq-dev libaprutil1-dev apache2-dev libboost-all-dev
sudo apt install librdkafka-dev libmysqlclient-dev default-libmysqlclient-dev
sudo apt install librdkafka-dev liblua5.3-dev libmysqlclient-dev
git clone https://github.com/stephb9959/poco
cd poco
@@ -502,7 +502,7 @@ environment variables. Here is a sample configuration:
The communication protocol between the device and the controller is detailed in this [document](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/PROTOCOL.md).
## OpenAPI
The service supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/owgw.yaml).
The service supports an OpenAPI REST based interface for management. You can find the [definition here](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/openapi/ucentral/ucentral.yaml).
And here is [how to use it](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/main/OPENAPI.md)
## Using the API

2
build
View File

@@ -1 +1 @@
44
74

View File

@@ -38,7 +38,6 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWGW_CONFIG"/owgw.properties ]]; the
SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17002"} \
SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16002"} \
SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
SIMULATORID=${SIMULATORID:-""} \
RTTY_ENABLED=${RTTY_ENABLED:-"false"} \
RTTY_SERVER=${RTTY_SERVER:-"localhost"} \
RTTY_PORT=${RTTY_PORT:-"5912"} \
@@ -58,7 +57,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWGW_CONFIG"/owgw.properties ]]; the
STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owgw"} \
STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owgw"} \
STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \
envsubst < /owgw.properties.tmpl > $OWGW_CONFIG/owgw.properties
envsubst < $OWGW_CONFIG/owgw.properties.tmpl > $OWGW_CONFIG/owgw.properties
fi
if [ "$1" = '/openwifi/owgw' -a "$(id -u)" = '0' ]; then

12
helm/Chart.lock Normal file
View File

@@ -0,0 +1,12 @@
dependencies:
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 10.9.2
- name: mysql
repository: https://charts.bitnami.com/bitnami
version: 8.8.3
- name: mariadb
repository: https://charts.bitnami.com/bitnami
version: 9.4.2
digest: sha256:1fdae7cbea906e41dccd8618ff9e2c68d0c684724ae27c79a12bb6089968df5c
generated: "2021-08-17T12:18:40.341427893+03:00"

View File

@@ -5,14 +5,14 @@ name: owgw
version: 0.1.0
dependencies:
- name: postgresql
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
repository: https://charts.bitnami.com/bitnami
version: 10.9.2
condition: postgresql.enabled
- name: mysql
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
repository: https://charts.bitnami.com/bitnami
version: 8.8.3
condition: mysql.enabled
- name: mariadb
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
repository: https://charts.bitnami.com/bitnami
version: 9.4.2
condition: mariadb.enabled

View File

@@ -20,7 +20,7 @@ Currently this chart is not assembled in charts archives, so [helm-git](https://
To install the chart with the release name `my-release`:
```bash
$ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw@helm/owgw-0.1.0.tgz?ref=master
$ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralgw@helm?ref=master
```
The command deploys the Gateway on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation.

View File

@@ -30,13 +30,3 @@ Create chart name and version as used by the chart label.
{{- define "owgw.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "owgw.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

@@ -24,12 +24,6 @@ spec:
metadata:
annotations:
checksum/config: {{ include "owgw.config" . | sha256sum }}
{{- if .Values.podSecurityPolicy.enabled }}
kubernetes.io/psp: {{ include "owgw.fullname" . }}-{{ .Release.Namespace }}-owgw-unsafe-sysctl
{{- end }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
app.kubernetes.io/name: {{ include "owgw.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}

View File

@@ -2,7 +2,7 @@
{{- range $ingress, $ingressValue := .Values.ingresses }}
{{- if $ingressValue.enabled }}
---
apiVersion: {{ include "owgw.ingress.apiVersion" $root }}
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ include "owgw.fullname" $root }}-{{ $ingress }}
@@ -36,23 +36,9 @@ 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 "owgw.fullname" $root }}-{{ .serviceName }}
port:
{{- if kindIs "string" .servicePort }}
name: {{ .servicePort }}
{{- else }}
number: {{ .servicePort }}
{{- end }}
{{- else }}
serviceName: {{ include "owgw.fullname" $root }}-{{ .serviceName }}
servicePort: {{ .servicePort }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -1,28 +0,0 @@
{{- if .Values.podSecurityPolicy.enabled }}
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: {{ include "owgw.fullname" . }}-{{ .Release.Namespace }}-owgw-unsafe-sysctl
labels:
app.kubernetes.io/name: {{ include "owgw.name" . }}
helm.sh/chart: {{ include "owgw.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
allowedUnsafeSysctls:
{{- range $unsafeSysctl := .Values.securityContext.sysctls }}
- {{ $unsafeSysctl.name }}
{{- end }}
privileged: false
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: RunAsAny
fsGroup:
rule: RunAsAny
volumes:
- '*'
{{- end }}

View File

@@ -1,16 +0,0 @@
{{- if .Values.podSecurityPolicy.enabled }}
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: {{ include "owgw.fullname" . }}-owgw-use-unsafe-sysctl
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
verbs:
- use
resourceNames:
- {{ include "owgw.fullname" . }}-{{ .Release.Namespace }}-owgw-unsafe-sysctl
{{- end }}

View File

@@ -1,15 +0,0 @@
{{- if .Values.podSecurityPolicy.enabled }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: {{ include "owgw.fullname" . }}-owgw-use-unsafe-sysctl-to-default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: {{ include "owgw.fullname" . }}-owgw-use-unsafe-sysctl
subjects:
- kind: ServiceAccount
name: default
namespace: {{ .Release.Namespace }}
{{- end }}

View File

@@ -8,7 +8,7 @@ fullnameOverride: ""
images:
owgw:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owgw
tag: v2.4.2
tag: v2.2.0-RC1
pullPolicy: Always
# regcred:
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
@@ -43,10 +43,9 @@ checks:
path: /
port: 16102
readiness:
exec:
command:
- /readiness_check
failureThreshold: 1
httpGet:
path: /
port: 16102
ingresses:
restapi:
@@ -58,7 +57,6 @@ ingresses:
- restapi.chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
serviceName: owgw
servicePort: restapi
fileuploader:
@@ -70,7 +68,6 @@ ingresses:
- fileuploader.chart-example.local
paths:
- path: /
pathType: ImplementationSpecific
serviceName: owgw
servicePort: fileuploader
@@ -114,17 +111,6 @@ resources: {}
securityContext:
fsGroup: 101
# Usage of unsafe sysctls requires multiple things:
# - allow these unsafe sysctls on kubelet level (by adding --allowed-unsafe-sysctls flag)
# - enabling addition of PodSecurityContext setting podSecurityPolicy.enabled to "true" below
# - uncommenting parameters below
#sysctls:
#- name: net.ipv4.tcp_keepalive_intvl
# value: "5"
#- name: net.ipv4.tcp_keepalive_probes
# value: "2"
#- name: net.ipv4.tcp_keepalive_time
# value: "45"
nodeSelector: {}
@@ -132,11 +118,6 @@ tolerations: []
affinity: {}
podAnnotations: {}
podSecurityPolicy:
enabled: false
persistence:
enabled: true
# storageClassName: "-"
@@ -149,16 +130,8 @@ persistence:
public_env_variables:
OWGW_ROOT: /owgw-data
OWGW_CONFIG: /owgw-data
# Environment variables required for the readiness checks using script
FLAGS: "-s --connect-timeout 3"
# NOTE in order for readiness check to use system info you need to set READINESS_METHOD to "systeminfo" and set OWSEC to the OWSEC's REST API endpoint
#READINESS_METHOD: systeminfo
#OWSEC: gw-qa01.cicd.lab.wlan.tip.build:16001
secret_env_variables:
# NOTE in order for readiness check to use system info method you need to override these values to the real OWSEC credentials
OWSEC_USERNAME: tip@ucentral.com
OWSEC_PASSWORD: openwifi
secret_env_variables: {}
configProperties:
# -> Public part

View File

@@ -51,16 +51,6 @@ components:
properties:
ErrorCode:
type: integer
enum:
- 0 # Success
- 1 # PASSWORD_CHANGE_REQUIRED,
- 2 # INVALID_CREDENTIALS,
- 3 # PASSWORD_ALREADY_USED,
- 4 # USERNAME_PENDING_VERIFICATION,
- 5 # PASSWORD_INVALID,
- 6 # INTERNAL_ERROR,
- 7 # ACCESS_DENIED,
- 8 # INVALID_TOKEN
ErrorDetails:
type: string
ErrorDescription:
@@ -657,10 +647,6 @@ components:
type: array
items:
type: string
enum:
- dhcp-snooping
- wire-frames
- state
uuid:
type: string
example:
@@ -858,7 +844,7 @@ components:
items:
type: string
SystemGetSubSystemNamesResult:
SystemGetSubSystemNemesResult:
type: object
properties:
taglist:
@@ -979,22 +965,6 @@ components:
password:
type: string
CapabilitiesModel:
type: object
properties:
deviceType:
type: string
capabilities:
type: string
CapabilitiesModelList:
type: object
properties:
devices:
type: array
items:
$ref: '#/components/schemas/CapabilitiesModel'
paths:
/devices:
get:
@@ -1152,7 +1122,7 @@ paths:
get:
tags:
- Commands
summary: Returns a specific command.
summary: Returns a specific command
description: Returns a specific command
operationId: getACommandDetails
parameters:
@@ -1177,7 +1147,7 @@ paths:
delete:
tags:
- Commands
summary: Delete a specific command.
summary: Delete a specific command
description: Delete a specific command
operationId: deleteACommand
parameters:
@@ -1203,8 +1173,8 @@ paths:
get:
tags:
- Configurations
summary: Retrieve the lists of all default configurations.
description: Retrieve the lists of all default configurations.
summary: Retrieve the lists of all default configurations
description: Retrieve the lists of all default configurations
operationId: getDefaultConfigurations
responses:
@@ -1223,8 +1193,8 @@ paths:
get:
tags:
- Configurations
summary: Retrieve a default configuration.
description: Retrieve a default configuration.
summary: Retrieve a default configuration
description: Retrieve a default configuration
operationId: getDefaultConfiguration
parameters:
- in: path
@@ -1247,8 +1217,8 @@ paths:
post:
tags:
- Configurations
summary: Create a default configuration.
description: Create a default configuration.
summary: Create a default configuration
description: Create a default configuration
operationId: createDefaultConfiguration
parameters:
- in: path
@@ -1320,7 +1290,7 @@ paths:
get:
tags:
- Devices
summary: Retrieve information for a single device.
summary: Retrieve information for a single device
description: Retrieve all the inforamtion about a single device
operationId: getDeviceInformation
parameters:
@@ -1344,7 +1314,7 @@ paths:
post:
tags:
- Devices
summary: Create a new device.
summary: Creating a new device
operationId: createNewDevice
parameters:
- in: path
@@ -1378,7 +1348,7 @@ paths:
put:
tags:
- Devices
summary: Update a device.
summary: Updating a new device
operationId: updateNewDevice
parameters:
- in: path
@@ -1407,7 +1377,7 @@ paths:
delete:
tags:
- Devices
summary: Delete a single device.
summary: Deleting a single device
operationId: deleteDevice
parameters:
- in: path
@@ -1484,7 +1454,7 @@ paths:
delete:
tags:
- Commands
summary: Delete some device logs.
summary: Delete some device logs
operationId: deleteDeviceLogs
parameters:
- in: path
@@ -1526,7 +1496,7 @@ paths:
get:
tags:
- Commands
summary: Get the latest health checks for a given device.
summary: Get the latest health checks for a given device
operationId: getDeviceHealthChecks
parameters:
- in: path
@@ -1573,7 +1543,7 @@ paths:
responses:
200:
description: Array of device health checks for this device
description: Array of device health checks for this device
content:
application/json:
schema:
@@ -1586,7 +1556,7 @@ paths:
delete:
tags:
- Commands
summary: Delete some device health checks.
summary: Delete some device health checks
operationId: deleteDeviceHealthChecks
parameters:
- in: path
@@ -1623,7 +1593,7 @@ paths:
get:
tags:
- Commands
summary: Get the latest capabilities for a given device.
summary: Get the latest capabilities for a given device
operationId: getDeviceCapabilities
parameters:
- in: path
@@ -1646,7 +1616,7 @@ paths:
delete:
tags:
- Commands
summary: Delete the capabilities for a given device.
summary: Delete the capabilities for a given device
operationId: deleteDeviceCapabilities
parameters:
- in: path
@@ -1670,7 +1640,7 @@ paths:
get:
tags:
- Commands
summary: Get the latest statistics for a given device.
summary: Get the latest statistics for a given device
operationId: getDeviceStats
parameters:
- in: path
@@ -1738,7 +1708,7 @@ paths:
delete:
tags:
- Commands
summary: Get the latest statistics for a given device.
summary: Get the latest statistics for a given device
operationId: deleteDeviceStats
parameters:
- in: path
@@ -1775,7 +1745,7 @@ paths:
get:
tags:
- Commands
summary: Get the latest status for a given device.
summary: Get the latest status for a given device
operationId: getDeviceStatus
parameters:
- in: path
@@ -1825,7 +1795,7 @@ paths:
post:
tags:
- Commands
summary: Configure a device.
summary: Configura a device
operationId: updateConfigurationForADevice
parameters:
- in: path
@@ -1851,7 +1821,7 @@ paths:
post:
tags:
- Commands
summary: Upgrade a device.
summary: Upgrade a device
operationId: UpgradeDeviceFirmware
parameters:
- in: path
@@ -1877,7 +1847,7 @@ paths:
post:
tags:
- Commands
summary: Reboot a device.
summary: Upgrade a device
operationId: rebootDevice
parameters:
- in: path
@@ -1903,7 +1873,7 @@ paths:
post:
tags:
- Commands
summary: Factory reset a device.
summary: Factory reset a device a device
operationId: factoryReset
parameters:
- in: path
@@ -1929,7 +1899,7 @@ paths:
post:
tags:
- Commands
summary: Blink the LEDs on a device.
summary: Blink the LEDs on a device
operationId: ledsRequest
parameters:
- in: path
@@ -1955,7 +1925,7 @@ paths:
post:
tags:
- Commands
summary: Launch a trace for a device.
summary: Launch a trace for a device
operationId: traceRequest
parameters:
- in: path
@@ -1981,7 +1951,7 @@ paths:
post:
tags:
- Commands
summary: Launch a wifi scan for a device.
summary: Launch a wifi scan for a device
operationId: wifiscanRequest
parameters:
- in: path
@@ -2033,7 +2003,7 @@ paths:
post:
tags:
- Commands
summary: Request a list of queued events.
summary: Request a list of queued events
operationId: eventQueueRequest
parameters:
- in: path
@@ -2059,7 +2029,7 @@ paths:
post:
tags:
- Commands
summary: Request a telemetry stream.
summary: Request a telemetry stream
operationId: eventTelemetryStreamRequest
parameters:
- in: path
@@ -2086,7 +2056,7 @@ paths:
tags:
- OUIs
operationId: getOUIs
summary: Get a list of OUIs.
summary: Get a list of OUIs
parameters:
- in: query
name: macList
@@ -2105,7 +2075,7 @@ paths:
get:
tags:
- Commands
summary: Get the rtty parameters to initiate a session.
summary: Get the rtty parameters to initiate a session
operationId: getRttySessionInfo
parameters:
- in: path
@@ -2129,7 +2099,7 @@ paths:
get:
tags:
- Files
summary: Get a file from the upload directory.
summary: Get a file from the upload directory
operationId: getUploadFile
parameters:
- in: path
@@ -2159,7 +2129,7 @@ paths:
delete:
tags:
- Files
summary: Delete a file from the upload directory.
summary: Delete a file from the upload directory
operationId: deleteUploadFidelete
parameters:
- in: path
@@ -2186,7 +2156,7 @@ paths:
tags:
- Blacklist
summary: Returns a list blacklisted devices.
description: Get a list of blacklisted devices.
description: Get a list of blacklisteddevices.
operationId: getBlacklistDeviceList
parameters:
- in: query
@@ -2219,76 +2189,17 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/blacklist/{serialNumber}:
get:
tags:
- Blacklist
summary: Returns a blacklist entry.
description: Get a list of blacklisted devices.
operationId: getBlacklistDevice
parameters:
- in: path
description: Pagination start (starts at 1. If not specified, 1 is assumed)
name: serialNumber
schema:
type: string
required: true
responses:
200:
description: List blacklisted devices
content:
application/json:
schema:
$ref: '#/components/schemas/BlackDeviceInfo'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
post:
tags:
- Blacklist
summary: Create to the blacklist.
operationId: createBlackListDevice
parameters:
- in: path
description: Pagination start (starts at 1. If not specified, 1 is assumed)
name: serialNumber
schema:
type: string
required: true
requestBody:
description: Add blacklisted device
content:
application/json:
schema:
$ref: '#/components/schemas/BlackDeviceInfo'
responses:
200:
$ref: '#/components/responses/Success'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
put:
tags:
- Blacklist
summary: Modify to the blacklist.
operationId: modifyBlackList
parameters:
- in: path
description: Pagination start (starts at 1. If not specified, 1 is assumed)
name: serialNumber
schema:
type: string
required: true
summary: Adds to the blacklist
operationId: addToBlackList
requestBody:
description: Add blacklisted devices
content:
application/json:
schema:
$ref: '#/components/schemas/BlackDeviceInfo'
$ref: '#/components/schemas/BlackDeviceList'
responses:
200:
$ref: '#/components/responses/Success'
@@ -2300,10 +2211,11 @@ paths:
delete:
tags:
- Blacklist
summary: Delete from the blacklist.
summary: Delete from the blacklist
operationId: deleteFromBlackList
parameters:
- in: path
- in: query
description: Serial Number
name: serialNumber
schema:
type: string
@@ -2316,29 +2228,11 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/capabilities:
get:
tags:
- Devices
summary: Get the list of device types and capabilities.
operationId: getCapabilitiesList
responses:
200:
description: Successful command execution
content:
application/json:
schema:
$ref: '#/components/schemas/CapabilitiesModelList'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/deviceDashboard:
get:
tags:
- Dashboards
summary: Get the last version of the dashboard.
summary: Get the last version of the dashboard
operationId: getDeviceDashboard
responses:
200:
@@ -2357,7 +2251,7 @@ paths:
post:
tags:
- System Commands
summary: Perform some system wide commands.
summary: Perform some systeme wide commands
operationId: systemCommand
requestBody:
description: Command details
@@ -2372,14 +2266,14 @@ paths:
- $ref: '#/components/schemas/SystemCommandGetSubsystemNames'
responses:
200:
description: Successful command execution
description: Successfull command execution
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/SystemGetLogLevelsResult'
- $ref: '#/components/schemas/SystemCommandGetLogLevelNamesResult'
- $ref: '#/components/schemas/SystemGetSubSystemNamesResult'
- $ref: '#/components/schemas/SystemGetSubSystemNemesResult'
403:
$ref: '#/components/responses/Unauthorized'
404:
@@ -2401,7 +2295,7 @@ paths:
responses:
200:
description: Successful command execution
description: Successfull command execution
content:
application/json:
schema:
@@ -2410,4 +2304,4 @@ paths:
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
$ref: '#/components/responses/NotFound'

View File

@@ -76,7 +76,6 @@ openwifi.devicetypes.1 = SWITCH:edgecore_ecs4100-12ph
openwifi.devicetypes.2 = IOT:esp32
oui.download.uri = https://linuxnet.ca/ieee/oui.txt
firmware.autoupdate.policy.default = auto
simulatorid = ${SIMULATORID}
#
# rtty
@@ -162,7 +161,7 @@ logging.channels.c1.formatter = f1
# This is where the logs will be written. This path MUST exist
logging.channels.c2.class = FileChannel
logging.channels.c2.path = $OWGW_ROOT/logs/log
logging.channels.c2.path = $UCENTRALGW_ROOT/logs/log
logging.channels.c2.formatter.class = PatternFormatter
logging.channels.c2.formatter.pattern = %Y-%m-%d %H:%M:%S %s: [%p] %t
logging.channels.c2.rotation = 20 M

View File

@@ -1,65 +0,0 @@
#!/bin/bash
set -e
if [[ "$(which jq)" == "" ]]
then
echo "You need the package jq installed to use this script."
exit 1
fi
if [[ "$(which curl)" == "" ]]
then
echo "You need the package curl installed to use this script."
exit 1
fi
if [[ "${OWSEC}" == "" ]]
then
echo "You must set the variable OWSEC in order to use this script. Something like"
echo "OWSEC=security.isp.com:16001"
exit 1
fi
if [[ "${OWSEC_USERNAME}" == "" ]]
then
echo "You must set the variable OWSEC_USERNAME in order to use this script. Something like"
echo "OWSEC_USERNAME=tip@ucentral.com"
exit 1
fi
if [[ "${OWSEC_PASSWORD}" == "" ]]
then
echo "You must set the variable OWSEC_PASSWORD in order to use this script. Something like"
echo "OWSEC_PASSWORD=openwifi"
exit 1
fi
if [[ "${READINESS_METHOD}" == "systeminfo" ]]
then
# Get OAuth token from OWSEC and cache it or use cached one
payload="{ \"userId\" : \"$OWSEC_USERNAME\" , \"password\" : \"$OWSEC_PASSWORD\" }"
if [[ -f "/tmp/token" ]]
then
token=$(cat /tmp/token)
else
token=$(curl ${FLAGS} -X POST -H "Content-Type: application/json" -d "$payload" "https://${OWSEC}/api/v1/oauth2" | jq -r '.access_token')
fi
if [[ "${token}" == "" ]]
then
echo "Could not login. Please verify the host and username/password."
exit 13
fi
echo -n $token > /tmp/token
# Make systeminfo request to the local owgw instance
export RESTAPI_PORT=$(grep 'openwifi.restapi.host.0.port' $OWGW_CONFIG/owgw.properties | awk -F '=' '{print $2}' | xargs | envsubst)
curl ${FLAGS} -k -X GET "https://localhost:$RESTAPI_PORT/api/v1/system?command=info" \
-H "accept: application/json" \
-H "Authorization: Bearer ${token}" > /tmp/result.json
exit_code=$?
jq < /tmp/result.json
exit $exit_code
else
export ALB_PORT=$(grep 'alb.port' $OWGW_CONFIG/owgw.properties | awk -F '=' '{print $2}' | xargs | envsubst)
curl localhost:$ALB_PORT
fi

118
src/ALBHealthCheckServer.h Normal file
View File

@@ -0,0 +1,118 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRALGW_ALBHEALTHCHECKSERVER_H
#define UCENTRALGW_ALBHEALTHCHECKSERVER_H
#include <memory>
#include <iostream>
#include <fstream>
#include <sstream>
#include "Poco/Thread.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Logger.h"
#include "Daemon.h"
#include "SubSystemServer.h"
namespace OpenWifi {
class ALBRequestHandler: public Poco::Net::HTTPRequestHandler
/// Return a HTML document with the current date and time.
{
public:
explicit ALBRequestHandler(Poco::Logger & L)
: Logger_(L)
{
}
void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override
{
Logger_.information(Poco::format("ALB-REQUEST(%s): New ALB request.",Request.clientAddress().toString()));
Response.setChunkedTransferEncoding(true);
Response.setContentType("text/html");
Response.setDate(Poco::Timestamp());
Response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
Response.setKeepAlive(true);
Response.set("Connection","keep-alive");
Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
std::ostream &Answer = Response.send();
Answer << "uCentralGW Alive and kicking!" ;
}
private:
Poco::Logger & Logger_;
};
class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory
{
public:
explicit ALBRequestHandlerFactory(Poco::Logger & L):
Logger_(L)
{
}
ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override
{
if (request.getURI() == "/")
return new ALBRequestHandler(Logger_);
else
return nullptr;
}
private:
Poco::Logger &Logger_;
};
class ALBHealthCheckServer : public SubSystemServer {
public:
ALBHealthCheckServer() noexcept:
SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb")
{
}
static ALBHealthCheckServer *instance() {
if (instance_ == nullptr) {
instance_ = new ALBHealthCheckServer;
}
return instance_;
}
int Start() override {
if(Daemon()->ConfigGetBool("alb.enable",false)) {
Port_ = (int)Daemon()->ConfigGetInt("alb.port",15015);
Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_);
auto Params = new Poco::Net::HTTPServerParams;
Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger_), *Socket_, Params);
Server_->start();
}
return 0;
}
void Stop() override {
if(Server_)
Server_->stop();
}
private:
static ALBHealthCheckServer *instance_;
std::unique_ptr<Poco::Net::HTTPServer> Server_;
std::unique_ptr<Poco::Net::ServerSocket> Socket_;
int Port_ = 0;
};
inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); }
inline class ALBHealthCheckServer * ALBHealthCheckServer::instance_ = nullptr;
}
#endif // UCENTRALGW_ALBHEALTHCHECKSERVER_H

View File

@@ -1,61 +0,0 @@
//
// Created by stephane bourque on 2021-10-23.
//
#include "framework/MicroService.h"
#include "RESTAPI/RESTAPI_blacklist.h"
#include "RESTAPI/RESTAPI_blacklist_list.h"
#include "RESTAPI/RESTAPI_command.h"
#include "RESTAPI/RESTAPI_commands.h"
#include "RESTAPI/RESTAPI_default_configuration.h"
#include "RESTAPI/RESTAPI_default_configurations.h"
#include "RESTAPI/RESTAPI_deviceDashboardHandler.h"
#include "RESTAPI/RESTAPI_device_commandHandler.h"
#include "RESTAPI/RESTAPI_device_handler.h"
#include "RESTAPI/RESTAPI_devices_handler.h"
#include "RESTAPI/RESTAPI_file.h"
#include "RESTAPI/RESTAPI_ouis.h"
#include "RESTAPI/RESTAPI_capabilities_handler.h"
#include "RESTAPI/RESTAPI_TelemetryWebSocket.h"
#include "RESTAPI/RESTAPI_webSocketServer.h"
namespace OpenWifi {
Poco::Net::HTTPRequestHandler * RESTAPI_external_server(const char *Path, RESTAPIHandler::BindingMap &Bindings,
Poco::Logger & L, RESTAPI_GenericServer & S) {
return RESTAPI_Router<
RESTAPI_devices_handler,
RESTAPI_device_handler,
RESTAPI_device_commandHandler,
RESTAPI_default_configurations,
RESTAPI_default_configuration,
RESTAPI_command,
RESTAPI_commands,
RESTAPI_ouis,
RESTAPI_file,
RESTAPI_system_command,
RESTAPI_deviceDashboardHandler,
RESTAPI_webSocketServer,
RESTAPI_blacklist,
RESTAPI_blacklist_list,
RESTAPI_capabilities_handler,
RESTAPI_TelemetryWebSocket>(Path,Bindings,L, S);
}
Poco::Net::HTTPRequestHandler * RESTAPI_internal_server(const char *Path, RESTAPIHandler::BindingMap &Bindings,
Poco::Logger & L, RESTAPI_GenericServer & S) {
return RESTAPI_Router_I<
RESTAPI_devices_handler,
RESTAPI_device_handler,
RESTAPI_device_commandHandler,
RESTAPI_default_configurations,
RESTAPI_default_configuration,
RESTAPI_command,
RESTAPI_commands,
RESTAPI_ouis,
RESTAPI_file, RESTAPI_blacklist,
RESTAPI_blacklist_list>(Path,Bindings,L, S);
}
}

93
src/AuthClient.cpp Normal file
View File

@@ -0,0 +1,93 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <utility>
#include "AuthClient.h"
#include "RESTAPI_SecurityObjects.h"
#include "Daemon.h"
#include "OpenAPIRequest.h"
namespace OpenWifi {
class AuthClient * AuthClient::instance_ = nullptr;
int AuthClient::Start() {
return 0;
}
void AuthClient::Stop() {
}
void AuthClient::RemovedCachedToken(const std::string &Token) {
std::lock_guard G(Mutex_);
UserCache_.erase(Token);
}
bool IsTokenExpired(const SecurityObjects::WebToken &T) {
return ((T.expires_in_+T.created_)<std::time(nullptr));
}
bool AuthClient::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 {
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;
}
}
return false;
}
bool AuthClient::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;
}
}
return false;
}
}

45
src/AuthClient.h Normal file
View File

@@ -0,0 +1,45 @@
//
// Created by stephane bourque on 2021-06-30.
//
#ifndef UCENTRALGW_AUTHCLIENT_H
#define UCENTRALGW_AUTHCLIENT_H
#include "Poco/JSON/Object.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/JWT/Signer.h"
#include "Poco/SHA2Engine.h"
#include "RESTAPI_SecurityObjects.h"
#include "SubSystemServer.h"
namespace OpenWifi {
class AuthClient : public SubSystemServer {
public:
explicit AuthClient() noexcept:
SubSystemServer("Authentication", "AUTH-CLNT", "authentication")
{
}
static AuthClient *instance() {
if (instance_ == nullptr) {
instance_ = new AuthClient;
}
return instance_;
}
int Start() override;
void Stop() override;
bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, OpenWifi::SecurityObjects::UserInfoAndPolicy & UInfo );
void RemovedCachedToken(const std::string &Token);
bool IsTokenAuthorized(const std::string &Token, SecurityObjects::UserInfoAndPolicy & UInfo);
private:
static AuthClient *instance_;
OpenWifi::SecurityObjects::UserInfoCache UserCache_;
};
inline AuthClient * AuthClient() { return AuthClient::instance(); }
}
#endif // UCENTRALGW_AUTHCLIENT_H

View File

@@ -7,14 +7,13 @@
//
#include <fstream>
#include "CentralConfig.h"
#include "Daemon.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSON/Parser.h"
#include "Poco/File.h"
#include "CentralConfig.h"
#include "framework/MicroService.h"
#include "Daemon.h"
namespace OpenWifi::Config {
static std::string DefaultConfiguration;
@@ -120,7 +119,7 @@ namespace OpenWifi::Config {
})lit"};
void SetBasicConfigFile() {
Poco::File DefaultConfigFileName{MicroService::instance().DataDir()+"/default_config.json"};
Poco::File DefaultConfigFileName{Daemon()->DataDir()+"/default_config.json"};
DefaultConfiguration = BasicConfig;
std::ofstream F;
F.open(DefaultConfigFileName.path(),std::ios::binary);
@@ -135,7 +134,7 @@ namespace OpenWifi::Config {
void Config::Init() {
if(DefaultConfiguration.empty()) {
// open the file
Poco::File DefaultConfigFileName{MicroService::instance().DataDir()+"/default_config.json"};
Poco::File DefaultConfigFileName{Daemon()->DataDir()+"/default_config.json"};
try {
if (!DefaultConfigFileName.exists()) {
SetBasicConfigFile();
@@ -233,7 +232,7 @@ namespace OpenWifi::Config {
}
catch ( const Poco::Exception & E )
{
Daemon()->logger().log(E);
Daemon::instance()->logger().warning(Poco::format("%s: Failed with: %s", std::string(__func__) , E.displayText()));
}
}

View File

@@ -6,20 +6,22 @@
// Arilia Wireless Inc.
//
#include "CommandManager.h"
#include <algorithm>
#include "Poco/JSON/Parser.h"
#include "CommandManager.h"
#include "DeviceRegistry.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "RESTAPI_GWobjects.h"
#include "RESTAPI_handler.h"
#include "StorageService.h"
#include "framework/MicroService.h"
#include "framework/uCentral_Protocol.h"
#include "uCentralProtocol.h"
#include "Poco/JSON/Parser.h"
namespace OpenWifi {
class CommandManager * CommandManager::instance_ = nullptr;
void CommandManager::run() {
Running_ = true;
while(Running_)
@@ -29,7 +31,7 @@ namespace OpenWifi {
break;
std::vector<GWObjects::CommandDetails> Commands;
if(StorageService()->GetReadyToExecuteCommands(1,200,Commands))
if(Storage()->GetReadyToExecuteCommands(1,200,Commands))
{
for(auto & Cmd: Commands)
{
@@ -45,7 +47,7 @@ namespace OpenWifi {
*Params,
Cmd.UUID,
RPC_Id)) {
StorageService()->SetCommandExecuted(Cmd.UUID);
Storage()->SetCommandExecuted(Cmd.UUID);
Logger_.information(Poco::format("Sent command '%s' to '%s'",Cmd.Command,Cmd.SerialNumber));
} else {
Logger_.information(Poco::format("Failed to send command '%s' to %s",Cmd.Command,Cmd.SerialNumber));
@@ -153,7 +155,7 @@ namespace OpenWifi {
RPC->second.Result = Obj;
Logger_.information(Poco::format("(%s): Received RPC answer %lu", SerialNumber, ID));
G.unlock();
StorageService()->CommandCompleted(RPC->second.UUID, Obj, true);
Storage()->CommandCompleted(RPC->second.UUID, Obj, true);
}
} // namespace

View File

@@ -19,8 +19,8 @@
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/MicroService.h"
#include "RESTAPI_GWobjects.h"
#include "SubSystemServer.h"
namespace OpenWifi {
@@ -68,12 +68,15 @@ namespace OpenWifi {
bool GetCommand(uint64_t Id, const std::string & SerialNumber, CommandTag &T);
static CommandManager *instance() {
static CommandManager *instance_ = new CommandManager;
if (instance_ == nullptr) {
instance_ = new CommandManager;
}
return instance_;
}
inline bool Running() const { return Running_; }
private:
static CommandManager * instance_;
std::atomic_bool Running_ = false;
Poco::Thread ManagerThread;
uint64_t Id_=2; // do not start @1. We ignore ID=1 & 0 is illegal..

View File

@@ -5,4 +5,5 @@
#include "ConfigurationCache.h"
namespace OpenWifi {
class ConfigurationCache * ConfigurationCache::instance_ = nullptr;
}

View File

@@ -13,9 +13,10 @@ namespace OpenWifi {
class ConfigurationCache {
public:
static ConfigurationCache & instance() {
static ConfigurationCache instance;
return instance;
static ConfigurationCache &instance() {
if(instance_== nullptr)
instance_ = new ConfigurationCache;
return *instance_;
}
inline uint64_t CurrentConfig(const std::string &SerialNumber) {
@@ -26,12 +27,13 @@ namespace OpenWifi {
return Hint->second;
}
inline void Add(const std::string &SerialNumber, uint64_t Id) {
void Add(const std::string &SerialNumber, uint64_t Id) {
std::lock_guard G(Mutex_);
Cache_[SerialNumber]=Id;
}
private:
static ConfigurationCache *instance_;
std::mutex Mutex_;
std::map<std::string,uint64_t> Cache_;
};

View File

@@ -4,12 +4,10 @@
#include <iostream>
#include <fstream>
#include <regex>
#include "framework/MicroService.h"
#include "ConfigurationValidator.h"
#include "framework/CountryCodes.h"
#include "Poco/StringTokenizer.h"
#include "Utils.h"
#include "Daemon.h"
#include "Poco/Logger.h"
namespace OpenWifi {
@@ -96,69 +94,6 @@ namespace OpenWifi {
}
}
},
"globals.wireless-multimedia.class-selector": {
"type": "array",
"items": {
"type": "string",
"enum": [
"CS1",
"CS2",
"CS3",
"CS4",
"CS5",
"CS6",
"AF11",
"AF12",
"AF13",
"AF21",
"AF22",
"AF23",
"AF31",
"AF32",
"AF33",
"AF41",
"AF42",
"AF43",
"DF",
"EF"
]
}
},
"globals.wireless-multimedia": {
"type": "object",
"properties": {
"UP0": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP1": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP2": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP3": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP4": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP5": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP6": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
},
"UP7": {
"$ref": "#/$defs/globals.wireless-multimedia.class-selector"
}
}
},
"globals.wireless-multimedia-profile": {
"type": "string",
"enum": [
"enterprise"
]
},
"globals": {
"type": "object",
"properties": {
@@ -175,16 +110,6 @@ namespace OpenWifi {
"examples": [
"fdca:1234:4567::/48"
]
},
"wireless-multimedia": {
"oneOf": [
{
"$ref": "#/$defs/globals.wireless-multimedia"
},
{
"$ref": "#/$defs/globals.wireless-multimedia-profile"
}
]
}
}
},
@@ -880,7 +805,6 @@ namespace OpenWifi {
"sae",
"sae-mixed",
"wpa3",
"wpa3-192",
"wpa3-mixed"
],
"examples": [
@@ -1217,11 +1141,8 @@ namespace OpenWifi {
"maxLength": 2
},
"domain-name": {
"type": "array",
"items": {
"type": "string",
"format": "hostname"
}
"type": "string",
"format": "hostname"
},
"nai-realm": {
"type": "array",
@@ -1342,25 +1263,6 @@ namespace OpenWifi {
}
]
}
},
"wan-metrics": {
"type": "object",
"properties": {
"info": {
"type": "string",
"enum": [
"up",
"down",
"testing"
]
},
"downlink": {
"type": "integer"
},
"uplink": {
"type": "integer"
}
}
}
}
},
@@ -1577,6 +1479,10 @@ namespace OpenWifi {
"type": "string",
"format": "ipv4",
"example": "192.168.100.1"
},
"vlan-id": {
"type": "integer",
"maximum": 4096
}
}
},
@@ -1934,13 +1840,9 @@ namespace OpenWifi {
"properties": {
"controller": {
"type": "string",
"format": "ip",
"uc-format": "cidr",
"example": "192.168.10.1"
},
"datapath-description": {
"type": "string",
"example": "Building 2, Floor 6, AP 2"
},
"mode": {
"type": "string",
"enum": [
@@ -1948,8 +1850,7 @@ namespace OpenWifi {
"ptcp",
"ssl",
"tcp"
],
"default": "ssl"
]
},
"ca-certificate": {
"type": "string"
@@ -2024,76 +1925,13 @@ namespace OpenWifi {
"service.quality-of-service": {
"type": "object",
"properties": {
"select-ports": {
"type": "array",
"items": {
"type": "string",
"default": "WAN"
}
},
"bandwidth-up": {
"upload-rate": {
"type": "integer",
"default": 0
},
"bandwidth-down": {
"download-rate": {
"type": "integer",
"default": 0
},
"classifier": {
"type": "array",
"items": {
"type": "object",
"properties": {
"dscp": {
"type": "string",
"enum": [
"CS0",
"CS1",
"CS2",
"CS3",
"CS4",
"CS5",
"CS6",
"CS7"
],
"default": "CS1"
},
"ports": {
"type": "array",
"items": {
"type": "object",
"properties": {
"protocol": {
"type": "string",
"enum": [
"any",
"tcp",
"udp"
],
"default": "any"
},
"port": {
"type": "integer"
},
"range-end": {
"type": "integer"
},
"reclassify": {
"type": "boolean",
"default": true
}
}
},
"dns": {
"type": "array",
"items": {
"type": "string",
"format": "fqdn"
}
}
}
}
}
}
}
},
@@ -2315,6 +2153,7 @@ namespace OpenWifi {
}
}
}
)"_json;
class ConfigurationValidator *ConfigurationValidator::instance_ = nullptr;
@@ -2323,13 +2162,13 @@ namespace OpenWifi {
if(Initialized_)
return;
std::string GitSchema;
try {
if(Utils::wgets(GitUCentralJSONSchemaFile, GitSchema)) {
auto schema = json::parse(GitSchema);
Validator_->set_root_schema(schema);
Logger_.information("Using uCentral validation schema from GIT.");
} else {
std::string FileName{ MicroService::instance().DataDir() + "/ucentral.schema.json" };
if(Utils::wgets(GitUCentralJSONSchemaFile, GitSchema)) {
auto schema = json::parse(GitSchema);
Validator_->set_root_schema(schema);
Logger_.information("Using uCentral validation schema from GIT.");
} else {
std::string FileName{ Daemon()->DataDir() + "/ucentral.schema.json" };
try {
std::ifstream input(FileName);
std::stringstream schema_file;
schema_file << input.rdbuf();
@@ -2337,10 +2176,10 @@ namespace OpenWifi {
auto schema = json::parse(schema_file.str());
Validator_->set_root_schema(schema);
Logger_.information("Using uCentral validation schema from local file.");
} catch (const Poco::Exception &E) {
Validator_->set_root_schema(DefaultUCentralSchema);
Logger_.information("Using uCentral validation from built-in default.");
}
} catch (const Poco::Exception &E) {
Validator_->set_root_schema(DefaultUCentralSchema);
Logger_.information("Using uCentral validation from built-in default.");
}
Initialized_ = Working_ = true;
}
@@ -2354,116 +2193,13 @@ namespace OpenWifi {
}
static inline bool IsIPv4(const std::string &value) {
Poco::Net::IPAddress A;
return ((Poco::Net::IPAddress::tryParse(value,A) && A.family()==Poco::Net::IPAddress::IPv4));
}
static inline bool IsIPv6(const std::string &value) {
Poco::Net::IPAddress A;
return ((Poco::Net::IPAddress::tryParse(value,A) && A.family()==Poco::Net::IPAddress::IPv6));
}
static inline bool IsIP(const std::string &value) {
return IsIPv4(value) || IsIPv6(value);
}
static inline bool IsCIDRv6(const std::string &value) {
auto Tokens = Poco::StringTokenizer(value,"/");
if(Tokens.count()==2 && IsIPv6(Tokens[0])) {
auto Mask = std::atoi(Tokens[1].c_str());
if(Mask>=48 && Mask<=128)
return true;
}
return false;
}
static inline bool IsCIDRv4(const std::string &value) {
auto Tokens = Poco::StringTokenizer(value,"/");
if(Tokens.count()==2 && IsIPv4(Tokens[0])) {
auto Mask = std::atoi(Tokens[1].c_str());
if(Mask>=0 && Mask<=32)
return true;
}
return false;
}
static inline bool IsCIDR(const std::string &value) {
return IsCIDRv4(value) || IsCIDRv6(value);
}
void ConfigurationValidator::my_format_checker(const std::string &format, const std::string &value)
{
static const std::regex host_regex{"^(?=.{1,254}$)((?=[a-z0-9-]{1,63}\\.)(xn--+)?[a-z0-9]+(-[a-z0-9]+)*\\.)+[a-z]{2,63}$"};
static const std::regex mac_regex{"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"};
static const std::regex uc_timeout_regex{"^[0-9]+[dmshw]$"};
static const std::regex b64_regex("^[a-zA-Z0-9\\+/]*={0,3}$");
if(format == "uc-cidr4") {
if(IsCIDRv4(value))
return;
throw std::invalid_argument(value + " is not a valid CIDR IPv4: should be something like 192.168.0.0/16.");
} else if(format == "uc-cidr6") {
if(IsCIDRv6(value))
return;
throw std::invalid_argument(value + " is not a valid CIDR IPv6: should be something like 2e60:3500::/64.");
} else if(format=="uc-cidr") {
if(IsCIDR(value))
return;
throw std::invalid_argument(value + " is not a valid CIDR IPv6/IPv4: should be something like 2e60:3500::/64.");
} else if(format == "uc-mac") {
if(std::regex_match(value,mac_regex))
return;
throw std::invalid_argument(value + " is not a valid MAC: should be something like 2e60:3500::/64.");
} else if(format == "uc-timeout") {
if(std::regex_match(value,uc_timeout_regex))
return;
throw std::invalid_argument(value + " is not a proper timeout value: 6d, 300m, 24h, 84000s, infinite");
} else if(format == "uc-host") {
if(IsIP(value))
return;
if(std::regex_match(value,host_regex))
return;
throw std::invalid_argument(value + " is not a proper FQDN.");
} else if(format == "fqdn") {
if(std::regex_match(value,host_regex))
return;
throw std::invalid_argument(value + " is not a proper FQDN.");
} else if(format == "uc-base64") {
std::string s{value};
Poco::trimInPlace(s);
if( (s.size() %4 ==0) && std::regex_match(s,b64_regex))
return;
throw std::invalid_argument(value + " is not a base64 encoded value.");
} else if(format == "uri") {
try {
Poco::URI uri(value);
return;
} catch (...) {
}
throw std::invalid_argument(value + " is not a valid URI: should be something like https://hello.world.com.");
} else if(format == "ip") {
if (IsIP(value))
return;
throw std::invalid_argument(value + " is not a valid IP address.");
} else {
try {
nlohmann::json_schema::default_string_format_check(format,value);
} catch (const std::logic_error &E) {
std::string Error{"JSON Schema validation: "};
}
}
}
bool ConfigurationValidator::Validate(const std::string &C, std::string &Error) {
bool ConfigurationValidator::Validate(const std::string &C) {
if(Working_) {
try {
auto Doc = json::parse(C);
Validator_->validate(Doc);
return true;
} catch(const std::exception &E) {
Error = E.what();
std::cout << "Validation failed, here is why: " << E.what() << "\n";
return false;
}
}

View File

@@ -6,7 +6,8 @@
#define OWPROV_CONFIGURATIONVALIDATOR_H
#include <nlohmann/json-schema.hpp>
#include "framework/MicroService.h"
#include "Poco/Logger.h"
#include "SubSystemServer.h"
using nlohmann::json;
using nlohmann::json_schema::json_validator;
@@ -21,8 +22,31 @@ namespace OpenWifi {
return instance_;
}
bool Validate(const std::string &C, std::string &Error);
static void my_format_checker(const std::string &format, const std::string &value);
bool Validate(const std::string &C);
static void my_format_checker(const std::string &format, const std::string &value)
{
/*
"format": "uc-mac"
"format": "uc-timeout",
"format": "uc-cidr4",
"format": "uc-cidr6",
"uc-format": "cidr",
"format": "fqdn",
"format": "uc-host",
"format": "uri"
"format": "hostname"
"format": "uc-base64"
if (format == "something") {
return;
if (!check_value_for_something(value))
throw std::invalid_argument("value is not a good something");
} else
throw std::logic_error("Don't know how to validate " + format);
*/
}
int Start() override;
void Stop() override;
void reinitialize(Poco::Util::Application &self) override;
@@ -40,7 +64,7 @@ namespace OpenWifi {
};
inline ConfigurationValidator * ConfigurationValidator() { return ConfigurationValidator::instance(); }
inline bool ValidateUCentralConfiguration(const std::string &C, std::string &Error) { return ConfigurationValidator::instance()->Validate(C, Error); }
inline bool ValidateUCentralConfiguration(const std::string &C) { return ConfigurationValidator::instance()->Validate(C); }
}
#endif //OWPROV_CONFIGURATIONVALIDATOR_H

View File

@@ -10,33 +10,45 @@
#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Environment.h"
#include "Poco/Net/HTTPStreamFactory.h"
#include "CentralConfig.h"
#include "CommandManager.h"
#include "Daemon.h"
#include "CommandManager.h"
#include "DeviceRegistry.h"
#include "FileUploader.h"
#include "OUIServer.h"
#include "SerialNumberCache.h"
#include "StorageArchiver.h"
#include "RESTAPI_server.h"
#include "StorageService.h"
#include "TelemetryStream.h"
#include "WebSocketServer.h"
#include "framework/ConfigurationValidator.h"
#include "framework/MicroService.h"
#include "CentralConfig.h"
#include "OUIServer.h"
#include "StateProcessor.h"
#include "Utils.h"
#include "RESTAPI_InternalServer.h"
#include "AuthClient.h"
#include "StorageArchiver.h"
#include "SerialNumberCache.h"
#include "TelemetryStream.h"
#include "ConfigurationValidator.h"
namespace OpenWifi {
class Daemon *Daemon::instance_ = nullptr;
class Daemon *Daemon::instance() {
static Daemon instance(vDAEMON_PROPERTIES_FILENAME,
if (instance_ == nullptr) {
instance_ = new Daemon(vDAEMON_PROPERTIES_FILENAME,
vDAEMON_ROOT_ENV_VAR,
vDAEMON_CONFIG_ENV_VAR,
vDAEMON_APP_NAME,
vDAEMON_BUS_TIMER,
SubSystemVec{
StorageService(),
Types::SubSystemVec{
Storage(),
SerialNumberCache(),
ConfigurationValidator(),
AuthClient(),
DeviceRegistry(),
RESTAPI_server(),
RESTAPI_InternalServer(),
WebSocketServer(),
CommandManager(),
FileUploader(),
@@ -44,10 +56,12 @@ namespace OpenWifi {
StorageArchiver(),
TelemetryStream()
});
return &instance;
}
return instance_;
}
void Daemon::initialize() {
void Daemon::initialize(Poco::Util::Application &self) {
MicroService::initialize(*this);
Config::Config::Init();
AutoProvisioning_ = config().getBool("openwifi.autoprovisioning",false);
@@ -74,10 +88,6 @@ namespace OpenWifi {
}
}
void MicroServicePostInitialization() {
Daemon()->initialize();
}
[[nodiscard]] std::string Daemon::IdentifyDevice(const std::string & Id ) const {
for(const auto &[Type,List]:DeviceTypeIdentifications_)
{
@@ -88,12 +98,12 @@ namespace OpenWifi {
}
}
int main(int argc, char **argv) {
try {
auto App = OpenWifi::Daemon::instance();
auto ExitCode = App->run(argc, argv);
delete App;
return ExitCode;
} catch (Poco::Exception &exc) {

View File

@@ -26,8 +26,8 @@
#include "Poco/Crypto/Cipher.h"
#include "Dashboard.h"
#include "framework/MicroService.h"
#include "framework/OpenWifiTypes.h"
#include "MicroService.h"
#include "OpenWifiTypes.h"
namespace OpenWifi {
@@ -44,16 +44,17 @@ namespace OpenWifi {
const std::string & ConfigEnv,
const std::string & AppName,
uint64_t BusTimer,
const SubSystemVec & SubSystems) :
const Types::SubSystemVec & SubSystems) :
MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {};
bool AutoProvisioning() const { return AutoProvisioning_ ; }
[[nodiscard]] std::string IdentifyDevice(const std::string & Compatible) const;
void initialize();
void initialize(Poco::Util::Application &self) override;
static Daemon *instance();
inline DeviceDashboard & GetDashboard() { return DB_; }
Poco::Logger & Log() { return Poco::Logger::get(AppName()); }
private:
static Daemon *instance_;
bool AutoProvisioning_ = false;
Types::StringMapStringSet DeviceTypeIdentifications_;
DeviceDashboard DB_;

View File

@@ -12,8 +12,8 @@ namespace OpenWifi {
if(LastRun_==0 || (Now-LastRun_)>120) {
DB_.reset();
StorageService()->AnalyzeCommands(DB_.commands);
StorageService()->AnalyzeDevices(DB_);
Storage()->AnalyzeCommands(DB_.commands);
Storage()->AnalyzeDevices(DB_);
LastRun_ = Now;
}
}

View File

@@ -5,8 +5,8 @@
#ifndef UCENTRALGW_DASHBOARD_H
#define UCENTRALGW_DASHBOARD_H
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/OpenWifiTypes.h"
#include "OpenWifiTypes.h"
#include "RESTAPI_GWobjects.h"
namespace OpenWifi {
class DeviceDashboard {

View File

@@ -6,14 +6,22 @@
// Arilia Wireless Inc.
//
#include "DeviceRegistry.h"
#include "RESTAPI_handler.h"
#include "WebSocketServer.h"
#include "DeviceRegistry.h"
#include "OUIServer.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSON/Parser.h"
#include "DeviceRegistry.h"
#include "WebSocketServer.h"
#include "OUIServer.h"
namespace OpenWifi {
class DeviceRegistry *DeviceRegistry::instance_ = nullptr;
DeviceRegistry::DeviceRegistry() noexcept:
SubSystemServer("DeviceRegistry", "DevStatus", "devicestatus") {
}
int DeviceRegistry::Start() {
std::lock_guard Guard(Mutex_);
@@ -41,6 +49,7 @@ namespace OpenWifi {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end())
{
Device->second->Conn_.LastContact = time(nullptr);
@@ -83,61 +92,74 @@ namespace OpenWifi {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device != Devices_.end())
{
Device->second->LastHealthcheck = CheckData;
}
}
std::shared_ptr<DeviceRegistry::ConnectionEntry> DeviceRegistry::Register(const std::string & SerialNumber, WSConnection *Ptr, uint64_t & ConnectionId )
GWObjects::ConnectionState * DeviceRegistry::Register(const std::string & SerialNumber, WSConnection *Ptr)
{
std::lock_guard Guard(Mutex_);
const auto & E = Devices_[SerialNumber] = std::make_shared<ConnectionEntry>();
E->WSConn_ = Ptr;
E->Conn_.SerialNumber = SerialNumber;
E->Conn_.LastContact = std::time(nullptr);
E->Conn_.Connected = true ;
E->Conn_.UUID = 0 ;
E->Conn_.MessageCount = 0 ;
E->Conn_.Address = "";
E->Conn_.TX = 0 ;
E->Conn_.RX = 0;
E->Conn_.VerifiedCertificate = GWObjects::CertificateValidation::NO_CERTIFICATE;
ConnectionId = E->ConnectionId = ++Id_;
return E;
auto Device = Devices_.find(SerialNumber);
if( Device == Devices_.end()) {
auto E = std::make_unique<ConnectionEntry>();
E->WSConn_ = Ptr;
E->Conn_.SerialNumber = SerialNumber;
E->Conn_.LastContact = std::time(nullptr);
E->Conn_.Connected = true ;
E->Conn_.UUID = 0 ;
E->Conn_.MessageCount = 0 ;
E->Conn_.Address = "";
E->Conn_.TX = 0 ;
E->Conn_.RX = 0;
E->Conn_.VerifiedCertificate = GWObjects::CertificateValidation::NO_CERTIFICATE;
auto R=&E->Conn_;
Devices_[SerialNumber] = std::move(E);
return R;
}
else
{
Device->second->WSConn_ = Ptr;
Device->second->Conn_.Connected = true;
Device->second->Conn_.LastContact = std::time(nullptr);
Device->second->Conn_.VerifiedCertificate = GWObjects::CertificateValidation::NO_CERTIFICATE;
return &Device->second->Conn_;
}
}
bool DeviceRegistry::Connected(const std::string & SerialNumber) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device == Devices_.end())
return false;
return Device->second->Conn_.Connected;
}
void DeviceRegistry::UnRegister(const std::string & SerialNumber, uint64_t ConnectionId) {
void DeviceRegistry::UnRegister(const std::string & SerialNumber, WSConnection *Ptr) {
std::lock_guard Guard(Mutex_);
auto It = Devices_.find(SerialNumber);
if(It!=Devices_.end()) {
if(It->second->ConnectionId == ConnectionId)
Devices_.erase(SerialNumber);
}
}
auto Device = Devices_.find(SerialNumber);
if( Device != Devices_.end() && Device->second->WSConn_==Ptr) {
Device->second->Conn_.Address = "";
Device->second->WSConn_ = nullptr;
Device->second->Conn_.Connected = false;
Device->second->Conn_.VerifiedCertificate = GWObjects::NO_CERTIFICATE;
}
}
bool DeviceRegistry::SendFrame(const std::string & SerialNumber, const std::string & Payload) {
std::lock_guard Guard(Mutex_);
auto Device = Devices_.find(SerialNumber);
if(Device!=Devices_.end() && Device->second->WSConn_!= nullptr) {
try {
return Device->second->WSConn_->Send(Payload);
} catch (...) {
Logger_.debug(Poco::format("Could not send data to device '%s'", SerialNumber));
Device->second->Conn_.Address = "";
Device->second->WSConn_ = nullptr;
Device->second->Conn_.Connected = false;
Device->second->Conn_.VerifiedCertificate = GWObjects::NO_CERTIFICATE;
}
return Device->second->WSConn_->Send(Payload);
}
return false;
}

View File

@@ -6,12 +6,13 @@
// Arilia Wireless Inc.
//
#pragma once
#ifndef UCENTRAL_UDEVICEREGISTRY_H
#define UCENTRAL_UDEVICEREGISTRY_H
#include "Poco/JSON/Object.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "framework/MicroService.h"
#include "RESTAPI_GWobjects.h"
#include "SubSystemServer.h"
// class uCentral::WebSocket::WSConnection;
@@ -25,11 +26,12 @@ namespace OpenWifi {
GWObjects::ConnectionState Conn_;
std::string LastStats;
GWObjects::HealthCheck LastHealthcheck;
uint64_t ConnectionId=0;
};
static DeviceRegistry *instance() {
static DeviceRegistry *instance_ = new DeviceRegistry;
if (instance_ == nullptr) {
instance_ = new DeviceRegistry;
}
return instance_;
}
@@ -41,24 +43,24 @@ namespace OpenWifi {
void SetState(const std::string & SerialNumber, GWObjects::ConnectionState & State);
bool GetHealthcheck(const std::string &SerialNumber, GWObjects::HealthCheck & CheckData);
void SetHealthcheck(const std::string &SerialNumber, const GWObjects::HealthCheck &H);
std::shared_ptr<ConnectionEntry> Register(const std::string & SerialNumber, WSConnection *, uint64_t & ConnectionId);
void UnRegister(const std::string & SerialNumber, uint64_t ConnectionId);
GWObjects::ConnectionState * Register(const std::string & SerialNumber, WSConnection *);
void UnRegister(const std::string & SerialNumber, WSConnection *);
bool SendCommand(GWObjects::CommandDetails & Command);
bool Connected(const std::string & SerialNumber);
bool SendFrame(const std::string & SerialNumber, const std::string & Payload);
void SetPendingUUID(const std::string & SerialNumber, uint64_t PendingUUID);
bool AnalyzeRegistry(GWObjects::Dashboard &D);
private:
inline static std::atomic_uint64_t Id_=1;
std::map<std::string,std::shared_ptr<ConnectionEntry>> Devices_;
static DeviceRegistry *instance_;
std::map<std::string,std::unique_ptr<ConnectionEntry>> Devices_;
DeviceRegistry() noexcept:
SubSystemServer("DeviceRegistry", "DevStatus", "devicestatus") {
}
DeviceRegistry() noexcept;
};
inline DeviceRegistry * DeviceRegistry() { return DeviceRegistry::instance(); }
} // namespace
#endif //UCENTRAL_UDEVICEREGISTRY_H

View File

@@ -10,6 +10,10 @@
#include <fstream>
#include <cstdio>
#include "Daemon.h"
#include "FileUploader.h"
#include "StorageService.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/DynamicAny.h"
@@ -20,18 +24,17 @@
#include "Poco/StreamCopier.h"
#include "Poco/Exception.h"
#include "FileUploader.h"
#include "StorageService.h"
#include "framework/MicroService.h"
#include "Utils.h"
namespace OpenWifi {
class FileUploader *FileUploader::instance_ = nullptr;
static const std::string URI_BASE{"/v1/upload/"};
int FileUploader::Start() {
Logger_.notice("Starting.");
Poco::File UploadsDir(MicroService::instance().ConfigPath("openwifi.fileuploader.path","/tmp"));
Poco::File UploadsDir(Daemon()->ConfigPath("openwifi.fileuploader.path","/tmp"));
Path_ = UploadsDir.path();
if(!UploadsDir.exists()) {
try {
@@ -59,7 +62,7 @@ namespace OpenWifi {
Params->setMaxQueued(100);
if(FullName_.empty()) {
std::string TmpName = MicroService::instance().ConfigGetString("openwifi.fileuploader.uri","");
std::string TmpName = Daemon()->ConfigGetString("openwifi.fileuploader.uri","");
if(TmpName.empty()) {
FullName_ =
"https://" + Svr.Name() + ":" + std::to_string(Svr.Port()) + URI_BASE;
@@ -74,13 +77,13 @@ namespace OpenWifi {
Servers_.push_back(std::move(NewServer));
}
MaxSize_ = 1000 * MicroService::instance().ConfigGetInt("openwifi.fileuploader.maxsize", 10000);
MaxSize_ = 1000 * Daemon()->ConfigGetInt("openwifi.fileuploader.maxsize", 10000);
return 0;
}
void FileUploader::reinitialize(Poco::Util::Application &self) {
MicroService::instance().LoadConfigurationFile();
Daemon()->LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
Start();
@@ -202,12 +205,12 @@ namespace OpenWifi {
if (partHandler.Good()) {
Answer.set("filename", UUID_);
Answer.set("error", 0);
StorageService()->AttachFileToCommand(UUID_);
Storage()->AttachFileToCommand(UUID_);
} else {
Answer.set("filename", UUID_);
Answer.set("error", 13);
Answer.set("errorText", partHandler.Error() );
StorageService()->CancelWaitFile(UUID_, partHandler.Error() );
Storage()->CancelWaitFile(UUID_, partHandler.Error() );
}
std::ostream &ResponseStream = Response.send();
Poco::JSON::Stringifier::stringify(Answer, ResponseStream);

View File

@@ -9,12 +9,13 @@
#ifndef UCENTRAL_UFILEUPLOADER_H
#define UCENTRAL_UFILEUPLOADER_H
#include "SubSystemServer.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
@@ -30,13 +31,16 @@ namespace OpenWifi {
const std::string & Path() { return Path_; };
static FileUploader *instance() {
static FileUploader * instance_ = new FileUploader;
return instance_;
if (instance_ == nullptr) {
instance_ = new FileUploader;
}
return instance_;
}
[[nodiscard]] inline uint64_t MaxSize() const { return MaxSize_; }
private:
static FileUploader *instance_;
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> Servers_;
Poco::ThreadPool Pool_;
std::string FullName_;

221
src/KafkaManager.cpp Normal file
View File

@@ -0,0 +1,221 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <thread>
#include "KafkaManager.h"
#include "Daemon.h"
#include "Utils.h"
namespace OpenWifi {
class KafkaManager *KafkaManager::instance_ = nullptr;
KafkaManager::KafkaManager() noexcept:
SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka")
{
}
void KafkaManager::initialize(Poco::Util::Application & self) {
SubSystemServer::initialize(self);
KafkaEnabled_ = Daemon()->ConfigGetBool("openwifi.kafka.enable",false);
}
#ifdef SMALL_BUILD
int KafkaManager::Start() {
return 0;
}
void KafkaManager::Stop() {
}
#else
int KafkaManager::Start() {
if(!KafkaEnabled_)
return 0;
ProducerThr_ = std::make_unique<std::thread>([this]() { this->ProducerThr(); });
ConsumerThr_ = std::make_unique<std::thread>([this]() { this->ConsumerThr(); });
return 0;
}
void KafkaManager::Stop() {
if(KafkaEnabled_) {
ProducerRunning_ = ConsumerRunning_ = false;
ProducerThr_->join();
ConsumerThr_->join();
return;
}
}
void KafkaManager::ProducerThr() {
cppkafka::Configuration Config({
{ "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") }
});
SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
std::to_string(Daemon()->ID()) +
R"lit( , "host" : ")lit" + Daemon()->PrivateEndPoint() +
R"lit(" } , "payload" : )lit" ;
cppkafka::Producer Producer(Config);
ProducerRunning_ = true;
while(ProducerRunning_) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
try
{
std::lock_guard G(ProducerMutex_);
auto Num=0;
while (!Queue_.empty()) {
const auto M = Queue_.front();
Producer.produce(
cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad));
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()}));
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
}
void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) {
Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition()));
}
void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) {
Logger_.information(Poco::format("Partition revocation: %Lu...",(uint64_t )partitions.front().get_partition()));
}
void KafkaManager::ConsumerThr() {
cppkafka::Configuration Config({
{ "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") },
{ "group.id", Daemon()->ConfigGetString("openwifi.kafka.group.id") },
{ "enable.auto.commit", Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false) },
{ "auto.offset.reset", "latest" } ,
{ "enable.partition.eof", false }
});
cppkafka::TopicConfiguration topic_config = {
{ "auto.offset.reset", "smallest" }
};
// Now configure it to be the default topic config
Config.set_default_topic_configuration(topic_config);
cppkafka::Consumer Consumer(Config);
Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) {
if(!partitions.empty()) {
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...",
(uint64_t)partitions.front().get_partition()));
}
});
bool AutoCommit = Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false);
auto BatchSize = Daemon()->ConfigGetInt("openwifi.kafka.consumer.batchsize",20);
Types::StringVec Topics;
for(const auto &i:Notifiers_)
Topics.push_back(i.first);
Consumer.subscribe(Topics);
ConsumerRunning_ = true;
while(ConsumerRunning_) {
try {
std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200));
for(auto const &Msg:MsgVec) {
if (!Msg)
continue;
if (Msg.get_error()) {
if (!Msg.is_eof()) {
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()) {
Types::TopicNotifyFunctionList &FL = It->second;
std::string Key{Msg.get_key()};
std::string Payload{Msg.get_payload()};
for (auto &F : FL) {
std::thread T(F.first, Key, Payload);
T.detach();
}
}
if (!AutoCommit)
Consumer.async_commit(Msg);
}
} catch (const cppkafka::HandleException &E) {
Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
}
std::string KafkaManager::WrapSystemId(const std::string & PayLoad) {
return std::move( SystemInfoWrapper_ + PayLoad + "}");
}
void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) {
if(KafkaEnabled_) {
std::lock_guard G(Mutex_);
KMessage M{
.Topic = topic,
.Key = key,
.PayLoad = WrapMessage ? WrapSystemId(PayLoad) : PayLoad };
Queue_.push(M);
}
}
int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) {
if(KafkaEnabled_) {
std::lock_guard G(Mutex_);
auto It = Notifiers_.find(Topic);
if(It == Notifiers_.end()) {
Types::TopicNotifyFunctionList L;
L.emplace(L.end(),std::make_pair(F,FunctionId_));
Notifiers_[Topic] = std::move(L);
} else {
It->second.emplace(It->second.end(),std::make_pair(F,FunctionId_));
}
return FunctionId_++;
} else {
return 0;
}
}
void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) {
if(KafkaEnabled_) {
std::lock_guard G(Mutex_);
auto It = Notifiers_.find(Topic);
if(It != Notifiers_.end()) {
Types::TopicNotifyFunctionList & L = It->second;
for(auto it=L.begin(); it!=L.end(); it++)
if(it->second == Id) {
L.erase(it);
break;
}
}
}
}
#endif
} // namespace

74
src/KafkaManager.h Normal file
View File

@@ -0,0 +1,74 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRALGW_KAFKAMANAGER_H
#define UCENTRALGW_KAFKAMANAGER_H
#include <queue>
#include <thread>
#include "SubSystemServer.h"
#include "OpenWifiTypes.h"
#include "cppkafka/cppkafka.h"
namespace OpenWifi {
class KafkaManager : public SubSystemServer {
public:
struct KMessage {
std::string Topic,
Key,
PayLoad;
};
void initialize(Poco::Util::Application & self) override;
static KafkaManager *instance() {
if(instance_== nullptr)
instance_ = new KafkaManager;
return instance_;
}
void ProducerThr();
void ConsumerThr();
int Start() override;
void Stop() override;
void PostMessage(const std::string &topic, const std::string & key, const std::string &payload, bool WrapMessage = true);
[[nodiscard]] std::string WrapSystemId(const std::string & PayLoad);
[[nodiscard]] bool Enabled() { return KafkaEnabled_; }
int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction & F);
void UnregisterTopicWatcher(const std::string &Topic, int FunctionId);
void WakeUp();
void PartitionAssignment(const cppkafka::TopicPartitionList& partitions);
void PartitionRevocation(const cppkafka::TopicPartitionList& partitions);
private:
static KafkaManager *instance_;
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_;
KafkaManager() noexcept;
};
inline KafkaManager * KafkaManager() { return KafkaManager::instance(); }
} // NameSpace
#endif // UCENTRALGW_KAFKAMANAGER_H

View File

@@ -1,10 +1,7 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
// Created by stephane bourque on 2021-06-07.
//
#ifndef UCENTRALGW_KAFKA_TOPICS_H
#define UCENTRALGW_KAFKA_TOPICS_H

532
src/MicroService.cpp Normal file
View File

@@ -0,0 +1,532 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <cstdlib>
#include <boost/algorithm/string.hpp>
#include "Poco/Util/Application.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Environment.h"
#include "Poco/Net/HTTPSStreamFactory.h"
#include "Poco/Net/HTTPStreamFactory.h"
#include "Poco/Net/FTPSStreamFactory.h"
#include "Poco/Net/FTPStreamFactory.h"
#include "Poco/Path.h"
#include "Poco/File.h"
#include "Poco/String.h"
#include "Poco/JSON/Object.h"
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h"
#include "ALBHealthCheckServer.h"
#ifndef SMALL_BUILD
#include "KafkaManager.h"
#endif
#include "Kafka_topics.h"
#include "MicroService.h"
#include "Utils.h"
#ifndef TIP_SECURITY_SERVICE
#include "AuthClient.h"
#endif
namespace OpenWifi {
void MyErrorHandler::exception(const Poco::Exception & E) {
Poco::Thread * CurrentThread = Poco::Thread::current();
App_.logger().log(E);
App_.logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName()));
}
void MyErrorHandler::exception(const std::exception & E) {
Poco::Thread * CurrentThread = Poco::Thread::current();
App_.logger().warning(Poco::format("std::exception on %s",CurrentThread->getName()));
}
void MyErrorHandler::exception() {
Poco::Thread * CurrentThread = Poco::Thread::current();
App_.logger().warning(Poco::format("exception on %s",CurrentThread->getName()));
}
void MicroService::Exit(int Reason) {
std::exit(Reason);
}
void MicroService::BusMessageReceived(const std::string &Key, const std::string & Message) {
std::lock_guard G(InfraMutex_);
try {
Poco::JSON::Parser P;
auto Object = P.parse(Message).extract<Poco::JSON::Object::Ptr>();
if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) &&
Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) {
uint64_t ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID);
auto Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString();
if (ID != ID_) {
if( Event==KafkaTopics::ServiceEvents::EVENT_JOIN ||
Event==KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE ||
Event==KafkaTopics::ServiceEvents::EVENT_LEAVE ) {
if( Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) &&
Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) &&
Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) &&
Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) &&
Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) {
if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(ID) != Services_.end()) {
Services_[ID].LastUpdate = std::time(nullptr);
} else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) {
Services_.erase(ID);
logger().information(Poco::format("Service %s ID=%Lu leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID));
} else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) {
logger().information(Poco::format("Service %s ID=%Lu joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID));
Services_[ID] = MicroServiceMeta{
.Id = ID,
.Type = Poco::toLower(Object->get(KafkaTopics::ServiceEvents::Fields::TYPE).toString()),
.PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),
.PublicEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC).toString(),
.AccessKey = Object->get(KafkaTopics::ServiceEvents::Fields::KEY).toString(),
.Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN).toString(),
.LastUpdate = (uint64_t)std::time(nullptr)};
for (const auto &[Id, Svc] : Services_) {
logger().information(Poco::format("ID: %Lu Type: %s EndPoint: %s",Id,Svc.Type,Svc.PrivateEndPoint));
}
}
} else {
logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing a field.",Event));
}
} else if (Event==KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) {
if(Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) {
#ifndef TIP_SECURITY_SERVICE
AuthClient()->RemovedCachedToken(Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString());
#endif
} else {
logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing token",Event));
}
} else {
logger().error(Poco::format("Unknown Event: %s Source: %Lu", Event, ID));
}
}
} else {
logger().error("Bad bus message.");
}
auto i=Services_.begin();
auto Now = (uint64_t )std::time(nullptr);
for(;i!=Services_.end();) {
if((Now - i->second.LastUpdate)>60) {
i = Services_.erase(i);
} else
++i;
}
} catch (const Poco::Exception &E) {
logger().log(E);
}
}
MicroServiceMetaVec MicroService::GetServices(const std::string & Type) {
std::lock_guard G(InfraMutex_);
auto T = Poco::toLower(Type);
MicroServiceMetaVec Res;
for(const auto &[Id,ServiceRec]:Services_) {
if(ServiceRec.Type==T)
Res.push_back(ServiceRec);
}
return Res;
}
MicroServiceMetaVec MicroService::GetServices() {
std::lock_guard G(InfraMutex_);
MicroServiceMetaVec Res;
for(const auto &[Id,ServiceRec]:Services_) {
Res.push_back(ServiceRec);
}
return Res;
}
void MicroService::LoadConfigurationFile() {
std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,".");
Poco::Path ConfigFile;
ConfigFile = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_;
if(!ConfigFile.isFile())
{
std::cerr << DAEMON_APP_NAME << ": Configuration "
<< ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR
+ " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl;
std::exit(Poco::Util::Application::EXIT_CONFIG);
}
loadConfiguration(ConfigFile.toString());
}
void MicroService::Reload() {
LoadConfigurationFile();
LoadMyConfig();
}
void MicroService::LoadMyConfig() {
std::string KeyFile = ConfigPath("openwifi.service.key");
std::string KeyFilePassword = ConfigPath("openwifi.service.key.password" , "" );
AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword));
Cipher_ = CipherFactory_.createCipher(*AppKey_);
ID_ = Utils::GetSystemId();
if(!DebugMode_)
DebugMode_ = ConfigGetBool("openwifi.system.debug",false);
MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private");
MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public");
UIURI_ = ConfigGetString("openwifi.system.uri.ui");
MyHash_ = CreateHash(MyPublicEndPoint_);
}
void MicroService::initialize(Poco::Util::Application &self) {
// add the default services
SubSystems_.push_back(KafkaManager());
SubSystems_.push_back(ALBHealthCheckServer());
Poco::Net::initializeSSL();
Poco::Net::HTTPStreamFactory::registerFactory();
Poco::Net::HTTPSStreamFactory::registerFactory();
Poco::Net::FTPStreamFactory::registerFactory();
Poco::Net::FTPSStreamFactory::registerFactory();
LoadConfigurationFile();
static const char * LogFilePathKey = "logging.channels.c2.path";
if(LogDir_.empty()) {
std::string OriginalLogFileValue = ConfigPath(LogFilePathKey);
config().setString(LogFilePathKey, OriginalLogFileValue);
} else {
config().setString(LogFilePathKey, LogDir_);
}
Poco::File DataDir(ConfigPath("openwifi.system.data"));
DataDir_ = DataDir.path();
if(!DataDir.exists()) {
try {
DataDir.createDirectory();
} catch (const Poco::Exception &E) {
logger().log(E);
}
}
LoadMyConfig();
InitializeSubSystemServers();
ServerApplication::initialize(self);
Types::TopicNotifyFunction F = [this](std::string s1,std::string s2) { this->BusMessageReceived(s1,s2); };
KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F);
}
void MicroService::uninitialize() {
// add your own uninitialization code here
ServerApplication::uninitialize();
}
void MicroService::reinitialize(Poco::Util::Application &self) {
ServerApplication::reinitialize(self);
// add your own reinitialization code here
}
void MicroService::defineOptions(Poco::Util::OptionSet &options) {
ServerApplication::defineOptions(options);
options.addOption(
Poco::Util::Option("help", "", "display help information on command line arguments")
.required(false)
.repeatable(false)
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleHelp)));
options.addOption(
Poco::Util::Option("file", "", "specify the configuration file")
.required(false)
.repeatable(false)
.argument("file")
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleConfig)));
options.addOption(
Poco::Util::Option("debug", "", "to run in debug, set to true")
.required(false)
.repeatable(false)
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleDebug)));
options.addOption(
Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)")
.required(false)
.repeatable(false)
.argument("dir")
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleLogs)));
options.addOption(
Poco::Util::Option("version", "", "get the version and quit.")
.required(false)
.repeatable(false)
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleVersion)));
}
void MicroService::handleHelp(const std::string &name, const std::string &value) {
HelpRequested_ = true;
displayHelp();
stopOptionsProcessing();
}
void MicroService::handleVersion(const std::string &name, const std::string &value) {
HelpRequested_ = true;
std::cout << Version() << std::endl;
stopOptionsProcessing();
}
void MicroService::handleDebug(const std::string &name, const std::string &value) {
if(value == "true")
DebugMode_ = true ;
}
void MicroService::handleLogs(const std::string &name, const std::string &value) {
LogDir_ = value;
}
void MicroService::handleConfig(const std::string &name, const std::string &value) {
ConfigFileName_ = value;
}
void MicroService::displayHelp() {
Poco::Util::HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP.");
helpFormatter.format(std::cout);
}
void MicroService::InitializeSubSystemServers() {
for(auto i:SubSystems_)
addSubsystem(i);
}
void MicroService::StartSubSystemServers() {
for(auto i:SubSystems_) {
i->Start();
}
BusEventManager_.Start();
}
void MicroService::StopSubSystemServers() {
BusEventManager_.Stop();
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
(*i)->Stop();
}
std::string MicroService::CreateUUID() {
return UUIDGenerator_.create().toString();
}
bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
try {
auto P = Poco::Logger::parseLevel(Level);
auto Sub = Poco::toLower(SubSystem);
if (Sub == "all") {
for (auto i : SubSystems_) {
i->Logger().setLevel(P);
}
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);
return true;
}
}
}
} catch (const Poco::Exception & E) {
std::cout << "Exception" << std::endl;
}
return false;
}
void MicroService::Reload(const std::string &Sub) {
for (auto i : SubSystems_) {
if (Poco::toLower(Sub) == Poco::toLower(i->Name())) {
i->reinitialize(Poco::Util::Application::instance());
return;
}
}
}
Types::StringVec MicroService::GetSubSystems() const {
Types::StringVec Result;
for(auto i:SubSystems_)
Result.push_back(Poco::toLower(i->Name()));
return Result;
}
Types::StringPairVec MicroService::GetLogLevels() {
Types::StringPairVec Result;
for(auto &i:SubSystems_) {
auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel()));
Result.push_back(P);
}
return Result;
}
const Types::StringVec & MicroService::GetLogLevelNames() {
static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" };
return LevelNames;
}
uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) {
return (uint64_t) config().getInt64(Key,Default);
}
uint64_t MicroService::ConfigGetInt(const std::string &Key) {
return config().getInt(Key);
}
uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) {
return config().getBool(Key,Default);
}
uint64_t MicroService::ConfigGetBool(const std::string &Key) {
return config().getBool(Key);
}
std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) {
return config().getString(Key, Default);
}
std::string MicroService::ConfigGetString(const std::string &Key) {
return config().getString(Key);
}
std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) {
std::string R = config().getString(Key, Default);
return Poco::Path::expand(R);
}
std::string MicroService::ConfigPath(const std::string &Key) {
std::string R = config().getString(Key);
return Poco::Path::expand(R);
}
std::string MicroService::Encrypt(const std::string &S) {
return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
}
std::string MicroService::Decrypt(const std::string &S) {
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
}
std::string MicroService::CreateHash(const std::string &S) {
SHA2_.update(S);
return Utils::ToHex(SHA2_.digest());
}
std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const {
Poco::JSON::Object Obj;
Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type);
Obj.set(KafkaTopics::ServiceEvents::Fields::ID,ID_);
Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE,Poco::toLower(DAEMON_APP_NAME));
Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC,MyPublicEndPoint_);
Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE,MyPrivateEndPoint_);
Obj.set(KafkaTopics::ServiceEvents::Fields::KEY,MyHash_);
Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN,Version_);
std::stringstream ResultText;
Poco::JSON::Stringifier::stringify(Obj, ResultText);
return ResultText.str();
}
void BusEventManager::run() {
Running_ = true;
auto Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false);
while(Running_) {
Poco::Thread::trySleep((unsigned long)Daemon()->DaemonBusTimer());
if(!Running_)
break;
Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false);
}
Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false);
};
void BusEventManager::Start() {
if(KafkaManager()->Enabled()) {
Thread_.start(*this);
}
}
void BusEventManager::Stop() {
if(KafkaManager()->Enabled()) {
Running_ = false;
Thread_.wakeUp();
Thread_.join();
}
}
[[nodiscard]] bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) {
try {
auto APIKEY = Request.get("X-API-KEY");
return APIKEY == MyHash_;
} catch (const Poco::Exception &E) {
logger().log(E);
}
return false;
}
void MicroService::SavePID() {
try {
std::ofstream O;
O.open(Daemon()->DataDir() + "/pidfile",std::ios::binary | std::ios::trunc);
O << Poco::Process::id();
O.close();
} catch (...)
{
std::cout << "Could not save system ID" << std::endl;
}
}
int MicroService::main(const ArgVec &args) {
MyErrorHandler ErrorHandler(*this);
Poco::ErrorHandler::set(&ErrorHandler);
if (!HelpRequested_) {
SavePID();
Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME);
logger.notice(Poco::format("Starting %s version %s.",DAEMON_APP_NAME, Version()));
if(Poco::Net::Socket::supportsIPv6())
logger.information("System supports IPv6.");
else
logger.information("System does NOT support IPv6.");
if (config().getBool("application.runAsDaemon", false)) {
logger.information("Starting as a daemon.");
}
logger.information(Poco::format("System ID set to %Lu",ID_));
StartSubSystemServers();
waitForTerminationRequest();
StopSubSystemServers();
logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME));
}
return Application::EXIT_OK;
}
}

184
src/MicroService.h Normal file
View File

@@ -0,0 +1,184 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRALGW_MICROSERVICE_H
#define UCENTRALGW_MICROSERVICE_H
#include <array>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <set>
#include "Poco/Util/Application.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/UUIDGenerator.h"
#include "Poco/ErrorHandler.h"
#include "Poco/Crypto/RSAKey.h"
#include "Poco/Crypto/CipherFactory.h"
#include "Poco/Crypto/Cipher.h"
#include "Poco/SHA2Engine.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Process.h"
#include "OpenWifiTypes.h"
#include "SubSystemServer.h"
namespace OpenWifi {
static const std::string uSERVICE_SECURITY{"owsec"};
static const std::string uSERVICE_GATEWAY{"owgw"};
static const std::string uSERVICE_FIRMWARE{ "owfms"};
static const std::string uSERVICE_TOPOLOGY{ "owtopo"};
static const std::string uSERVICE_PROVISIONING{ "owprov"};
static const std::string uSERVICE_OWLS{ "owls"};
class MyErrorHandler : public Poco::ErrorHandler {
public:
explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {}
void exception(const Poco::Exception & E) override;
void exception(const std::exception & E) override;
void exception() override;
private:
Poco::Util::Application &App_;
};
class BusEventManager : public Poco::Runnable {
public:
void run() override;
void Start();
void Stop();
private:
std::atomic_bool Running_ = false;
Poco::Thread Thread_;
};
struct MicroServiceMeta {
uint64_t Id=0;
std::string Type;
std::string PrivateEndPoint;
std::string PublicEndPoint;
std::string AccessKey;
std::string Version;
uint64_t LastUpdate=0;
};
typedef std::map<uint64_t, MicroServiceMeta> MicroServiceMetaMap;
typedef std::vector<MicroServiceMeta> MicroServiceMetaVec;
class MicroService : public Poco::Util::ServerApplication {
public:
explicit MicroService( std::string PropFile,
std::string RootEnv,
std::string ConfigVar,
std::string AppName,
uint64_t BusTimer,
Types::SubSystemVec Subsystems) :
DAEMON_PROPERTIES_FILENAME(std::move(PropFile)),
DAEMON_ROOT_ENV_VAR(std::move(RootEnv)),
DAEMON_CONFIG_ENV_VAR(std::move(ConfigVar)),
DAEMON_APP_NAME(std::move(AppName)),
DAEMON_BUS_TIMER(BusTimer),
SubSystems_(std::move(Subsystems)) {
}
int main(const ArgVec &args) override;
void initialize(Application &self) override;
void uninitialize() override;
void reinitialize(Application &self) override;
void defineOptions(Poco::Util::OptionSet &options) override;
void handleHelp(const std::string &name, const std::string &value);
void handleVersion(const std::string &name, const std::string &value);
void handleDebug(const std::string &name, const std::string &value);
void handleLogs(const std::string &name, const std::string &value);
void handleConfig(const std::string &name, const std::string &value);
void displayHelp();
void InitializeSubSystemServers();
void StartSubSystemServers();
void StopSubSystemServers();
void Exit(int Reason);
bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level);
[[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]] std::string CreateUUID();
[[nodiscard]] bool Debug() const { return DebugMode_; }
[[nodiscard]] uint64_t ID() const { return ID_; }
[[nodiscard]] Types::StringVec GetSubSystems() const;
[[nodiscard]] Types::StringPairVec GetLogLevels() ;
[[nodiscard]] static const Types::StringVec & GetLogLevelNames();
[[nodiscard]] std::string ConfigGetString(const std::string &Key,const std::string & Default);
[[nodiscard]] std::string ConfigGetString(const std::string &Key);
[[nodiscard]] std::string ConfigPath(const std::string &Key,const std::string & Default);
[[nodiscard]] std::string ConfigPath(const std::string &Key);
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key,uint64_t Default);
[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key);
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key,bool Default);
[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key);
[[nodiscard]] std::string Encrypt(const std::string &S);
[[nodiscard]] std::string Decrypt(const std::string &S);
[[nodiscard]] std::string CreateHash(const std::string &S);
[[nodiscard]] std::string Hash() const { return MyHash_; };
[[nodiscard]] std::string ServiceType() const { return DAEMON_APP_NAME; };
[[nodiscard]] std::string PrivateEndPoint() const { return MyPrivateEndPoint_; };
[[nodiscard]] std::string PublicEndPoint() const { return MyPublicEndPoint_; };
[[nodiscard]] std::string MakeSystemEventMessage( const std::string & Type ) const ;
[[nodiscard]] const Types::SubSystemVec & GetFullSubSystems() { return SubSystems_; }
inline uint64_t DaemonBusTimer() const { return DAEMON_BUS_TIMER; };
void BusMessageReceived( const std::string & Key, const std::string & Message);
[[nodiscard]] MicroServiceMetaVec GetServices(const std::string & type);
[[nodiscard]] MicroServiceMetaVec GetServices();
[[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request);
[[nodiscard]] const std::string & AppName() { return DAEMON_APP_NAME; }
static void SavePID();
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_;};
void Reload(const std::string &Name); // reload a subsystem
void Reload(); // reload the daemon itself
void LoadMyConfig();
void LoadConfigurationFile();
private:
bool HelpRequested_ = false;
std::string LogDir_;
std::string ConfigFileName_;
Poco::UUIDGenerator UUIDGenerator_;
uint64_t ID_ = 1;
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_ = nullptr;
bool DebugMode_ = false;
std::string DataDir_;
Types::SubSystemVec SubSystems_;
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
Poco::Crypto::Cipher * Cipher_ = nullptr;
Poco::SHA2Engine SHA2_;
MicroServiceMetaMap Services_;
std::string MyHash_;
std::string MyPrivateEndPoint_;
std::string MyPublicEndPoint_;
std::string UIURI_;
std::string Version_{std::string(APP_VERSION) + "("+ BUILD_NUMBER + ")"};
BusEventManager BusEventManager_;
std::mutex InfraMutex_;
std::string DAEMON_PROPERTIES_FILENAME;
std::string DAEMON_ROOT_ENV_VAR;
std::string DAEMON_CONFIG_ENV_VAR;
std::string DAEMON_APP_NAME;
uint64_t DAEMON_BUS_TIMER;
};
}
#endif // UCENTRALGW_MICROSERVICE_H

819
src/NewWebSocketServer.cpp Normal file
View File

@@ -0,0 +1,819 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <cerrno>
#include "Poco/Net/IPAddress.h"
#include "Poco/Net/SSLException.h"
#include "Poco/Net/HTTPServerSession.h"
#include "Poco/Net/HTTPHeaderStream.h"
#include "Poco/Net/HTTPServerRequestImpl.h"
#include "Poco/JSON/Array.h"
#include "Poco/zlib.h"
#include "CommandManager.h"
#include "Daemon.h"
#include "KafkaManager.h"
#include "Kafka_topics.h"
#include "StorageService.h"
#include "Utils.h"
#include "WebSocketServer.h"
#include "uCentralProtocol.h"
#include "TelemetryStream.h"
namespace OpenWifi {
class WebSocketServer *WebSocketServer::instance_ = nullptr;
bool WebSocketServer::ValidateCertificate(const std::string & ConnectionId, const Poco::Crypto::X509Certificate & Certificate) {
if(IsCertOk()) {
Logger_.debug(Poco::format("CERTIFICATE(%s): issuer='%s' cn='%s'", ConnectionId, Certificate.issuerName(),Certificate.commonName()));
if(!Certificate.issuedBy(*IssuerCert_)) {
Logger_.debug(Poco::format("CERTIFICATE(%s): issuer mismatch. Local='%s' Incoming='%s'", ConnectionId, IssuerCert_->issuerName(), Certificate.issuerName()));
return false;
}
return true;
}
return false;
}
int WebSocketServer::Start() {
std::cout << __LINE__ << std::endl;
ReactorPool_.Start();
std::cout << __LINE__ << std::endl;
Poco::Net::Context::Params P;
P.verificationMode = Poco::Net::Context::VERIFY_STRICT;
P.certificateFile = Daemon()->ConfigPath("ucentral.websocket.host.0.cert");
P.privateKeyFile = Daemon()->ConfigPath("ucentral.websocket.host.0.key");
P.loadDefaultCAs = true ;
P.caLocation = Daemon()->ConfigPath("ucentral.websocket.host.0.cas");
P.verificationDepth = 9 ;
P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH";
P.dhUse2048Bits = true;
std::cout << __LINE__ << std::endl;
Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard(Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 : Poco::Net::AddressFamily::IPv4));
std::cout << __LINE__ << std::endl;
Poco::Net::SocketAddress SockAddr(Addr, 15002);
std::cout << __LINE__ << std::endl;
auto Context = Poco::AutoPtr<Poco::Net::Context>(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P));
std::cout << __LINE__ << std::endl;
auto Params = new Poco::Net::HTTPServerParams;
std::cout << __LINE__ << std::endl;
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
std::cout << __LINE__ << std::endl;
auto Sock = Poco::Net::SecureServerSocket(SockAddr, 200, Context);
std::cout << __LINE__ << std::endl;
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new WebSocketRequestHandlerFactory(ReactorPool_,Logger_), Pool_, Sock, Params);
std::cout << __LINE__ << std::endl;
NewServer->start();
std::cout << __LINE__ << std::endl;
WebServers_.push_back(std::move(NewServer));
return 0;
}
void WebSocketServer::Stop() {
Logger_.notice("Stopping reactors...");
Logger_.information("Stopping ");
for( const auto & svr : WebServers_ )
svr->stop();
ReactorPool_.Stop();
}
void WSConnection::LogException(const Poco::Exception &E) {
Logger_.information(Poco::format("EXCEPTION(%s): %s",CId_,E.displayText()));
}
void WSConnection::CompleteStartup() {
std::lock_guard Guard(Mutex_);
try {
Socket_ = *WS_;
std::cout << __LINE__ << std::endl;
auto SS = dynamic_cast<Poco::Net::SecureStreamSocketImpl *>(WS_->impl());
std::cout << __LINE__ << std::endl;
SS->completeHandshake();
std::cout << __LINE__ << std::endl;
CId_ = Utils::FormatIPv6(SS->peerAddress().toString());
std::cout << __LINE__ << std::endl;
if (!SS->secure()) {
Logger_.error(Poco::format("%s: Connection is NOT secure.", CId_));
} else {
Logger_.debug(Poco::format("%s: Connection is secure.", CId_));
}
std::cout << __LINE__ << std::endl;
if (SS->havePeerCertificate()) {
// Get the cert info...
CertValidation_ = GWObjects::VALID_CERTIFICATE;
try {
Poco::Crypto::X509Certificate PeerCert(SS->peerCertificate());
if (WebSocketServer()->ValidateCertificate(CId_, PeerCert)) {
CN_ = Poco::trim(Poco::toLower(PeerCert.commonName()));
CertValidation_ = GWObjects::MISMATCH_SERIAL;
Logger_.debug(Poco::format("%s: Valid certificate: CN=%s", CId_, CN_));
} else {
Logger_.debug(Poco::format("%s: Certificate is not valid", CId_));
}
} catch (const Poco::Exception &E) {
LogException(E);
}
std::cout << __LINE__ << std::endl;
} else {
Logger_.error(Poco::format("%s: No certificates available..", CId_));
}
std::cout << __LINE__ << std::endl;
WS_->setMaxPayloadSize(BufSize);
auto TS = Poco::Timespan(240,0);
WS_->setReceiveTimeout(TS);
WS_->setNoDelay(true);
WS_->setKeepAlive(true);
Reactor_.addEventHandler(*WS_,
Poco::NObserver<WSConnection, Poco::Net::ReadableNotification>(
*this, &WSConnection::OnSocketReadable));
Reactor_.addEventHandler(*WS_,
Poco::NObserver<WSConnection, Poco::Net::ShutdownNotification>(
*this, &WSConnection::OnSocketShutdown));
Reactor_.addEventHandler(*WS_,
Poco::NObserver<WSConnection, Poco::Net::ErrorNotification>(
*this, &WSConnection::OnSocketError));
Registered_ = true;
Logger_.information(Poco::format("CONNECTION(%s): completed.",CId_));
std::cout << __LINE__ << std::endl;
return;
} catch (const Poco::Exception &E ) {
Logger_.error("Exception caught during device connection. Device will have to retry.");
}
delete this;
}
WSConnection::WSConnection(Poco::SharedPtr<Poco::Net::WebSocket> WS, Poco::Net::SocketReactor& Reactor, Poco::Logger &Logger):
WS_(WS), Reactor_(Reactor), Logger_(WebSocketServer()->Logger())
{
std::cout << __LINE__ << std::endl;
std::thread T([this](){ this->CompleteStartup();});
std::cout << __LINE__ << std::endl;
T.detach();
std::cout << __LINE__ << std::endl;
}
WSConnection::~WSConnection() {
DeviceRegistry()->UnRegister(SerialNumber_,this);
if(Registered_ && WS_)
{
Reactor_.removeEventHandler(*WS_,
Poco::NObserver<WSConnection,
Poco::Net::ReadableNotification>(*this,&WSConnection::OnSocketReadable));
Reactor_.removeEventHandler(*WS_,
Poco::NObserver<WSConnection,
Poco::Net::ShutdownNotification>(*this,&WSConnection::OnSocketShutdown));
Reactor_.removeEventHandler(*WS_,
Poco::NObserver<WSConnection,
Poco::Net::ErrorNotification>(*this,&WSConnection::OnSocketError));
(*WS_).close();
Socket_.shutdown();
} else if(WS_) {
(*WS_).close();
Socket_.shutdown();
}
if(KafkaManager()->Enabled() && !SerialNumber_.empty()) {
Poco::JSON::Object Disconnect;
Poco::JSON::Object Details;
Details.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
Details.set(uCentralProtocol::TIMESTAMP,std::time(nullptr));
Disconnect.set(uCentralProtocol::DISCONNECTION,Details);
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(Disconnect,OS);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
}
}
bool WSConnection::LookForUpgrade(uint64_t UUID) {
// A UUID of zero means ignore updates for that connection.
if(UUID==0)
return false;
std::string NewConfig;
uint64_t NewConfigUUID = 0 ;
if (Storage()->ExistingConfiguration(SerialNumber_,UUID, NewConfig, NewConfigUUID)) {
// Device is already using the latest configuration.
if(UUID == NewConfigUUID)
return false;
// if the new config is already pending,
if(NewConfigUUID == Conn_->PendingUUID)
return false;
Conn_->PendingUUID = NewConfigUUID;
Poco::JSON::Parser Parser( new Poco::JSON::ParseHandler);
auto ParsedConfig = Parser.parse(NewConfig).extract<Poco::JSON::Object::Ptr>();
ParsedConfig->set(uCentralProtocol::UUID,NewConfigUUID);
// create the command stub...
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = uCentralProtocol::SUBMITTED_BY_SYSTEM;
Cmd.Status = uCentralProtocol::PENDING;
Cmd.Command = uCentralProtocol::CONFIGURE;
Poco::JSON::Object Params;
Params.set(uCentralProtocol::SERIAL, SerialNumber_);
Params.set(uCentralProtocol::UUID, NewConfigUUID);
Params.set(uCentralProtocol::WHEN, 0);
Params.set(uCentralProtocol::CONFIG, ParsedConfig);
std::string Log = Poco::format("CFG-UPGRADE(%s):, Current ID: %Lu, newer configuration %Lu.", SerialNumber_, UUID, NewConfigUUID);
Storage()->AddLog(SerialNumber_, Conn_->UUID, Log);
Logger_.debug(Log);
uint64_t RPC_Id;
CommandManager()->SendCommand(SerialNumber_ , Cmd.Command, Params, Cmd.UUID, RPC_Id);
Storage()->AddCommand(SerialNumber_, Cmd, Storage::COMMAND_EXECUTED);
return true;
}
return false;
}
bool WSConnection::ExtractCompressedData(const std::string & CompressedData, std::string & UnCompressedData)
{
std::vector<uint8_t> OB = Utils::base64decode(CompressedData);
unsigned long MaxSize=OB.size()*10;
std::vector<char> UncompressedBuffer(MaxSize);
unsigned long FinalSize = MaxSize;
if(uncompress((Bytef *)&UncompressedBuffer[0], & FinalSize, (Bytef *)&OB[0],OB.size())==Z_OK) {
UncompressedBuffer[FinalSize] = 0;
UnCompressedData = &UncompressedBuffer[0];
return true;
}
return false;
}
void WSConnection::ProcessJSONRPCResult(Poco::JSON::Object::Ptr & Doc) {
CommandManager()->PostCommandResult(SerialNumber_, Doc);
}
void WSConnection::ProcessJSONRPCEvent(Poco::JSON::Object::Ptr & Doc) {
auto Method = Doc->get(uCentralProtocol::METHOD).toString();
auto EventType = uCentralProtocol::EventFromString(Method);
if(EventType == uCentralProtocol::ET_UNKNOWN) {
Logger_.error(Poco::format("ILLEGAL-PROTOCOL(%s): Unknown message type '%s'",Method));
Errors_++;
return;
}
if(!Doc->isObject(uCentralProtocol::PARAMS))
{
Logger_.warning(Poco::format("MISSING-PARAMS(%s): params must be an object.",CId_));
Errors_++;
return;
}
// expand params if necessary
auto ParamsObj = Doc->get(uCentralProtocol::PARAMS).extract<Poco::JSON::Object::Ptr>();
if(ParamsObj->has(uCentralProtocol::COMPRESS_64))
{
std::string UncompressedData;
if(ExtractCompressedData(ParamsObj->get(uCentralProtocol::COMPRESS_64).toString(),UncompressedData)) {
Logger_.debug(Poco::format("EVENT(%s): Found compressed payload expanded to '%s'.",CId_, UncompressedData));
Poco::JSON::Parser Parser;
ParamsObj = Parser.parse(UncompressedData).extract<Poco::JSON::Object::Ptr>();
} else {
Logger_.warning(Poco::format("INVALID-COMPRESSED-DATA(%s): Compressed cannot be uncompressed - content must be corrupt..",CId_));
Errors_++;
return;
}
}
if(!ParamsObj->has(uCentralProtocol::SERIAL))
{
Logger_.warning(Poco::format("MISSING-PARAMS(%s): Serial number is missing in message.",CId_));
return;
}
auto Serial = Poco::trim(Poco::toLower(ParamsObj->get(uCentralProtocol::SERIAL).toString()));
if(!Utils::ValidSerialNumber(Serial)) {
Poco::Exception E(Poco::format("ILLEGAL-DEVICE-NAME(%s): device name is illegal and not allowed to connect.",Serial), EACCES);
E.rethrow();
}
if(Storage()->IsBlackListed(Serial)) {
Poco::Exception E(Poco::format("BLACKLIST(%s): device is blacklisted and not allowed to connect.",Serial), EACCES);
E.rethrow();
}
if(Conn_!= nullptr)
Conn_->LastContact = std::time(nullptr);
switch(EventType) {
case uCentralProtocol::ET_CONNECT: {
if( ParamsObj->has(uCentralProtocol::UUID) &&
ParamsObj->has(uCentralProtocol::FIRMWARE) &&
ParamsObj->has(uCentralProtocol::CAPABILITIES)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto Firmware = ParamsObj->get(uCentralProtocol::FIRMWARE).toString();
auto Capabilities = ParamsObj->get(uCentralProtocol::CAPABILITIES).toString();
Conn_ = DeviceRegistry()->Register(Serial, this);
SerialNumber_ = Serial;
Conn_->SerialNumber = Serial;
Conn_->UUID = UUID;
Conn_->Firmware = Firmware;
Conn_->PendingUUID = 0;
Conn_->LastContact = std::time(nullptr);
Conn_->Address = Utils::FormatIPv6(WS_->peerAddress().toString());
CId_ = SerialNumber_ + "@" + CId_ ;
// We need to verify the certificate if we have one
if(!CN_.empty() && Utils::SerialNumberMatch(CN_,SerialNumber_)) {
CertValidation_ = GWObjects::VERIFIED;
Logger_.information(Poco::format("CONNECT(%s): Fully validated and authenticated device..", CId_));
} else {
if(CN_.empty())
Logger_.information(Poco::format("CONNECT(%s): Not authenticated or validated.", CId_));
else
Logger_.information(Poco::format("CONNECT(%s): Authenticated but not validated. Serial='%s' CN='%s'", CId_, Serial, CN_));
}
Conn_->VerifiedCertificate = CertValidation_;
if (Daemon()->AutoProvisioning() && !Storage()->DeviceExists(SerialNumber_)) {
Storage()->CreateDefaultDevice(SerialNumber_, Capabilities, Firmware, Compatible_);
} else if (Storage()->DeviceExists(SerialNumber_)) {
Storage()->UpdateDeviceCapabilities(SerialNumber_, Capabilities, Compatible_);
if(!Firmware.empty()) {
Storage()->SetConnectInfo(SerialNumber_, Firmware );
}
}
Conn_->Compatible = Compatible_;
StatsProcessor_ = std::make_unique<StateProcessor>(Conn_);
StatsProcessor_->Initialize(Serial);
LookForUpgrade(UUID);
if(KafkaManager()->Enabled()) {
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(ParamsObj,OS);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_, OS.str());
}
} else {
Logger_.warning(Poco::format("CONNECT(%s): Missing one of uuid, firmware, or capabilities",CId_));
return;
}
}
break;
case uCentralProtocol::ET_STATE: {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::STATE)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto State = ParamsObj->get(uCentralProtocol::STATE).toString();
std::string request_uuid;
if (ParamsObj->has(uCentralProtocol::REQUEST_UUID))
request_uuid = ParamsObj->get(uCentralProtocol::REQUEST_UUID).toString();
if (request_uuid.empty())
Logger_.debug(Poco::format("STATE(%s): UUID=%Lu Updating.", CId_, UUID));
else
Logger_.debug(Poco::format("STATE(%s): UUID=%Lu Updating for CMD=%s.", CId_,
UUID, request_uuid));
Conn_->UUID = UUID;
Storage()->AddStatisticsData(Serial, UUID, State);
DeviceRegistry()->SetStatistics(Serial, State);
if (!request_uuid.empty()) {
Storage()->SetCommandResult(request_uuid, State);
}
if (StatsProcessor_)
StatsProcessor_->Add(State);
if(KafkaManager()->Enabled()) {
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(ParamsObj,OS);
KafkaManager()->PostMessage(KafkaTopics::STATE, SerialNumber_, OS.str());
}
} else {
Logger_.warning(Poco::format(
"STATE(%s): Invalid request. Missing serial, uuid, or state", CId_));
}
}
break;
case uCentralProtocol::ET_HEALTHCHECK: {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::SANITY) && ParamsObj->has(uCentralProtocol::DATA)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
auto Sanity = ParamsObj->get(uCentralProtocol::SANITY);
auto CheckData = ParamsObj->get(uCentralProtocol::DATA).toString();
if (CheckData.empty())
CheckData = uCentralProtocol::EMPTY_JSON_DOC;
std::string request_uuid;
if (ParamsObj->has(uCentralProtocol::REQUEST_UUID))
request_uuid = ParamsObj->get(uCentralProtocol::REQUEST_UUID).toString();
if (request_uuid.empty())
Logger_.debug(
Poco::format("HEALTHCHECK(%s): UUID=%Lu Updating.", CId_, UUID));
else
Logger_.debug(Poco::format("HEALTHCHECK(%s): UUID=%Lu Updating for CMD=%s.",
CId_, UUID, request_uuid));
Conn_->UUID = UUID;
GWObjects::HealthCheck Check;
Check.Recorded = std::time(nullptr);
Check.UUID = UUID;
Check.Data = CheckData;
Check.Sanity = Sanity;
Storage()->AddHealthCheckData(Serial, Check);
if (!request_uuid.empty()) {
Storage()->SetCommandResult(request_uuid, CheckData);
}
DeviceRegistry()->SetHealthcheck(Serial, Check);
if(KafkaManager()->Enabled()) {
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
ParamsObj->set("timestamp",std::time(nullptr));
Stringify.condense(ParamsObj,OS);
KafkaManager()->PostMessage(KafkaTopics::HEALTHCHECK, SerialNumber_, OS.str());
}
} else {
Logger_.warning(Poco::format("HEALTHCHECK(%s): Missing parameter", CId_));
return;
}
}
break;
case uCentralProtocol::ET_LOG: {
if (ParamsObj->has(uCentralProtocol::LOG) && ParamsObj->has(uCentralProtocol::SEVERITY)) {
Logger_.debug(Poco::format("LOG(%s): new entry.", CId_));
auto Log = ParamsObj->get(uCentralProtocol::LOG).toString();
auto Severity = ParamsObj->get(uCentralProtocol::SEVERITY);
std::string DataStr = uCentralProtocol::EMPTY_JSON_DOC;
if (ParamsObj->has(uCentralProtocol::DATA)) {
auto DataObj = ParamsObj->get(uCentralProtocol::DATA);
if (DataObj.isStruct())
DataStr = DataObj.toString();
}
GWObjects::DeviceLog DeviceLog{.Log = Log,
.Data = DataStr,
.Severity = Severity,
.Recorded = (uint64_t)time(nullptr),
.LogType = 0,
.UUID = Conn_->UUID};
Storage()->AddLog(Serial, DeviceLog);
} else {
Logger_.warning(Poco::format("LOG(%s): Missing parameters.", CId_));
return;
}
}
break;
case uCentralProtocol::ET_CRASHLOG: {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::LOGLINES)) {
Logger_.debug(Poco::format("CRASH-LOG(%s): new entry.", CId_));
auto LogLines = ParamsObj->get(uCentralProtocol::LOGLINES);
std::string LogText;
if (LogLines.isArray()) {
auto LogLinesArray = LogLines.extract<Poco::JSON::Array::Ptr>();
for (const auto &i : *LogLinesArray)
LogText += i.toString() + "\r\n";
}
GWObjects::DeviceLog DeviceLog{
.Log = LogText,
.Data = "",
.Severity = GWObjects::DeviceLog::LOG_EMERG,
.Recorded = (uint64_t)time(nullptr),
.LogType = 1,
.UUID = Conn_->UUID};
Storage()->AddLog(Serial, DeviceLog, true);
} else {
Logger_.warning(Poco::format("LOG(%s): Missing parameters.", CId_));
return;
}
}
break;
case uCentralProtocol::ET_PING: {
if (ParamsObj->has(uCentralProtocol::UUID)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
Logger_.debug(Poco::format("PING(%s): Current config is %Lu", CId_, UUID));
} else {
Logger_.warning(Poco::format("PING(%s): Missing parameter.", CId_));
}
}
break;
case uCentralProtocol::ET_CFGPENDING: {
if (ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::ACTIVE)) {
uint64_t UUID = ParamsObj->get(uCentralProtocol::UUID);
uint64_t Active = ParamsObj->get(uCentralProtocol::ACTIVE);
Logger_.debug(Poco::format("CFG-PENDING(%s): Active: %Lu Target: %Lu", CId_,
Active, UUID));
} else {
Logger_.warning(Poco::format("CFG-PENDING(%s): Missing some parameters", CId_));
}
}
break;
case uCentralProtocol::ET_RECOVERY: {
if (ParamsObj->has(uCentralProtocol::SERIAL) && ParamsObj->has(uCentralProtocol::FIRMWARE) &&
ParamsObj->has(uCentralProtocol::UUID) && ParamsObj->has(uCentralProtocol::REBOOT) &&
ParamsObj->has(uCentralProtocol::LOGLINES)) {
auto LogLines = ParamsObj->get(uCentralProtocol::LOGLINES);
std::string LogText;
if (LogLines.isArray()) {
auto LogLinesArray = LogLines.extract<Poco::JSON::Array::Ptr>();
for (const auto &i : *LogLinesArray)
LogText += i.toString() + "\r\n";
}
} else {
Logger_.error(Poco::format(
"RECOVERY(%s): Recovery missing one of firmware, uuid, loglines, reboot",
Serial));
}
}
break;
case uCentralProtocol::ET_DEVICEUPDATE: {
if (ParamsObj->has("currentPassword")) {
auto Password = ParamsObj->get("currentPassword").toString();
Storage()->SetDevicePassword(Serial, Password);
Logger_.error(Poco::format(
"DEVICEUPDATE(%s): Device is updating its login password.", Serial));
}
}
break;
case uCentralProtocol::ET_TELEMETRY: {
std::cout << "Telemetry date..." << std::endl;
if(ParamsObj->has("data")) {
auto Payload = ParamsObj->get("data").toString();
TelemetryStream()->UpdateEndPoint(SerialNumber_, Payload);
}
}
break;
// this will never be called but some compilers will complain if we do not have a case for
// every single values of an enum
case uCentralProtocol::ET_UNKNOWN: {
Logger_.error(Poco::format("ILLEGAL-EVENT(%s): Event '%s' unknown", CId_, Method));
Errors_++;
}
}
}
void WSConnection::OnSocketShutdown(const Poco::AutoPtr<Poco::Net::ShutdownNotification>& pNf) {
std::lock_guard Guard(Mutex_);
Logger_.information(Poco::format("SOCKET-SHUTDOWN(%s): Closing.",CId_));
delete this;
}
void WSConnection::OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification>& pNf) {
std::lock_guard Guard(Mutex_);
Logger_.information(Poco::format("SOCKET-ERROR(%s): Closing.",CId_));
delete this;
}
void WSConnection::OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf) {
std::lock_guard Guard(Mutex_);
try
{
ProcessIncomingFrame();
}
catch (const Poco::Exception & E)
{
Logger_.log(E);
delete this;
}
catch (const std::exception & E) {
std::string W = E.what();
Logger_.information(Poco::format("std::exception caught: %s. Connection terminated with %s",W,CId_));
delete this;
}
catch ( ... ) {
Logger_.information(Poco::format("Unknown exception for %s. Connection terminated.",CId_));
delete this;
}
}
std::string asString(Poco::Buffer<char> & buf ) {
if(buf.sizeBytes()>0) {
buf.append(0);
return buf.begin();
}
return "";
}
void WSConnection::ProcessIncomingFrame() {
bool MustDisconnect=false;
Poco::Buffer<char> IncomingFrame(0);
try {
int Op,flags;
int IncomingSize;
IncomingSize = WS_->receiveFrame(IncomingFrame,flags);
Op = flags & Poco::Net::WebSocket::FRAME_OP_BITMASK;
// std::cout << "ID:" << CId_ << " Size=" << IncomingSize << " Flags=" << flags << " Op=" << Op << std::endl;
if (IncomingSize == 0 && flags == 0 && Op == 0) {
Logger_.information(Poco::format("DISCONNECT(%s): device has disconnected.", CId_));
MustDisconnect = true;
} else {
switch (Op) {
case Poco::Net::WebSocket::FRAME_OP_PING: {
Logger_.debug(Poco::format("WS-PING(%s): received. PONG sent back.", CId_));
WS_->sendFrame("", 0,
(int)Poco::Net::WebSocket::FRAME_OP_PONG |
(int)Poco::Net::WebSocket::FRAME_FLAG_FIN);
if (KafkaManager()->Enabled() && Conn_) {
Poco::JSON::Object PingObject;
Poco::JSON::Object PingDetails;
PingDetails.set(uCentralProtocol::FIRMWARE, Conn_->Firmware);
PingDetails.set(uCentralProtocol::SERIALNUMBER, SerialNumber_);
PingDetails.set(uCentralProtocol::COMPATIBLE, Compatible_);
PingObject.set(uCentralProtocol::PING,PingDetails);
Poco::JSON::Stringifier Stringify;
std::ostringstream OS;
Stringify.condense(PingObject, OS);
KafkaManager()->PostMessage(KafkaTopics::CONNECTION, SerialNumber_,
OS.str());
}
}
break;
case Poco::Net::WebSocket::FRAME_OP_PONG: {
Logger_.debug(Poco::format("PONG(%s): received and ignored.",CId_));
}
break;
case Poco::Net::WebSocket::FRAME_OP_TEXT: {
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.debug(Poco::format("FRAME(%s): Frame received (length=%d, flags=0x%x). Msg=%s",
CId_, IncomingSize, unsigned(flags),IncomingMessageStr));
Poco::JSON::Parser parser;
auto ParsedMessage = parser.parse(IncomingMessageStr);
auto IncomingJSON = ParsedMessage.extract<Poco::JSON::Object::Ptr>();
if (IncomingJSON->has(uCentralProtocol::JSONRPC)) {
if(IncomingJSON->has(uCentralProtocol::METHOD) &&
IncomingJSON->has(uCentralProtocol::PARAMS)) {
ProcessJSONRPCEvent(IncomingJSON);
} else if (IncomingJSON->has(uCentralProtocol::RESULT) &&
IncomingJSON->has(uCentralProtocol::ID)) {
Logger_.debug(Poco::format("RPC-RESULT(%s): payload: %s",CId_,IncomingMessageStr));
ProcessJSONRPCResult(IncomingJSON);
} else {
Logger_.warning(Poco::format(
"INVALID-PAYLOAD(%s): Payload is not JSON-RPC 2.0: %s", CId_,
IncomingMessageStr));
}
} else {
Logger_.error(Poco::format("FRAME(%s): illegal transaction header, missing 'jsonrpc'",CId_));
Errors_++;
}
}
break;
case Poco::Net::WebSocket::FRAME_OP_CLOSE: {
Logger_.warning(Poco::format("CLOSE(%s): Device is closing its connection.",CId_));
MustDisconnect = true;
}
break;
default: {
Logger_.warning(Poco::format("UNKNOWN(%s): unknownWS Frame operation: %s",CId_, std::to_string(Op)));
}
break;
}
if (Conn_ != nullptr) {
Conn_->RX += IncomingSize;
Conn_->MessageCount++;
}
}
}
catch (const Poco::Net::ConnectionResetException & E)
{
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning(Poco::format("%s(%s): Caught a ConnectionResetException: %s, Message: %s",
std::string(__func__), CId_, E.displayText(),IncomingMessageStr));
MustDisconnect= true;
}
catch (const Poco::JSON::JSONException & E)
{
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning(Poco::format("%s(%s): Caught a JSONException: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
}
catch (const Poco::Net::WebSocketException & E)
{
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning(Poco::format("%s(%s): Caught a websocket exception: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException & E)
{
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning(Poco::format("%s(%s): Caught a SSLConnectionUnexpectedlyClosedException: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const Poco::Net::SSLException & E)
{
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning(Poco::format("%s(%s): Caught a SSL exception: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const Poco::Net::NetException & E) {
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning( Poco::format("%s(%s): Caught a NetException: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const Poco::IOException & E) {
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning( Poco::format("%s(%s): Caught a IOException: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const Poco::Exception &E) {
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning( Poco::format("%s(%s): Caught a more generic Poco exception: %s. Message: %s",
std::string(__func__), CId_, E.displayText(), IncomingMessageStr ));
MustDisconnect = true ;
}
catch (const std::exception & E) {
std::string IncomingMessageStr = asString(IncomingFrame);
Logger_.warning( Poco::format("%s(%s): Caught a std::exception: %s. Message: %s",
std::string{__func__}, CId_, std::string{E.what()}, IncomingMessageStr) );
MustDisconnect = true ;
}
if(!MustDisconnect && Errors_<10)
return;
if(Errors_>10) {
Logger_.information(Poco::format("DISCONNECTING(%s): Too many errors",CId_));
}
delete this;
}
bool WSConnection::Send(const std::string &Payload) {
std::lock_guard Guard(Mutex_);
auto BytesSent = WS_->sendFrame(Payload.c_str(),(int)Payload.size());
if(Conn_)
Conn_->TX += BytesSent;
return BytesSent == Payload.size();
}
} //namespace

192
src/NewWebSocketServer.h Normal file
View File

@@ -0,0 +1,192 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRAL_UCENTRALWEBSOCKETSERVER_H
#define UCENTRAL_UCENTRALWEBSOCKETSERVER_H
#include <mutex>
#include <thread>
#include <array>
#include <ctime>
#include "DeviceRegistry.h"
#include "RESTAPI_GWobjects.h"
#include "StateProcessor.h"
#include "SubSystemServer.h"
#include "Poco/AutoPtr.h"
#include "Poco/Net/WebSocket.h"
#include "Poco/Net/NetException.h"
#include "Poco/JSON/Parser.h"
#include "Poco/DynamicAny.h"
#include "Poco/Net/SocketReactor.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/NObserver.h"
#include "Poco/Net/SocketAcceptor.h"
#include "Poco/Net/SocketNotification.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/SecureStreamSocket.h"
#include "Poco/Net/SecureStreamSocketImpl.h"
#include "Poco/Net/ParallelSocketAcceptor.h"
#include "Poco/Environment.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPServer.h"
namespace OpenWifi {
class ReactorPool {
public:
ReactorPool( unsigned int NumberOfThreads = Poco::Environment::processorCount() )
: NumberOfThreads_(NumberOfThreads)
{
}
void Start() {
for(auto i=0;i<NumberOfThreads_;++i) {
auto NewReactor = std::make_unique<Poco::Net::SocketReactor>();
auto NewThread = std::make_unique<Poco::Thread>();
NewThread->start(*NewReactor);
Reactors_.emplace_back( std::move(NewReactor));
Threads_.emplace_back( std::move(NewThread));
}
}
void Stop() {
for(auto &i:Reactors_)
i->stop();
for(auto &i:Threads_) {
i->join();
}
}
Poco::Net::SocketReactor & NextReactor() {
NextReactor_ ++;
NextReactor_ %= NumberOfThreads_;
return *Reactors_[NextReactor_];
}
private:
unsigned int NumberOfThreads_;
unsigned int NextReactor_=0;
std::vector<std::unique_ptr<Poco::Net::SocketReactor>> Reactors_;
std::vector<std::unique_ptr<Poco::Thread>> Threads_;
};
class WSConnection {
static constexpr int BufSize = 64000;
public:
WSConnection(Poco::SharedPtr<Poco::Net::WebSocket> WS, Poco::Net::SocketReactor& Reactor, Poco::Logger &Logger);
~WSConnection();
void ProcessJSONRPCEvent(Poco::JSON::Object::Ptr & Doc);
void ProcessJSONRPCResult(Poco::JSON::Object::Ptr & Doc);
void ProcessIncomingFrame();
bool Send(const std::string &Payload);
void OnSocketReadable(const Poco::AutoPtr<Poco::Net::ReadableNotification>& pNf);
void OnSocketShutdown(const Poco::AutoPtr<Poco::Net::ShutdownNotification>& pNf);
void OnSocketError(const Poco::AutoPtr<Poco::Net::ErrorNotification>& pNf);
bool LookForUpgrade(uint64_t UUID);
static bool ExtractCompressedData(const std::string & CompressedData, std::string & UnCompressedData);
void LogException(const Poco::Exception &E);
[[nodiscard]] GWObjects::CertificateValidation CertificateValidation() const { return CertValidation_; };
private:
std::recursive_mutex Mutex_;
Poco::SharedPtr<Poco::Net::WebSocket> WS_;
Poco::Net::SocketReactor & Reactor_;
Poco::Logger &Logger_;
Poco::Net::StreamSocket Socket_;
std::string SerialNumber_;
std::string Compatible_;
GWObjects::ConnectionState * Conn_ = nullptr;
bool Registered_ = false ;
std::string CId_;
std::string CN_;
GWObjects::CertificateValidation CertValidation_ = GWObjects::CertificateValidation::NO_CERTIFICATE;
uint64_t Errors_=0;
std::unique_ptr<StateProcessor> StatsProcessor_;
void CompleteStartup();
};
class WebSocketRequestHandler : public Poco::Net::HTTPRequestHandler {
public:
explicit WebSocketRequestHandler(ReactorPool &Pool, Poco::Logger &Logger) :
Pool_(Pool), Logger_(Logger) {}
void handleRequest(Poco::Net::HTTPServerRequest & Request, Poco::Net::HTTPServerResponse & Response) final {
try {
std::cout << __LINE__ << std::endl;
auto WS = Poco::SharedPtr<Poco::Net::WebSocket>(new Poco::Net::WebSocket(Request, Response));
std::cout << __LINE__ << std::endl;
new WSConnection(WS, Pool_.NextReactor(), Logger_);
std::cout << __LINE__ << std::endl;
} catch (const Poco::Exception &E) {
std::cout << E.what() << " " << E.name() << " " << E.displayText() << std::endl;
} catch (...) {
std::cout << __LINE__ << std::endl;
}
}
private:
ReactorPool &Pool_;
Poco::Logger &Logger_;
};
class WebSocketRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
explicit WebSocketRequestHandlerFactory(ReactorPool & Pool, Poco::Logger & Logger) :
Pool_(Pool),
Logger_(Logger)
{}
inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest & Request) final {
std::cout << __LINE__ << std::endl;
return new WebSocketRequestHandler(Pool_,Logger_);
}
private:
Poco::Logger & Logger_;
ReactorPool & Pool_;
};
class WebSocketServer : public SubSystemServer {
public:
static WebSocketServer *instance() {
if (instance_ == nullptr) {
instance_ = new WebSocketServer;
}
return instance_;
}
int Start() override;
void Stop() override;
bool IsCertOk() { return IssuerCert_!= nullptr; }
const Poco::Crypto::X509Certificate & Certificate() const { return *IssuerCert_; }
bool ValidateCertificate(const std::string & ConnectionId, const Poco::Crypto::X509Certificate & Certificate);
private:
static WebSocketServer *instance_;
std::unique_ptr<Poco::Crypto::X509Certificate> IssuerCert_;
ReactorPool ReactorPool_;
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> WebServers_;
Poco::ThreadPool Pool_;
WebSocketServer() noexcept: SubSystemServer("WebSocketServer", "WS-SVR", "nano")
{
std::cout << __LINE__ << std::endl;
}
};
inline WebSocketServer * WebSocketServer() { return WebSocketServer::instance(); }
} //namespace
#endif //UCENTRAL_UCENTRALWEBSOCKETSERVER_H

View File

@@ -6,6 +6,7 @@
#include <vector>
#include "OUIServer.h"
#include "Daemon.h"
#include "Poco/String.h"
#include "Poco/StringTokenizer.h"
@@ -14,10 +15,10 @@
#include "Poco/URI.h"
#include "Poco/File.h"
#include "OUIServer.h"
#include "framework/MicroService.h"
#include "Utils.h"
namespace OpenWifi {
class OUIServer * OUIServer::instance_;
int OUIServer::Start() {
Running_=true;
@@ -38,7 +39,7 @@ namespace OpenWifi {
}
void OUIServer::reinitialize(Poco::Util::Application &self) {
MicroService::instance().LoadConfigurationFile();
Daemon()->LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
Start();
@@ -47,7 +48,7 @@ namespace OpenWifi {
bool OUIServer::GetFile(const std::string &FileName) {
try {
std::unique_ptr<std::istream> pStr(
Poco::URIStreamOpener::defaultOpener().open(MicroService::instance().ConfigGetString("oui.download.uri")));
Poco::URIStreamOpener::defaultOpener().open(Daemon()->ConfigGetString("oui.download.uri")));
std::ofstream OS;
Poco::File F(FileName);
if(F.exists())
@@ -103,8 +104,8 @@ namespace OpenWifi {
Updating_ = true;
// fetch data from server, if not available, just use the file we already have.
std::string LatestOUIFileName{ MicroService::instance().DataDir() + "/newOUIFile.txt"};
std::string CurrentOUIFileName{ MicroService::instance().DataDir() + "/current_oui.txt"};
std::string LatestOUIFileName{ Daemon()->DataDir() + "/newOUIFile.txt"};
std::string CurrentOUIFileName{ Daemon()->DataDir() + "/current_oui.txt"};
OUIMap TmpOUIs;
if(GetFile(LatestOUIFileName) && ProcessFile(LatestOUIFileName, TmpOUIs)) {

View File

@@ -5,7 +5,7 @@
#ifndef UCENTRALGW_OUISERVER_H
#define UCENTRALGW_OUISERVER_H
#include "framework/MicroService.h"
#include "SubSystemServer.h"
namespace OpenWifi {
@@ -15,7 +15,9 @@ namespace OpenWifi {
typedef std::map<uint64_t,std::string> OUIMap;
static OUIServer *instance() {
static OUIServer *instance_ = new OUIServer;
if (instance_ == nullptr) {
instance_ = new OUIServer;
}
return instance_;
}
@@ -31,6 +33,7 @@ namespace OpenWifi {
[[nodiscard]] bool ProcessFile(const std::string &FileName, OUIMap &Map);
private:
static OUIServer * instance_;
uint64_t LastUpdate_ = 0 ;
bool ValidFile_=false;
OUIMap OUIs_;

71
src/OpenAPIRequest.cpp Normal file
View File

@@ -0,0 +1,71 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
//
#include <iostream>
#include "OpenAPIRequest.h"
#include "Poco/Net/HTTPSClientSession.h"
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/JSON/Parser.h>
#include <Poco/URI.h>
#include <Poco/Exception.h>
#include "Utils.h"
#include "Daemon.h"
namespace OpenWifi {
OpenAPIRequestGet::OpenAPIRequestGet( std::string ServiceType,
std::string EndPoint,
Types::StringPairVec & QueryData,
uint64_t msTimeout):
Type_(std::move(ServiceType)),
EndPoint_(std::move(EndPoint)),
QueryData_(QueryData),
msTimeout_(msTimeout) {
}
int OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject) {
try {
auto Services = Daemon()->GetServices(Type_);
for(auto const &Svc:Services) {
Poco::URI URI(Svc.PrivateEndPoint);
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
URI.setPath(EndPoint_);
for (const auto &qp : QueryData_)
URI.addQueryParameter(qp.first, qp.second);
std::string Path(URI.getPathAndQuery());
Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000));
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET,
Path,
Poco::Net::HTTPMessage::HTTP_1_1);
Request.add("X-API-KEY", Svc.AccessKey);
Session.sendRequest(Request);
Poco::Net::HTTPResponse Response;
std::istream &is = Session.receiveResponse(Response);
if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
}
return Response.getStatus();
}
}
catch (const Poco::Exception &E)
{
std::cerr << E.displayText() << std::endl;
}
return -1;
}
}

33
src/OpenAPIRequest.h Normal file
View File

@@ -0,0 +1,33 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRALGW_OPENAPIREQUEST_H
#define UCENTRALGW_OPENAPIREQUEST_H
#include "Poco/JSON/Object.h"
#include "OpenWifiTypes.h"
namespace OpenWifi {
class OpenAPIRequestGet {
public:
explicit OpenAPIRequestGet( std::string Type,
std::string EndPoint,
Types::StringPairVec & QueryData,
uint64_t msTimeout);
int Do(Poco::JSON::Object::Ptr &ResponseObject);
private:
std::string Type_;
std::string EndPoint_;
Types::StringPairVec QueryData_;
uint64_t msTimeout_;
};
}
#endif // UCENTRALGW_OPENAPIREQUEST_H

View File

@@ -9,6 +9,8 @@
#ifndef UCENTRALGW_UCENTRALTYPES_H
#define UCENTRALGW_UCENTRALTYPES_H
#include "SubSystemServer.h"
#include <vector>
#include <string>
#include <map>
@@ -27,14 +29,15 @@ namespace OpenWifi::Types {
typedef std::queue<StringPair> StringPairQueue;
typedef std::vector<std::string> StringVec;
typedef std::set<std::string> StringSet;
typedef std::vector<SubSystemServer*> SubSystemVec;
typedef std::map<std::string,std::set<std::string>> StringMapStringSet;
typedef std::function<void(std::string, std::string)> TopicNotifyFunction;
typedef std::list<std::pair<TopicNotifyFunction,int>> TopicNotifyFunctionList;
typedef std::map<std::string, TopicNotifyFunctionList> NotifyTable;
typedef std::map<std::string,uint64_t> CountedMap;
typedef std::vector<uint64_t> TagList;
typedef std::string UUID_t;
typedef std::vector<UUID_t> UUIDvec_t;
typedef std::string UUID_t;
typedef std::vector<UUID_t> UUIDvec_t;
inline void UpdateCountedMap(CountedMap &M, const std::string &S, uint64_t Increment=1) {
auto it = M.find(S);

View File

@@ -1,120 +0,0 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <ctime>
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h"
#include "RESTAPI_blacklist.h"
#include "StorageService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
namespace OpenWifi {
void RESTAPI_blacklist::DoDelete() {
auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
}
GWObjects::BlackListedDevice D;
if(!StorageService()->GetBlackListDevice(SerialNumber, D)) {
return NotFound();
}
if (StorageService()->DeleteBlackListDevice(SerialNumber)) {
return OK();
}
BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
}
void RESTAPI_blacklist::DoGet() {
auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
}
GWObjects::BlackListedDevice D;
if(!StorageService()->GetBlackListDevice(SerialNumber, D)) {
return NotFound();
}
Poco::JSON::Object Answer;
D.to_json(Answer);
return ReturnObject(Answer);
}
void RESTAPI_blacklist::DoPost() {
auto Obj = ParseStream();
GWObjects::BlackListedDevice D;
if(!D.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
}
if(D.serialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
}
Poco::toLowerInPlace(D.serialNumber);
if(StorageService()->IsBlackListed(D.serialNumber)) {
return BadRequest(RESTAPI::Errors::SerialNumberExists);
}
D.author = UserInfo_.userinfo.email;
D.created = std::time(nullptr);
if(StorageService()->AddBlackListDevice(D)) {
GWObjects::BlackListedDevice CreatedDevice;
StorageService()->GetBlackListDevice(D.serialNumber,CreatedDevice);
Poco::JSON::Object Answer;
CreatedDevice.to_json(Answer);
return ReturnObject(Answer);
}
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
void RESTAPI_blacklist::DoPut() {
auto SerialNumber = Poco::toLower(GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""));
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
}
auto Obj = ParseStream();
GWObjects::BlackListedDevice Existing;
if(!StorageService()->GetBlackListDevice(SerialNumber, Existing)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
}
GWObjects::BlackListedDevice NewDevice;
if(!NewDevice.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
}
Existing.reason = NewDevice.reason;
Existing.author = UserInfo_.userinfo.email;
if(StorageService()->UpdateBlackListDevice(SerialNumber, Existing)) {
GWObjects::BlackListedDevice CreatedDevice;
StorageService()->GetBlackListDevice(SerialNumber,CreatedDevice);
Poco::JSON::Object Answer;
CreatedDevice.to_json(Answer);
return ReturnObject(Answer);
}
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
}

View File

@@ -1,28 +0,0 @@
//
// Created by stephane bourque on 2021-10-14.
//
#include "RESTAPI_blacklist_list.h"
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h"
#include "StorageService.h"
namespace OpenWifi {
void RESTAPI_blacklist_list::DoGet() {
std::vector<GWObjects::BlackListedDevice> Devices;
Poco::JSON::Array Arr;
Poco::JSON::Object Answer;
if(StorageService()->GetBlackListDevices(QB_.Offset, QB_.Limit, Devices)) {
for(const auto &i:Devices) {
Poco::JSON::Object O;
i.to_json(O);
Arr.add(O);
}
}
Answer.set("devices", Arr);
return ReturnObject(Answer);
}
}

View File

@@ -1,27 +0,0 @@
//
// Created by stephane bourque on 2021-10-14.
//
#ifndef UCENTRALGW_RESTAPI_BLACKLIST_LIST_H
#define UCENTRALGW_RESTAPI_BLACKLIST_LIST_H
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_blacklist_list : public RESTAPIHandler {
public:
RESTAPI_blacklist_list(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/blacklist"};}
void DoGet() final;
void DoDelete() final {};
void DoPost() final {};
void DoPut() final {};
};
}
#endif // UCENTRALGW_RESTAPI_BLACKLIST_LIST_H

View File

@@ -1,28 +0,0 @@
//
// Created by stephane bourque on 2021-10-17.
//
#include "RESTAPI_capabilities_handler.h"
#include "StorageService.h"
namespace OpenWifi {
void RESTAPI_capabilities_handler::DoGet() {
Storage::DeviceCapabilitiesCache DevCaps;
StorageService()->GetDeviceCapabilitiesCache(DevCaps);
Poco::JSON::Array ObjArr;
for(const auto &[deviceType,capabilities]:DevCaps) {
Poco::JSON::Object Inner;
Inner.set("deviceType",deviceType);
Poco::JSON::Parser P;
auto R = P.parse(capabilities).extract<Poco::JSON::Object::Ptr>();
Inner.set("capabilities", R);
ObjArr.add(Inner);
}
Poco::JSON::Object Answer;
Answer.set("devices",ObjArr);
return ReturnObject(Answer);
}
}

View File

@@ -1,26 +0,0 @@
//
// Created by stephane bourque on 2021-10-17.
//
#ifndef UCENTRALGW_RESTAPI_CAPABILITIES_HANDLER_H
#define UCENTRALGW_RESTAPI_CAPABILITIES_HANDLER_H
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_capabilities_handler : public RESTAPIHandler {
public:
RESTAPI_capabilities_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/capabilities"};}
void DoGet() final;
void DoDelete() final {};
void DoPost() final {};
void DoPut() final {};
};
}
#endif // UCENTRALGW_RESTAPI_CAPABILITIES_HANDLER_H

89
src/RESTAPI_BlackList.cpp Normal file
View File

@@ -0,0 +1,89 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <ctime>
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h"
#include "RESTAPI_BlackList.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_BlackList::DoDelete() {
auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
GWObjects::BlackListedDevice D;
if(!Storage()->GetBlackListDevice(SerialNumber, D)) {
NotFound();
return;
}
if (Storage()->DeleteBlackListDevice(SerialNumber)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
}
void RESTAPI_BlackList::DoGet() {
std::vector<GWObjects::BlackListedDevice> Devices;
Poco::JSON::Array Objects;
if (Storage()->GetBlackListDevices(QB_.Offset, QB_.Limit, Devices)) {
for (const auto &i : Devices) {
Poco::JSON::Object Obj;
i.to_json(Obj);
Objects.add(Obj);
}
}
Poco::JSON::Object RetObj;
RetObj.set(RESTAPI::Protocol::DEVICES, Objects);
ReturnObject(RetObj);
}
void RESTAPI_BlackList::DoPost() {
auto Obj = ParseStream();
if (Obj->has(RESTAPI::Protocol::DEVICES) &&
Obj->isArray(RESTAPI::Protocol::DEVICES)) {
std::vector<GWObjects::BlackListedDevice> Devices;
auto DeviceArray = Obj->getArray(RESTAPI::Protocol::DEVICES);
for (const auto &i : *DeviceArray) {
Poco::JSON::Parser pp;
auto InnerObj = pp.parse(i).extract<Poco::JSON::Object::Ptr>();
Poco::DynamicStruct Vars = *InnerObj;
if (Vars.contains(RESTAPI::Protocol::SERIALNUMBER) &&
Vars.contains(RESTAPI::Protocol::REASON)) {
auto SerialNumber = Vars[RESTAPI::Protocol::SERIALNUMBER].toString();
auto Reason = Vars[RESTAPI::Protocol::REASON].toString();
GWObjects::BlackListedDevice D{.SerialNumber = SerialNumber,
.Reason = Reason,
.Author = UserInfo_.webtoken.username_,
.Created = (uint64_t)time(nullptr)};
Devices.push_back(D);
}
}
if (!Devices.empty()) {
if (Storage()->AddBlackListDevices(Devices)) {
OK();
return;
}
} else {
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
} else {
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
}
}

View File

@@ -9,25 +9,24 @@
#ifndef UCENTRALGW_RESTAPI_BLACKLIST_H
#define UCENTRALGW_RESTAPI_BLACKLIST_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_blacklist : public RESTAPIHandler {
class RESTAPI_BlackList : public RESTAPIHandler {
public:
RESTAPI_blacklist(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal)
RESTAPI_BlackList(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_PUT,
Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/blacklist/{serialNumber}"};}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/blacklist"};}
void DoGet() final;
void DoDelete() final;
void DoPost() final;
void DoPut() final;
void DoPut() final {};
};
}

View File

@@ -15,7 +15,9 @@
#endif
#include "RESTAPI_GWobjects.h"
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
#include "RESTAPI_utils.h"
#include "Utils.h"
using OpenWifi::RESTAPI_utils::field_to_json;
using OpenWifi::RESTAPI_utils::field_from_json;
@@ -68,7 +70,7 @@ namespace OpenWifi::GWObjects {
#endif
}
bool Device::from_json(Poco::JSON::Object::Ptr &Obj) {
bool Device::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"serialNumber",SerialNumber);
field_from_json(Obj,"deviceType",DeviceType);
@@ -147,7 +149,7 @@ namespace OpenWifi::GWObjects {
field_to_json(Obj,"attachFile", AttachDate);
}
bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr &Obj) {
bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"name",Name);
field_from_json(Obj,"configuration",Configuration);
@@ -160,22 +162,10 @@ namespace OpenWifi::GWObjects {
}
void BlackListedDevice::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"serialNumber", serialNumber);
field_to_json(Obj,"author", author);
field_to_json(Obj,"reason", reason);
field_to_json(Obj,"created", created);
}
bool BlackListedDevice::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"serialNumber",serialNumber);
field_from_json(Obj,"author",author);
field_from_json(Obj,"reason",reason);
field_from_json(Obj,"created",created);
return true;
} catch (const Poco::Exception &E) {
}
return false;
field_to_json(Obj,"serialNumber", SerialNumber);
field_to_json(Obj,"author", Author);
field_to_json(Obj,"reason", Reason);
field_to_json(Obj,"created", Created);
}
void ConnectionState::to_json(Poco::JSON::Object &Obj) const {
@@ -253,11 +243,5 @@ namespace OpenWifi::GWObjects {
numberOfDevices = 0 ;
snapshot = std::time(nullptr);
}
void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{
field_to_json(Obj,"deviceType", deviceType);
field_to_json(Obj,"capabilities", capabilities);
};
}

View File

@@ -59,7 +59,7 @@ namespace OpenWifi::GWObjects {
std::string DevicePassword;
void to_json(Poco::JSON::Object &Obj) const;
void to_json_with_status(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
void Print() const;
};
@@ -116,7 +116,7 @@ namespace OpenWifi::GWObjects {
uint64_t Created;
uint64_t LastModified;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
};
struct CommandDetails {
@@ -142,12 +142,11 @@ namespace OpenWifi::GWObjects {
};
struct BlackListedDevice {
std::string serialNumber;
std::string reason;
std::string author;
uint64_t created;
std::string SerialNumber;
std::string Reason;
std::string Author;
uint64_t Created;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct RttySessionDetails {
@@ -183,13 +182,6 @@ namespace OpenWifi::GWObjects {
void to_json(Poco::JSON::Object &Obj) const;
void reset();
};
struct CapabilitiesModel {
std::string deviceType;
std::string capabilities;
void to_json(Poco::JSON::Object &Obj) const;
};
}
#endif //UCENTRAL_RESTAPI_OBJECTS_H

View File

@@ -0,0 +1,5 @@
//
// Created by stephane bourque on 2021-09-15.
//
#include "RESTAPI_GenericServer.h"

View File

@@ -0,0 +1,78 @@
//
// Created by stephane bourque on 2021-09-15.
//
#ifndef OWPROV_RESTAPI_GENERICSERVER_H
#define OWPROV_RESTAPI_GENERICSERVER_H
#include <vector>
#include <string>
#include "Daemon.h"
#include "Poco/StringTokenizer.h"
#include "Poco/Net/HTTPRequest.h"
namespace OpenWifi {
class RESTAPI_GenericServer {
public:
enum {
LOG_GET=0,
LOG_DELETE,
LOG_PUT,
LOG_POST
};
void inline SetFlags(bool External, const std::string &Methods) {
Poco::StringTokenizer Tokens(Methods,",");
auto Offset = (External ? 0 : 4);
for(const auto &i:Tokens) {
if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_DELETE)==0)
LogFlags_[Offset+LOG_DELETE]=true;
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_PUT)==0)
LogFlags_[Offset+LOG_PUT]=true;
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_POST)==0)
LogFlags_[Offset+LOG_POST]=true;
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_GET)==0)
LogFlags_[Offset+LOG_GET]=true;
}
}
inline void InitLogging() {
std::string Public = Daemon()->ConfigGetString("apilogging.public.methods","PUT,POST,DELETE");
SetFlags(true, Public);
std::string Private = Daemon()->ConfigGetString("apilogging.private.methods","PUT,POST,DELETE");
SetFlags(false, Private);
std::string PublicBadTokens = Daemon()->ConfigGetString("apilogging.public.badtokens.methods","");
LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0);
std::string PrivateBadTokens = Daemon()->ConfigGetString("apilogging.private.badtokens.methods","");
LogBadTokens_[1] = (Poco::icompare(PrivateBadTokens,"true")==0);
}
[[nodiscard]] inline bool LogIt(const std::string &Method, bool External) const {
auto Offset = (External ? 0 : 4);
if(Method == Poco::Net::HTTPRequest::HTTP_GET)
return LogFlags_[Offset+LOG_GET];
if(Method == Poco::Net::HTTPRequest::HTTP_POST)
return LogFlags_[Offset+LOG_POST];
if(Method == Poco::Net::HTTPRequest::HTTP_PUT)
return LogFlags_[Offset+LOG_PUT];
if(Method == Poco::Net::HTTPRequest::HTTP_DELETE)
return LogFlags_[Offset+LOG_DELETE];
return false;
};
[[nodiscard]] inline bool LogBadTokens(bool External) const {
return LogBadTokens_[ (External ? 0 : 1) ];
};
private:
std::array<bool,8> LogFlags_{false};
std::array<bool,2> LogBadTokens_{false};
};
}
#endif //OWPROV_RESTAPI_GENERICSERVER_H

View File

@@ -0,0 +1,82 @@
//
// Created by stephane bourque on 2021-06-29.
//
#include "RESTAPI_InternalServer.h"
#include "Poco/URI.h"
#include "RESTAPI_BlackList.h"
#include "RESTAPI_command.h"
#include "RESTAPI_commands.h"
#include "RESTAPI_default_configuration.h"
#include "RESTAPI_default_configurations.h"
#include "RESTAPI_device_commandHandler.h"
#include "RESTAPI_device_handler.h"
#include "RESTAPI_devices_handler.h"
#include "RESTAPI_file.h"
#include "RESTAPI_ouis.h"
#include "Utils.h"
namespace OpenWifi {
class RESTAPI_InternalServer *RESTAPI_InternalServer::instance_ = nullptr;
int RESTAPI_InternalServer::Start() {
Logger_.information("Starting.");
Server_.InitLogging();
for(const auto & Svr: ConfigServersList_) {
Logger_.information(Poco::format("Starting: %s:%s Keyfile:%s CertFile: %s", Svr.Address(), std::to_string(Svr.Port()),
Svr.KeyFile(),Svr.CertFile()));
auto Sock{Svr.CreateSecureSocket(Logger_)};
Svr.LogCert(Logger_);
if(!Svr.RootCA().empty())
Svr.LogCas(Logger_);
auto Params = new Poco::Net::HTTPServerParams;
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new InternalRequestHandlerFactory(Server_), Pool_, Sock, Params);
NewServer->start();
RESTServers_.push_back(std::move(NewServer));
}
return 0;
}
void RESTAPI_InternalServer::Stop() {
Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ )
svr->stop();
RESTServers_.clear();
}
void RESTAPI_InternalServer::reinitialize(Poco::Util::Application &self) {
Daemon()->LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
Start();
}
Poco::Net::HTTPRequestHandler *InternalRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) {
Poco::URI uri(Request.getURI());
const auto & Path = uri.getPath();
RESTAPIHandler::BindingMap Bindings;
return RESTAPI_Router_I<
RESTAPI_devices_handler,
RESTAPI_device_handler,
RESTAPI_device_commandHandler,
RESTAPI_default_configurations,
RESTAPI_default_configuration,
RESTAPI_command,
RESTAPI_commands,
RESTAPI_ouis,
RESTAPI_file,
RESTAPI_BlackList>(Path,Bindings,Logger_, Server_); }
}

View File

@@ -0,0 +1,62 @@
//
// Created by stephane bourque on 2021-06-29.
//
#ifndef UCENTRALSEC_RESTAPI_INTERNALSERVER_H
#define UCENTRALSEC_RESTAPI_INTERNALSERVER_H
#include "SubSystemServer.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/NetException.h"
#include "RESTAPI_GenericServer.h"
namespace OpenWifi {
class RESTAPI_InternalServer : public SubSystemServer {
public:
static RESTAPI_InternalServer *instance() {
if (instance_ == nullptr) {
instance_ = new RESTAPI_InternalServer;
}
return instance_;
}
int Start() override;
void Stop() override;
void reinitialize(Poco::Util::Application &self) override;
private:
static RESTAPI_InternalServer *instance_;
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
Poco::ThreadPool Pool_;
RESTAPI_GenericServer Server_;
RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi")
{
}
};
inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); };
class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
explicit InternalRequestHandlerFactory(RESTAPI_GenericServer & Server) :
Logger_(RESTAPI_InternalServer()->Logger()),
Server_(Server)
{}
Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override;
private:
Poco::Logger & Logger_;
RESTAPI_GenericServer & Server_;
};
} // namespace
#endif //UCENTRALSEC_RESTAPI_INTERNALSERVER_H

View File

@@ -10,10 +10,10 @@
#include <chrono>
#include "RESTAPI_RPC.h"
#include "CommandManager.h"
#include "DeviceRegistry.h"
#include "StorageService.h"
#include "framework/uCentral_Protocol.h"
#include "DeviceRegistry.h"
#include "CommandManager.h"
#include "uCentralProtocol.h"
namespace OpenWifi::RESTAPI_RPC {
void SetCommandStatus(GWObjects::CommandDetails &Cmd,
@@ -22,7 +22,7 @@ namespace OpenWifi::RESTAPI_RPC {
RESTAPIHandler *Handler,
OpenWifi::Storage::CommandExecutionType Status,
Poco::Logger &Logger) {
if (StorageService()->AddCommand(Cmd.SerialNumber, Cmd, Status)) {
if (Storage()->AddCommand(Cmd.SerialNumber, Cmd, Status)) {
Poco::JSON::Object RetObj;
Cmd.to_json(RetObj);
Handler->ReturnObject(RetObj);
@@ -80,7 +80,7 @@ namespace OpenWifi::RESTAPI_RPC {
}
// Add the completed command to the database...
StorageService()->AddCommand(Cmd.SerialNumber, Cmd,
Storage()->AddCommand(Cmd.SerialNumber, Cmd,
Storage::COMMAND_COMPLETED);
if (ObjectToReturn) {

View File

@@ -15,9 +15,9 @@
#include "Poco/File.h"
#include "Poco/JSON/Object.h"
#include "RESTObjects//RESTAPI_GWobjects.h"
#include "RESTAPI_GWobjects.h"
#include "RESTAPI_handler.h"
#include "StorageService.h"
#include "framework/MicroService.h"
namespace OpenWifi::RESTAPI_RPC {

View File

@@ -9,8 +9,8 @@
#include "Poco/JSON/Parser.h"
#include "Poco/JSON/Stringifier.h"
#include "framework/MicroService.h"
#include "RESTAPI_SecurityObjects.h"
#include "RESTAPI_utils.h"
using OpenWifi::RESTAPI_utils::field_to_json;
using OpenWifi::RESTAPI_utils::field_from_json;
@@ -58,28 +58,21 @@ namespace OpenWifi::SecurityObjects {
return CSR;
else if (!Poco::icompare(U, "system"))
return SYSTEM;
else if (!Poco::icompare(U, "installer"))
return INSTALLER;
else if (!Poco::icompare(U, "noc"))
return NOC;
else if (!Poco::icompare(U, "accounting"))
return ACCOUNTING;
else if (!Poco::icompare(U, "special"))
return SPECIAL;
return UNKNOWN;
}
std::string UserTypeToString(USER_ROLE U) {
switch(U) {
case UNKNOWN: return "unknown";
case ROOT: return "root";
case ADMIN: return "admin";
case SUBSCRIBER: return "subscriber";
case CSR: return "csr";
case SYSTEM: return "system";
case INSTALLER: return "installer";
case NOC: return "noc";
case ACCOUNTING: return "accounting";
case UNKNOWN:
default:
return "unknown";
case SPECIAL: return "special";
case ADMIN: return "admin";
default: return "unknown";
}
}
@@ -132,94 +125,6 @@ namespace OpenWifi::SecurityObjects {
return false;
}
void MobilePhoneNumber::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"number", number);
field_to_json(Obj,"verified", verified);
field_to_json(Obj,"primary", primary);
}
bool MobilePhoneNumber::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"number",number);
field_from_json(Obj,"verified",verified);
field_from_json(Obj,"primary",primary);
return true;
} catch (...) {
}
return false;
};
void MfaAuthInfo::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"enabled", enabled);
field_to_json(Obj,"method", method);
}
bool MfaAuthInfo::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"enabled",enabled);
field_from_json(Obj,"method",method);
return true;
} catch (...) {
}
return false;
}
void UserLoginLoginExtensions::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj, "mobiles", mobiles);
field_to_json(Obj, "mfa", mfa);
}
bool UserLoginLoginExtensions::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"mobiles",mobiles);
field_from_json(Obj,"mfa",mfa);
return true;
} catch (...) {
}
return false;
}
void MFAChallengeRequest::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj, "uuid", uuid);
field_to_json(Obj, "question", question);
field_to_json(Obj, "created", created);
field_to_json(Obj, "method", method);
}
bool MFAChallengeRequest::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"uuid",uuid);
field_from_json(Obj,"question",question);
field_from_json(Obj,"created",created);
field_from_json(Obj,"method",method);
return true;
} catch (...) {
}
return false;
};
void MFAChallengeResponse::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj, "uuid", uuid);
field_to_json(Obj, "answer", answer);
}
bool MFAChallengeResponse::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"uuid",uuid);
field_from_json(Obj,"answer",answer);
return true;
} catch (...) {
}
return false;
}
void UserInfo::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"Id",Id);
field_to_json(Obj,"name",name);
@@ -387,53 +292,40 @@ namespace OpenWifi::SecurityObjects {
field_to_json(Obj,"note", note);
}
bool NoteInfo::from_json(Poco::JSON::Object::Ptr &Obj) {
bool NoteInfo::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"created",created);
field_from_json(Obj,"createdBy",createdBy);
field_from_json(Obj,"note",note);
return true;
} catch(...) {
}
return false;
}
bool MergeNotes(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes) {
bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes) {
try {
if(Obj->has("notes") && Obj->isArray("notes")) {
SecurityObjects::NoteInfoVec NIV;
NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get("notes").toString());
for(auto const &i:NIV) {
SecurityObjects::NoteInfo ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note};
Notes.push_back(ii);
}
SecurityObjects::NoteInfoVec NIV;
NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get("notes").toString());
for(auto const &i:NIV) {
SecurityObjects::NoteInfo ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note};
Notes.push_back(ii);
}
return true;
} catch(...) {
}
return false;
}
bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes) {
for(auto const &i:NewNotes) {
SecurityObjects::NoteInfo ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note};
ExistingNotes.push_back(ii);
}
return true;
}
void ProfileAction::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"resource", resource);
field_to_json<ResourceAccessType>(Obj,"access", access, ResourceAccessTypeToString);
}
bool ProfileAction::from_json(Poco::JSON::Object::Ptr &Obj) {
bool ProfileAction::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"resource",resource);
field_from_json<ResourceAccessType>(Obj,"access",access,ResourceAccessTypeFromString );
return true;
} catch(...) {
}
@@ -449,7 +341,7 @@ namespace OpenWifi::SecurityObjects {
field_to_json(Obj,"notes", notes);
}
bool SecurityProfile::from_json(Poco::JSON::Object::Ptr &Obj) {
bool SecurityProfile::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"id",id);
field_from_json(Obj,"name",name);
@@ -457,7 +349,6 @@ namespace OpenWifi::SecurityObjects {
field_from_json(Obj,"policy",policy);
field_from_json(Obj,"role",role);
field_from_json(Obj,"notes",notes);
return true;
} catch(...) {
}
@@ -468,51 +359,13 @@ namespace OpenWifi::SecurityObjects {
field_to_json(Obj, "profiles", profiles);
}
bool SecurityProfileList::from_json(Poco::JSON::Object::Ptr &Obj) {
bool SecurityProfileList::from_json(Poco::JSON::Object::Ptr Obj) {
try {
field_from_json(Obj,"profiles",profiles);
return true;
} catch(...) {
}
return false;
}
void ActionLink::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"id",id);
field_to_json(Obj,"action",action);
field_to_json(Obj,"userId",userId);
field_to_json(Obj,"actionTemplate",actionTemplate);
field_to_json(Obj,"variables",variables);
field_to_json(Obj,"locale",locale);
field_to_json(Obj,"message",message);
field_to_json(Obj,"sent",sent);
field_to_json(Obj,"created",created);
field_to_json(Obj,"expires",expires);
field_to_json(Obj,"completed",completed);
field_to_json(Obj,"canceled",canceled);
}
bool ActionLink::from_json(Poco::JSON::Object::Ptr &Obj) {
try {
field_from_json(Obj,"id",id);
field_from_json(Obj,"action",action);
field_from_json(Obj,"userId",userId);
field_from_json(Obj,"actionTemplate",actionTemplate);
field_from_json(Obj,"variables",variables);
field_from_json(Obj,"locale",locale);
field_from_json(Obj,"message",message);
field_from_json(Obj,"sent",sent);
field_from_json(Obj,"created",created);
field_from_json(Obj,"expires",expires);
field_from_json(Obj,"completed",completed);
field_from_json(Obj,"canceled",canceled);
return true;
} catch(...) {
}
return false;
}
}

View File

@@ -10,7 +10,7 @@
#define UCENTRAL_RESTAPI_SECURITYOBJECTS_H
#include "Poco/JSON/Object.h"
#include "framework/OpenWifiTypes.h"
#include "OpenWifiTypes.h"
namespace OpenWifi::SecurityObjects {
@@ -42,7 +42,7 @@ namespace OpenWifi::SecurityObjects {
};
enum USER_ROLE {
UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, INSTALLER, NOC, ACCOUNTING
UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, SPECIAL
};
USER_ROLE UserTypeFromString(const std::string &U);
@@ -53,53 +53,10 @@ namespace OpenWifi::SecurityObjects {
std::string createdBy;
std::string note;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
};
typedef std::vector<NoteInfo> NoteInfoVec;
struct MobilePhoneNumber {
std::string number;
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 = false;
std::string method;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct UserLoginLoginExtensions {
std::vector<MobilePhoneNumber> mobiles;
struct MfaAuthInfo mfa;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct MFAChallengeRequest {
std::string uuid;
std::string question;
std::string method;
uint64_t created = std::time(nullptr);
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct MFAChallengeResponse {
std::string uuid;
std::string answer;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
struct UserInfo {
std::string Id;
std::string name;
@@ -124,7 +81,7 @@ namespace OpenWifi::SecurityObjects {
bool suspended = false;
bool blackListed = false;
USER_ROLE userRole;
UserLoginLoginExtensions userTypeProprietaryInfo;
std::string userTypeProprietaryInfo;
std::string securityPolicy;
uint64_t securityPolicyChange = 0 ;
std::string currentPassword;
@@ -137,9 +94,7 @@ namespace OpenWifi::SecurityObjects {
};
typedef std::vector<UserInfo> UserInfoVec;
// bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes);
bool MergeNotes(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes);
bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes);
bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes);
struct InternalServiceInfo {
std::string privateURI;
@@ -200,7 +155,7 @@ namespace OpenWifi::SecurityObjects {
std::string resource;
ResourceAccessType access;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
};
typedef std::vector<ProfileAction> ProfileActionVec;
@@ -212,37 +167,14 @@ namespace OpenWifi::SecurityObjects {
std::string role;
NoteInfoVec notes;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
};
typedef std::vector<SecurityProfile> SecurityProfileVec;
struct SecurityProfileList {
SecurityProfileVec profiles;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
};
enum LinkActions {
FORGOT_PASSWORD=1,
VERIFY_EMAIL
};
struct ActionLink {
std::string id;
uint64_t action;
std::string userId;
std::string actionTemplate;
Types::StringPairVec variables;
std::string locale;
std::string message;
uint64_t sent=0;
uint64_t created=std::time(nullptr);
uint64_t expires=0;
uint64_t completed=0;
uint64_t canceled=0;
void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj);
bool from_json(Poco::JSON::Object::Ptr Obj);
};
}

View File

@@ -4,7 +4,6 @@
#include "RESTAPI_TelemetryWebSocket.h"
#include "Poco/Net/WebSocket.h"
#include "Poco/Net/NetException.h"
#include "TelemetryStream.h"
namespace OpenWifi {

View File

@@ -5,7 +5,7 @@
#ifndef OWGW_RESTAPI_TELEMETRYWEBSOCKET_H
#define OWGW_RESTAPI_TELEMETRYWEBSOCKET_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_TelemetryWebSocket : public RESTAPIHandler {

View File

@@ -8,37 +8,42 @@
#include "RESTAPI_command.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_command::DoGet() {
auto CommandUUID = GetBinding(RESTAPI::Protocol::COMMANDUUID, "");
GWObjects::CommandDetails Command;
if (StorageService()->GetCommand(CommandUUID, Command)) {
if (Storage()->GetCommand(CommandUUID, Command)) {
Poco::JSON::Object RetObj;
Command.to_json(RetObj);
return ReturnObject(RetObj);
ReturnObject(RetObj);
return;
}
return NotFound();
NotFound();
}
void RESTAPI_command::DoDelete() {
auto UUID = GetBinding(RESTAPI::Protocol::COMMANDUUID, "");
if(UUID.empty()) {
return BadRequest(RESTAPI::Errors::MissingUUID);
BadRequest(RESTAPI::Errors::MissingUUID);
return;
}
GWObjects::CommandDetails C;
if(!StorageService()->GetCommand(UUID, C)) {
return NotFound();
if(!Storage()->GetCommand(UUID, C)) {
NotFound();
return;
}
if (StorageService()->DeleteCommand(UUID)) {
return OK();
if (Storage()->DeleteCommand(UUID)) {
OK();
return;
}
return InternalError();
InternalError();
}
}

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_COMMAND_H
#define UCENTRAL_RESTAPI_COMMAND_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_command : public RESTAPIHandler {

View File

@@ -7,18 +7,19 @@
//
#include "RESTAPI_commands.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
#include "Utils.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_commands::DoGet() {
auto SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
std::vector<GWObjects::CommandDetails> Commands;
if (QB_.Newest) {
StorageService()->GetNewestCommands(SerialNumber, QB_.Limit, Commands);
Storage()->GetNewestCommands(SerialNumber, QB_.Limit, Commands);
} else {
StorageService()->GetCommands(SerialNumber, QB_.StartDate, QB_.EndDate, QB_.Offset, QB_.Limit,
Storage()->GetCommands(SerialNumber, QB_.StartDate, QB_.EndDate, QB_.Offset, QB_.Limit,
Commands);
}
Poco::JSON::Array ArrayObj;
@@ -36,11 +37,15 @@ namespace OpenWifi {
auto SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
if (StorageService()->DeleteCommands(SerialNumber, QB_.StartDate, QB_.EndDate)) {
return OK();
if (Storage()->DeleteCommands(SerialNumber, QB_.StartDate, QB_.EndDate)) {
OK();
return;
}
InternalError();
}
}

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_COMMANDS_H
#define UCENTRAL_RESTAPI_COMMANDS_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_commands : public RESTAPIHandler {

View File

@@ -10,19 +10,20 @@
#include "RESTAPI_default_configuration.h"
#include "RESTObjects/RESTAPI_GWobjects.h"
#include "RESTAPI_GWobjects.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_default_configuration::DoGet() {
std::string Name = GetBinding(RESTAPI::Protocol::NAME, "");
GWObjects::DefaultConfiguration DefConfig;
if (StorageService()->GetDefaultConfiguration(Name, DefConfig)) {
if (Storage()->GetDefaultConfiguration(Name, DefConfig)) {
Poco::JSON::Object Obj;
DefConfig.to_json(Obj);
return ReturnObject(Obj);
ReturnObject(Obj);
return;
}
NotFound();
}
@@ -30,11 +31,13 @@ namespace OpenWifi {
void RESTAPI_default_configuration::DoDelete() {
std::string Name = GetBinding(RESTAPI::Protocol::NAME, "");
if(Name.empty()) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
if (StorageService()->DeleteDefaultConfiguration(Name)) {
return OK();
if (Storage()->DeleteDefaultConfiguration(Name)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
}
@@ -43,17 +46,20 @@ namespace OpenWifi {
std::string Name = GetBinding(RESTAPI::Protocol::NAME, "");
if(Name.empty()) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
auto Obj = ParseStream();
GWObjects::DefaultConfiguration DefConfig;
if (!DefConfig.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
BadRequest(RESTAPI::Errors::InvalidJSONDocument);
return;
}
if (StorageService()->CreateDefaultConfiguration(Name, DefConfig)) {
return OK();
if (Storage()->CreateDefaultConfiguration(Name, DefConfig)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::RecordNotCreated);
}
@@ -65,11 +71,13 @@ namespace OpenWifi {
GWObjects::DefaultConfiguration DefConfig;
if (!DefConfig.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
BadRequest(RESTAPI::Errors::InvalidJSONDocument);
return;
}
if (StorageService()->UpdateDefaultConfiguration(Name, DefConfig)) {
return OK();
if (Storage()->UpdateDefaultConfiguration(Name, DefConfig)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::RecordNotUpdated);
}

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_DEFAULT_CONFIGURATION_H
#define UCENTRAL_RESTAPI_DEFAULT_CONFIGURATION_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_default_configuration : public RESTAPIHandler {

View File

@@ -10,13 +10,13 @@
#include "Poco/JSON/Stringifier.h"
#include "RESTAPI_default_configurations.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/RESTAPI_protocol.h"
namespace OpenWifi {
void RESTAPI_default_configurations::DoGet() {
std::vector<GWObjects::DefaultConfiguration> DefConfigs;
StorageService()->GetDefaultConfigurations(QB_.Offset, QB_.Limit, DefConfigs);
Storage()->GetDefaultConfigurations(QB_.Offset, QB_.Limit, DefConfigs);
Poco::JSON::Array Objects;
for (const auto &i : DefConfigs) {

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_DEFAULT_CONFIGURATIONS_H
#define UCENTRAL_RESTAPI_DEFAULT_CONFIGURATIONS_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_default_configurations : public RESTAPIHandler {

View File

@@ -5,7 +5,7 @@
#ifndef UCENTRALGW_RESTAPI_DEVICEDASHBOARDHANDLER_H
#define UCENTRALGW_RESTAPI_DEVICEDASHBOARDHANDLER_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_deviceDashboardHandler : public RESTAPIHandler {

View File

@@ -13,124 +13,117 @@
#include "Poco/JSON/Parser.h"
#include "CentralConfig.h"
#include "Daemon.h"
#include "DeviceRegistry.h"
#include "FileUploader.h"
#include "RESTObjects/RESTAPI_GWobjects.h"
#include "RESTAPI_GWobjects.h"
#include "RESTAPI_device_commandHandler.h"
#include "StorageService.h"
#include "Utils.h"
#include "uCentralProtocol.h"
#include "RESTAPI_protocol.h"
#include "RESTAPI_RPC.h"
#include "CommandManager.h"
#include "KafkaManager.h"
#include "Kafka_topics.h"
#include "TelemetryStream.h"
#include "framework/RESTAPI_protocol.h"
#include "framework/uCentral_Protocol.h"
#include "framework/KafkaTopics.h"
#include "framework/RESTAPI_errors.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_device_commandHandler::DoGet() {
if(!ValidateParameters()) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
GWObjects::Device TheDevice;
if(!StorageService()->GetDevice(SerialNumber_,TheDevice)) {
return NotFound();
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
if (Command_ == RESTAPI::Protocol::CAPABILITIES){
return GetCapabilities();
GetCapabilities();
} else if (Command_ == RESTAPI::Protocol::LOGS) {
return GetLogs();
GetLogs();
} else if (Command_ == RESTAPI::Protocol::HEALTHCHECKS) {
return GetChecks();
GetChecks();
} else if (Command_ == RESTAPI::Protocol::STATISTICS) {
return GetStatistics();
GetStatistics();
} else if (Command_ == RESTAPI::Protocol::STATUS) {
return GetStatus();
GetStatus();
} else if (Command_ == RESTAPI::Protocol::RTTY) {
return Rtty();
Rtty();
} else {
return BadRequest(RESTAPI::Errors::InvalidCommand);
BadRequest(RESTAPI::Errors::InvalidCommand);
}
}
void RESTAPI_device_commandHandler::DoDelete() {
if(!ValidateParameters()) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
GWObjects::Device TheDevice;
if(!StorageService()->GetDevice(SerialNumber_,TheDevice)) {
return NotFound();
}
if (Command_ == RESTAPI::Protocol::CAPABILITIES) {
return DeleteCapabilities();
DeleteCapabilities();
} else if (Command_ == RESTAPI::Protocol::LOGS){
return DeleteLogs();
DeleteLogs();
} else if (Command_ == RESTAPI::Protocol::HEALTHCHECKS){
return DeleteChecks();
DeleteChecks();
} else if (Command_ == RESTAPI::Protocol::STATISTICS) {
return DeleteStatistics();
DeleteStatistics();
} else {
return BadRequest(RESTAPI::Errors::InvalidCommand);
BadRequest(RESTAPI::Errors::InvalidCommand);
}
}
void RESTAPI_device_commandHandler::DoPost() {
if(!ValidateParameters()) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
GWObjects::Device TheDevice;
if(!StorageService()->GetDevice(SerialNumber_,TheDevice)) {
return NotFound();
}
if (Command_ == RESTAPI::Protocol::PERFORM) {
return ExecuteCommand();
ExecuteCommand();
} else if (Command_ == RESTAPI::Protocol::CONFIGURE) {
return Configure();
Configure();
} else if (Command_ == RESTAPI::Protocol::UPGRADE) {
return Upgrade();
Upgrade();
} else if (Command_ == RESTAPI::Protocol::REBOOT) {
return Reboot();
Reboot();
} else if (Command_ == RESTAPI::Protocol::FACTORY) {
return Factory();
Factory();
} else if (Command_ == RESTAPI::Protocol::LEDS) {
return LEDs();
LEDs();
} else if (Command_ == RESTAPI::Protocol::TRACE) {
return Trace();
Trace();
} else if (Command_ == RESTAPI::Protocol::REQUEST) {
return MakeRequest();
MakeRequest();
} else if (Command_ == RESTAPI::Protocol::WIFISCAN) {
return WifiScan();
WifiScan();
} else if (Command_ == RESTAPI::Protocol::EVENTQUEUE) {
return EventQueue();
EventQueue();
} else if (Command_ == RESTAPI::Protocol::TELEMETRY) {
return Telemetry();
Telemetry();
} else {
return BadRequest(RESTAPI::Errors::InvalidCommand);
BadRequest(RESTAPI::Errors::InvalidCommand);
}
}
void RESTAPI_device_commandHandler::GetCapabilities() {
GWObjects::Capabilities Caps;
if (StorageService()->GetDeviceCapabilities(SerialNumber_, Caps)) {
if (Storage()->GetDeviceCapabilities(SerialNumber_, Caps)) {
Poco::JSON::Object RetObj;
Caps.to_json(RetObj);
RetObj.set(RESTAPI::Protocol::SERIALNUMBER, SerialNumber_);
return ReturnObject(RetObj);
ReturnObject(RetObj);
return;
}
NotFound();
}
void RESTAPI_device_commandHandler::DeleteCapabilities() {
Logger_.information(Poco::format("DELETE-CAPABILITIES: user=%s serial=%s", UserInfo_.userinfo.email,SerialNumber_));
if (StorageService()->DeleteDeviceCapabilities(SerialNumber_)) {
return OK();
if (Storage()->DeleteDeviceCapabilities(SerialNumber_)) {
OK();
return;
}
NotFound();
}
@@ -138,12 +131,12 @@ void RESTAPI_device_commandHandler::DeleteCapabilities() {
void RESTAPI_device_commandHandler::GetStatistics() {
if (QB_.Lifetime) {
std::string Stats;
StorageService()->GetLifetimeStats(SerialNumber_, Stats);
Storage()->GetLifetimeStats(SerialNumber_, Stats);
Poco::JSON::Parser P;
if (Stats.empty())
Stats = uCentralProtocol::EMPTY_JSON_DOC;
auto Obj = P.parse(Stats).extract<Poco::JSON::Object::Ptr>();
return ReturnObject(*Obj);
ReturnObject(*Obj);
} else if (QB_.LastOnly) {
std::string Stats;
if (DeviceRegistry()->GetStatistics(SerialNumber_, Stats)) {
@@ -151,16 +144,16 @@ void RESTAPI_device_commandHandler::GetStatistics() {
if (Stats.empty())
Stats = uCentralProtocol::EMPTY_JSON_DOC;
auto Obj = P.parse(Stats).extract<Poco::JSON::Object::Ptr>();
return ReturnObject(*Obj);
ReturnObject(*Obj);
} else {
return NotFound();
NotFound();
}
} else {
std::vector<GWObjects::Statistics> Stats;
if (QB_.Newest) {
StorageService()->GetNewestStatisticsData(SerialNumber_, QB_.Limit, Stats);
Storage()->GetNewestStatisticsData(SerialNumber_, QB_.Limit, Stats);
} else {
StorageService()->GetStatisticsData(SerialNumber_, QB_.StartDate, QB_.EndDate,
Storage()->GetStatisticsData(SerialNumber_, QB_.StartDate, QB_.EndDate,
QB_.Offset, QB_.Limit, Stats);
}
Poco::JSON::Array ArrayObj;
@@ -172,19 +165,21 @@ void RESTAPI_device_commandHandler::GetStatistics() {
Poco::JSON::Object RetObj;
RetObj.set(RESTAPI::Protocol::DATA, ArrayObj);
RetObj.set(RESTAPI::Protocol::SERIALNUMBER, SerialNumber_);
return ReturnObject(RetObj);
ReturnObject(RetObj);
}
}
void RESTAPI_device_commandHandler::DeleteStatistics() {
Logger_.information(Poco::format("DELETE-STATISTICS: user=%s serial=%s", UserInfo_.userinfo.email,SerialNumber_));
if (QB_.Lifetime) {
if (StorageService()->ResetLifetimeStats(SerialNumber_)) {
return OK();
if (Storage()->ResetLifetimeStats(SerialNumber_)) {
OK();
return;
}
} else {
if (StorageService()->DeleteStatisticsData(SerialNumber_, QB_.StartDate, QB_.EndDate)) {
return OK();
if (Storage()->DeleteStatisticsData(SerialNumber_, QB_.StartDate, QB_.EndDate)) {
OK();
return;
}
}
NotFound();
@@ -196,7 +191,8 @@ void RESTAPI_device_commandHandler::GetStatus() {
if (DeviceRegistry()->GetState(SerialNumber_, State)) {
Poco::JSON::Object RetObject;
State.to_json(RetObject);
return ReturnObject(RetObject);
ReturnObject(RetObject);
return;
}
NotFound();
}
@@ -211,17 +207,18 @@ void RESTAPI_device_commandHandler::Configure() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto Configuration = GetS(RESTAPI::Protocol::CONFIGURATION, Obj,uCentralProtocol::EMPTY_JSON_DOC);
auto When = GetWhen(Obj);
uint64_t NewUUID;
if (StorageService()->UpdateDeviceConfiguration(SerialNumber_, Configuration, NewUUID)) {
if (Storage()->UpdateDeviceConfiguration(SerialNumber_, Configuration, NewUUID)) {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = uCentralProtocol::CONFIGURE;
Cmd.RunAt = When;
@@ -238,9 +235,11 @@ void RESTAPI_device_commandHandler::Configure() {
Cmd.Details = ParamStream.str();
DeviceRegistry()->SetPendingUUID(SerialNumber_, NewUUID);
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
return BadRequest(RESTAPI::Errors::RecordNotUpdated);
BadRequest(RESTAPI::Errors::RecordNotUpdated);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -254,7 +253,8 @@ void RESTAPI_device_commandHandler::Upgrade() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto URI = GetS(RESTAPI::Protocol::URI, Obj);
@@ -263,7 +263,7 @@ void RESTAPI_device_commandHandler::Upgrade() {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = uCentralProtocol::UPGRADE;
Cmd.RunAt = When;
@@ -278,7 +278,8 @@ void RESTAPI_device_commandHandler::Upgrade() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -286,9 +287,9 @@ void RESTAPI_device_commandHandler::Upgrade() {
void RESTAPI_device_commandHandler::GetLogs() {
std::vector<GWObjects::DeviceLog> Logs;
if (QB_.Newest) {
StorageService()->GetNewestLogData(SerialNumber_, QB_.Limit, Logs, QB_.LogType);
Storage()->GetNewestLogData(SerialNumber_, QB_.Limit, Logs, QB_.LogType);
} else {
StorageService()->GetLogData(SerialNumber_, QB_.StartDate, QB_.EndDate, QB_.Offset,
Storage()->GetLogData(SerialNumber_, QB_.StartDate, QB_.EndDate, QB_.Offset,
QB_.Limit, Logs, QB_.LogType);
}
@@ -306,9 +307,10 @@ void RESTAPI_device_commandHandler::GetLogs() {
void RESTAPI_device_commandHandler::DeleteLogs() {
Logger_.information(Poco::format("DELETE-LOGS: user=%s serial=%s", UserInfo_.userinfo.email,SerialNumber_));
if (StorageService()->DeleteLogData(SerialNumber_, QB_.StartDate, QB_.EndDate,
if (Storage()->DeleteLogData(SerialNumber_, QB_.StartDate, QB_.EndDate,
QB_.LogType)) {
return OK();
OK();
return;
}
BadRequest(RESTAPI::Errors::NoRecordsDeleted);
}
@@ -321,15 +323,15 @@ void RESTAPI_device_commandHandler::GetChecks() {
if (DeviceRegistry()->GetHealthcheck(SerialNumber_, HC)) {
Poco::JSON::Object Answer;
HC.to_json(Answer);
return ReturnObject(Answer);
ReturnObject(Answer);
} else {
return NotFound();
NotFound();
}
} else {
if (QB_.Newest) {
StorageService()->GetNewestHealthCheckData(SerialNumber_, QB_.Limit, Checks);
Storage()->GetNewestHealthCheckData(SerialNumber_, QB_.Limit, Checks);
} else {
StorageService()->GetHealthCheckData(SerialNumber_, QB_.StartDate, QB_.EndDate,
Storage()->GetHealthCheckData(SerialNumber_, QB_.StartDate, QB_.EndDate,
QB_.Offset, QB_.Limit, Checks);
}
@@ -349,8 +351,9 @@ void RESTAPI_device_commandHandler::GetChecks() {
void RESTAPI_device_commandHandler::DeleteChecks() {
Logger_.information(Poco::format("DELETE-HEALTHCHECKS: user=%s serial=%s", UserInfo_.userinfo.email,SerialNumber_));
if (StorageService()->DeleteHealthCheckData(SerialNumber_, QB_.StartDate, QB_.EndDate)) {
return OK();
if (Storage()->DeleteHealthCheckData(SerialNumber_, QB_.StartDate, QB_.EndDate)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::NoRecordsDeleted);
}
@@ -364,7 +367,8 @@ void RESTAPI_device_commandHandler::ExecuteCommand() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest("Missing serial number.");
BadRequest("Missing serial number.");
return;
}
auto Command = GetS(RESTAPI::Protocol::COMMAND, Obj);
@@ -374,7 +378,7 @@ void RESTAPI_device_commandHandler::ExecuteCommand() {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = Command;
Cmd.Custom = 1;
@@ -396,7 +400,8 @@ void RESTAPI_device_commandHandler::ExecuteCommand() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -408,13 +413,14 @@ void RESTAPI_device_commandHandler::Reboot() {
if (Obj->has(RESTAPI::Protocol::SERIALNUMBER)) {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
uint64_t When = GetWhen(Obj);
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = uCentralProtocol::REBOOT;
Cmd.RunAt = When;
@@ -428,7 +434,8 @@ void RESTAPI_device_commandHandler::Reboot() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingSerialNumber);
}
@@ -442,7 +449,8 @@ void RESTAPI_device_commandHandler::Factory() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto KeepRedirector = GetB(RESTAPI::Protocol::KEEPREDIRECTOR, Obj, true);
@@ -451,7 +459,7 @@ void RESTAPI_device_commandHandler::Factory() {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = uCentralProtocol::FACTORY;
Cmd.RunAt = When;
@@ -466,7 +474,8 @@ void RESTAPI_device_commandHandler::Factory() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -480,7 +489,8 @@ void RESTAPI_device_commandHandler::LEDs() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto Pattern =
@@ -488,7 +498,8 @@ void RESTAPI_device_commandHandler::LEDs() {
if (Pattern != uCentralProtocol::ON &&
Pattern != uCentralProtocol::OFF &&
Pattern != uCentralProtocol::BLINK) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
auto Duration = Get(uCentralProtocol::DURATION, Obj, 30);
@@ -497,7 +508,7 @@ void RESTAPI_device_commandHandler::LEDs() {
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.Command = uCentralProtocol::LEDS;
Cmd.RunAt = When;
@@ -512,7 +523,8 @@ void RESTAPI_device_commandHandler::LEDs() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -527,7 +539,8 @@ void RESTAPI_device_commandHandler::Trace() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto Duration = Get(RESTAPI::Protocol::DURATION, Obj, 30);
@@ -536,7 +549,7 @@ void RESTAPI_device_commandHandler::Trace() {
auto Network = GetS(RESTAPI::Protocol::NETWORK, Obj);
auto Interface = GetS(RESTAPI::Protocol::INTERFACE, Obj);
auto UUID = MicroService::CreateUUID();
auto UUID = Daemon()->CreateUUID();
auto URI = FileUploader()->FullName() + UUID;
GWObjects::CommandDetails Cmd;
@@ -563,7 +576,8 @@ void RESTAPI_device_commandHandler::Trace() {
Cmd.Details = ParamStream.str();
FileUploader()->AddUUID(UUID);
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
@@ -574,7 +588,8 @@ void RESTAPI_device_commandHandler::WifiScan() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
if ((Obj->has(RESTAPI::Protocol::BANDS) &&
@@ -584,7 +599,7 @@ void RESTAPI_device_commandHandler::WifiScan() {
(!Obj->has(RESTAPI::Protocol::BANDS) &&
!Obj->has(RESTAPI::Protocol::CHANNELS)))) {
bool Verbose = GetB(RESTAPI::Protocol::VERBOSE, Obj);
auto UUID = MicroService::CreateUUID();
auto UUID = Daemon()->CreateUUID();
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
@@ -630,12 +645,13 @@ void RESTAPI_device_commandHandler::EventQueue() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
auto Types = Obj->getArray(RESTAPI::Protocol::TYPES);
auto UUID = MicroService::CreateUUID();
auto UUID = Daemon()->CreateUUID();
GWObjects::CommandDetails Cmd;
Cmd.SerialNumber = SerialNumber_;
@@ -673,7 +689,8 @@ void RESTAPI_device_commandHandler::MakeRequest() {
if ((SerialNumber_ != SNum) ||
(MessageType != uCentralProtocol::STATE &&
MessageType != uCentralProtocol::HEALTHCHECK)) {
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
return;
}
auto When = GetWhen(Obj);
@@ -681,7 +698,7 @@ void RESTAPI_device_commandHandler::MakeRequest() {
Cmd.SerialNumber = SerialNumber_;
Cmd.SubmittedBy = UserInfo_.webtoken.username_;
Cmd.UUID = MicroService::CreateUUID();
Cmd.UUID = Daemon()->CreateUUID();
Cmd.Command = uCentralProtocol::REQUEST;
Cmd.RunAt = When;
@@ -696,28 +713,29 @@ void RESTAPI_device_commandHandler::MakeRequest() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_ );
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, nullptr, this, Logger_ );
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
void RESTAPI_device_commandHandler::Rtty() {
Logger_.information(Poco::format("RTTY: user=%s serial=%s", UserInfo_.userinfo.email,SerialNumber_));
if (MicroService::instance().ConfigGetString("rtty.enabled", "false") == "true") {
if (Daemon()->ConfigGetString("rtty.enabled", "false") == "true") {
GWObjects::Device Device;
if (StorageService()->GetDevice(SerialNumber_, Device)) {
auto CommandUUID = MicroService::CreateUUID();
if (Storage()->GetDevice(SerialNumber_, Device)) {
auto CommandUUID = Daemon::instance()->CreateUUID();
GWObjects::RttySessionDetails Rtty{
.SerialNumber = SerialNumber_,
.Server = MicroService::instance().ConfigGetString("rtty.server", "localhost"),
.Port = MicroService::instance().ConfigGetInt("rtty.port", 5912),
.Token = MicroService::instance().ConfigGetString("rtty.token", "nothing"),
.TimeOut = MicroService::instance().ConfigGetInt("rtty.timeout", 60),
.Server = Daemon()->ConfigGetString("rtty.server", "localhost"),
.Port = Daemon()->ConfigGetInt("rtty.port", 5912),
.Token = Daemon()->ConfigGetString("rtty.token", "nothing"),
.TimeOut = Daemon()->ConfigGetInt("rtty.timeout", 60),
.ConnectionId = CommandUUID,
.Started = (uint64_t)time(nullptr),
.CommandUUID = CommandUUID,
.ViewPort = MicroService::instance().ConfigGetInt("rtty.viewport", 5913),
.ViewPort = Daemon()->ConfigGetInt("rtty.viewport", 5913),
};
@@ -746,9 +764,11 @@ void RESTAPI_device_commandHandler::MakeRequest() {
std::stringstream ParamStream;
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, &ReturnedObject, this, Logger_);
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response, 60000, &ReturnedObject, this, Logger_);
return;
}
return NotFound();
NotFound();
return;
}
ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE);
}
@@ -762,16 +782,19 @@ void RESTAPI_device_commandHandler::MakeRequest() {
auto SNum = Obj->get(RESTAPI::Protocol::SERIALNUMBER).toString();
if (SerialNumber_ != SNum) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
GWObjects::Device Device;
if (!StorageService()->GetDevice(SerialNumber_, Device)) {
return NotFound();
if (!Storage()->GetDevice(SerialNumber_, Device)) {
NotFound();
return;
}
if (!DeviceRegistry()->Connected(SerialNumber_)) {
return BadRequest(RESTAPI::Errors::DeviceNotConnected);
BadRequest(RESTAPI::Errors::DeviceNotConnected);
return;
}
auto Interval = Obj->get(RESTAPI::Protocol::INTERVAL);
@@ -800,7 +823,8 @@ void RESTAPI_device_commandHandler::MakeRequest() {
Answer.set("uri", Endpoint);
}
} else {
return BadRequest(RESTAPI::Errors::CannotCreateWS);
BadRequest(RESTAPI::Errors::CannotCreateWS);
return;
}
Cmd.UUID = NewUUID;
@@ -808,8 +832,9 @@ void RESTAPI_device_commandHandler::MakeRequest() {
Params.stringify(ParamStream);
Cmd.Details = ParamStream.str();
return RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response,
RESTAPI_RPC::WaitForCommand(Cmd, Params, *Request, *Response,
60000, &Answer, this, Logger_);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}

View File

@@ -9,8 +9,8 @@
#ifndef UCENTRAL_RESTAPI_DEVICECOMMANDHANDLER_H
#define UCENTRAL_RESTAPI_DEVICECOMMANDHANDLER_H
#include "framework/MicroService.h"
#include "framework/RESTAPI_protocol.h"
#include "RESTAPI_handler.h"
#include "RESTAPI_protocol.h"
namespace OpenWifi {
class RESTAPI_device_commandHandler : public RESTAPIHandler {

View File

@@ -7,29 +7,31 @@
//
#include "RESTAPI_device_handler.h"
#include "CentralConfig.h"
#include "ConfigurationCache.h"
#include "Poco/JSON/Parser.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/ConfigurationValidator.h"
#include "framework/MicroService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
#include "Utils.h"
#include "ConfigurationValidator.h"
#include "ConfigurationCache.h"
#include "CentralConfig.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_device_handler::DoGet() {
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
GWObjects::Device Device;
if (StorageService()->GetDevice(SerialNumber, Device)) {
if (Storage()->GetDevice(SerialNumber, Device)) {
Poco::JSON::Object Obj;
Device.to_json(Obj);
return ReturnObject(Obj);
ReturnObject(Obj);
return;
}
NotFound();
}
@@ -38,43 +40,14 @@ namespace OpenWifi {
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
std::string Arg;
if(HasParameter("oui",Arg) && Arg=="true" && SerialNumber.size()==6) {
std::set<std::string> Set;
std::vector<GWObjects::Device> Devices;
bool Done = false;
uint64_t Offset=1;
while(!Done) {
StorageService()->GetDevices(Offset,500,Devices);
for(const auto &i:Devices) {
if(i.SerialNumber.substr(0,6) == SerialNumber) {
Set.insert(i.SerialNumber);
}
}
if(Devices.size()<500)
Done=true;
Offset += Devices.size();
}
for(auto &i:Set) {
std::string SNum{i};
StorageService()->DeleteDevice(SNum);
}
return OK();
} else if (StorageService()->DeleteDevice(SerialNumber)) {
return OK();
if (Storage()->DeleteDevice(SerialNumber)) {
OK();
return;
}
NotFound();
}
@@ -82,43 +55,46 @@ namespace OpenWifi {
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
std::string Arg;
if(HasParameter("validateOnly",Arg) && Arg=="true") {
auto Body = ParseStream();
if(!Body->has("configuration")) {
return BadRequest("Must have 'configuration' element.");
BadRequest("Must have 'configuration' element.");
return;
}
auto Config=Body->get("configuration").toString();
Poco::JSON::Object Answer;
std::string Error;
auto Res = ValidateUCentralConfiguration(Config, Error);
auto Res = ValidateUCentralConfiguration(Config);
Answer.set("valid",Res);
if(!Error.empty())
Answer.set("error",Error);
return ReturnObject(Answer);
ReturnObject(Answer);
return;
}
if (!Utils::ValidSerialNumber(SerialNumber)) {
Logger_.warning(Poco::format("CREATE-DEVICE(%s): Illegal serial number.", SerialNumber));
return BadRequest( RESTAPI::Errors::InvalidSerialNumber);
BadRequest( RESTAPI::Errors::InvalidSerialNumber);
return;
}
auto Obj = ParseStream();
GWObjects::Device Device;
if (!Device.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
BadRequest(RESTAPI::Errors::InvalidJSONDocument);
return;
}
if(SerialNumber!=Device.SerialNumber) {
return BadRequest(RESTAPI::Errors::SerialNumberMismatch);
BadRequest(RESTAPI::Errors::SerialNumberMismatch);
return;
}
std::string Error;
if(Device.Configuration.empty() || (!Device.Configuration.empty() && !ValidateUCentralConfiguration(Device.Configuration,Error))) {
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
if(Device.Configuration.empty() || (!Device.Configuration.empty() && !ValidateUCentralConfiguration(Device.Configuration))) {
BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
return;
}
for(auto &i:Device.Notes)
@@ -131,11 +107,12 @@ namespace OpenWifi {
Poco::toLowerInPlace(Device.SerialNumber);
if (StorageService()->CreateDevice(Device)) {
if (Storage()->CreateDevice(Device)) {
SetCurrentConfigurationID(SerialNumber, Device.UUID);
Poco::JSON::Object DevObj;
Device.to_json(DevObj);
return ReturnObject(DevObj);
ReturnObject(DevObj);
return;
}
InternalError(RESTAPI::Errors::RecordNotCreated);
}
@@ -144,24 +121,27 @@ namespace OpenWifi {
std::string SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, "");
if(SerialNumber.empty()) {
return BadRequest(RESTAPI::Errors::MissingSerialNumber);
BadRequest(RESTAPI::Errors::MissingSerialNumber);
return;
}
auto Obj = ParseStream();
GWObjects::Device NewDevice;
if (!NewDevice.from_json(Obj)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
BadRequest(RESTAPI::Errors::InvalidJSONDocument);
return;
}
GWObjects::Device Existing;
if(!StorageService()->GetDevice(SerialNumber, Existing)) {
return NotFound();
if(!Storage()->GetDevice(SerialNumber, Existing)) {
NotFound();
return;
}
if(!NewDevice.Configuration.empty()) {
std::string Error;
if (!ValidateUCentralConfiguration(NewDevice.Configuration, Error)) {
return BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
if (!ValidateUCentralConfiguration(NewDevice.Configuration)) {
BadRequest(RESTAPI::Errors::ConfigBlockInvalid);
return;
}
Config::Config NewConfig(NewDevice.Configuration);
uint64_t NewConfigUUID = std::time(nullptr);
@@ -180,11 +160,12 @@ namespace OpenWifi {
}
Existing.LastConfigurationChange = std::time(nullptr);
if (StorageService()->UpdateDevice(Existing)) {
if (Storage()->UpdateDevice(Existing)) {
SetCurrentConfigurationID(SerialNumber, Existing.UUID);
Poco::JSON::Object DevObj;
NewDevice.to_json(DevObj);
return ReturnObject(DevObj);
ReturnObject(DevObj);
return;
}
InternalError(RESTAPI::Errors::RecordNotUpdated);
}

View File

@@ -9,9 +9,9 @@
#ifndef UCENTRAL_RESTAPI_DEVICEHANDLER_H
#define UCENTRAL_RESTAPI_DEVICEHANDLER_H
#include "RESTAPI_handler.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_device_handler : public RESTAPIHandler {

View File

@@ -10,9 +10,11 @@
#include "Poco/JSON/Stringifier.h"
#include "RESTAPI_devices_handler.h"
#include "RESTAPI_protocol.h"
#include "StorageService.h"
#include "framework/RESTAPI_protocol.h"
#include "framework/MicroService.h"
#include "Utils.h"
#include "Utils.h"
namespace OpenWifi {
void RESTAPI_devices_handler::DoGet() {
@@ -26,7 +28,7 @@ namespace OpenWifi {
std::vector<std::string> Numbers = Utils::Split(QB_.Select);
for (auto &i : Numbers) {
GWObjects::Device D;
if (StorageService()->GetDevice(i, D)) {
if (Storage()->GetDevice(i, D)) {
Poco::JSON::Object Obj;
if (deviceWithStatus)
D.to_json_with_status(Obj);
@@ -45,12 +47,13 @@ namespace OpenWifi {
} else if (QB_.CountOnly == true) {
uint64_t Count = 0;
if (StorageService()->GetDeviceCount(Count)) {
return ReturnCountOnly(Count);
if (Storage()->GetDeviceCount(Count)) {
ReturnCountOnly(Count);
return;
}
} else if (serialOnly) {
std::vector<std::string> SerialNumbers;
StorageService()->GetDeviceSerialNumbers(QB_.Offset, QB_.Limit, SerialNumbers);
Storage()->GetDeviceSerialNumbers(QB_.Offset, QB_.Limit, SerialNumbers);
Poco::JSON::Array Objects;
for (const auto &i : SerialNumbers) {
Objects.add(i);
@@ -58,7 +61,7 @@ namespace OpenWifi {
RetObj.set(RESTAPI::Protocol::SERIALNUMBERS, Objects);
} else {
std::vector<GWObjects::Device> Devices;
StorageService()->GetDevices(QB_.Offset, QB_.Limit, Devices);
Storage()->GetDevices(QB_.Offset, QB_.Limit, Devices);
Poco::JSON::Array Objects;
for (const auto &i : Devices) {
Poco::JSON::Object Obj;

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_DEVICESHANDLER_H
#define UCENTRAL_RESTAPI_DEVICESHANDLER_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_devices_handler : public RESTAPIHandler {

View File

@@ -47,19 +47,9 @@ namespace OpenWifi::RESTAPI::Errors {
static const std::string IdMustBe0{"To create a user, you must set the ID to 0"};
static const std::string InvalidUserRole{"Invalid userRole."};
static const std::string InvalidEmailAddress{"Invalid email address."};
static const std::string InvalidPassword{"Invalid password."};
static const std::string PasswordRejected{"Password was rejected. This maybe an old password."};
static const std::string InvalidIPRanges{"Invalid IP range specifications."};
static const std::string InvalidLOrderBy{"Invalid orderBy specification."};
static const std::string NeedMobileNumber{"You must provide at least one validated phone number."};
static const std::string BadMFAMethod{"MFA only supports sms or email."};
static const std::string InvalidCredentials{"Invalid credentials (username/password)."};
static const std::string InvalidPassword{"Password does not conform to basic password rules."};
static const std::string UserPendingVerification{"User access denied pending email verification."};
static const std::string PasswordMustBeChanged{"Password must be changed."};
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

@@ -12,9 +12,9 @@
#include "Poco/File.h"
#include "StorageService.h"
#include "framework/RESTAPI_errors.h"
#include "framework/RESTAPI_protocol.h"
#include <fstream>
#include "RESTAPI_protocol.h"
#include "RESTAPI_errors.h"
namespace OpenWifi {
void RESTAPI_file::DoGet() {
@@ -25,8 +25,9 @@ namespace OpenWifi {
Poco::File DownloadFile(FileUploader()->Path() + "/" + UUID);
std::string FileType;
if (!StorageService()->GetAttachedFile(UUID, SerialNumber, DownloadFile.path(), FileType)) {
return NotFound();
if (!Storage()->GetAttachedFile(UUID, SerialNumber, DownloadFile.path(), FileType)) {
NotFound();
return;
}
SendFile(DownloadFile, UUID);
DownloadFile.remove();
@@ -36,11 +37,13 @@ namespace OpenWifi {
auto UUID = GetBinding(RESTAPI::Protocol::FILEUUID, "");
if (UUID.empty()) {
return BadRequest(RESTAPI::Errors::MissingUUID);
BadRequest(RESTAPI::Errors::MissingUUID);
return;
}
if (StorageService()->RemoveAttachedFile(UUID)) {
return OK();
if (Storage()->RemoveAttachedFile(UUID)) {
OK();
return;
}
BadRequest(RESTAPI::Errors::CouldNotBeDeleted);
}

View File

@@ -9,7 +9,7 @@
#ifndef UCENTRAL_RESTAPI_FILE_H
#define UCENTRAL_RESTAPI_FILE_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_file : public RESTAPIHandler {

479
src/RESTAPI_handler.cpp Normal file
View File

@@ -0,0 +1,479 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <cctype>
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <future>
#include <chrono>
#include "Poco/URI.h"
#include "Poco/Net/OAuth20Credentials.h"
#include "RESTAPI_errors.h"
#ifdef TIP_SECURITY_SERVICE
#include "AuthService.h"
#else
#include "AuthClient.h"
#endif
#include "RESTAPI_handler.h"
#include "RESTAPI_protocol.h"
#include "Utils.h"
#include "Daemon.h"
namespace OpenWifi {
void RESTAPIHandler::handleRequest(Poco::Net::HTTPServerRequest &RequestIn,
Poco::Net::HTTPServerResponse &ResponseIn) {
try {
Request = &RequestIn;
Response = &ResponseIn;
if (!ContinueProcessing())
return;
if (AlwaysAuthorize_ && !IsAuthorized())
return;
ParseParameters();
if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
DoGet();
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST)
DoPost();
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE)
DoDelete();
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT)
DoPut();
else
BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod);
return;
} catch (const Poco::Exception &E) {
Logger_.log(E);
BadRequest(RESTAPI::Errors::InternalError);
}
}
const Poco::JSON::Object::Ptr &RESTAPIHandler::ParseStream() {
return IncomingParser_.parse(Request->stream()).extract<Poco::JSON::Object::Ptr>();
}
bool RESTAPIHandler::ParseBindings(const std::string & Request, const std::list<const char *> & EndPoints, BindingMap &bindings) {
bindings.clear();
std::vector<std::string> PathItems = Utils::Split(Request, '/');
for(const auto &EndPoint:EndPoints) {
std::vector<std::string> ParamItems = Utils::Split(EndPoint, '/');
if (PathItems.size() != ParamItems.size())
continue;
bool Matched = true;
for (auto i = 0; i != PathItems.size() && Matched; i++) {
if (PathItems[i] != ParamItems[i]) {
if (ParamItems[i][0] == '{') {
auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2);
bindings[Poco::toLower(ParamName)] = PathItems[i];
} else {
Matched = false;
}
}
}
if(Matched)
return true;
}
return false;
}
void RESTAPIHandler::PrintBindings() {
for (const auto &[key, value] : Bindings_)
std::cout << "Key = " << key << " Value= " << value << std::endl;
}
void RESTAPIHandler::ParseParameters() {
Poco::URI uri(Request->getURI());
Parameters_ = uri.getQueryParameters();
InitQueryBlock();
}
static bool is_number(const std::string &s) {
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
}
static bool is_bool(const std::string &s) {
if (s == "true" || s == "false")
return true;
return false;
}
uint64_t RESTAPIHandler::GetParameter(const std::string &Name, const uint64_t Default) {
auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; });
if(Hint==Parameters_.end() || !is_number(Hint->second))
return Default;
return std::stoull(Hint->second);
}
bool RESTAPIHandler::GetBoolParameter(const std::string &Name, bool Default) {
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; });
if(Hint==end(Parameters_) || !is_bool(Hint->second))
return Default;
return Hint->second=="true";
}
std::string RESTAPIHandler::GetParameter(const std::string &Name, const std::string &Default) {
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; });
if(Hint==end(Parameters_))
return Default;
return Hint->second;
}
bool RESTAPIHandler::HasParameter(const std::string &Name, std::string &Value) {
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; });
if(Hint==end(Parameters_))
return false;
Value = Hint->second;
return true;
}
bool RESTAPIHandler::HasParameter(const std::string &Name, uint64_t & Value) {
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; });
if(Hint==end(Parameters_))
return false;
Value = std::stoull(Hint->second);
return true;
}
const std::string &RESTAPIHandler::GetBinding(const std::string &Name, const std::string &Default) {
auto E = Bindings_.find(Poco::toLower(Name));
if (E == Bindings_.end())
return Default;
return E->second;
}
static std::string MakeList(const std::vector<std::string> &L) {
std::string Return;
for (const auto &i : L)
if (Return.empty())
Return = i;
else
Return += ", " + i;
return Return;
}
bool RESTAPIHandler::AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value) {
if(O->has(Field)) {
Value = O->get(Field).toString();
return true;
}
return false;
}
bool RESTAPIHandler::AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value) {
if(O->has(Field)) {
Value = O->get(Field);
return true;
}
return false;
}
void RESTAPIHandler::AddCORS() {
auto Origin = Request->find("Origin");
if (Origin != Request->end()) {
Response->set("Access-Control-Allow-Origin", Origin->second);
Response->set("Vary", "Origin");
} else {
Response->set("Access-Control-Allow-Origin", "*");
}
Response->set("Access-Control-Allow-Headers", "*");
Response->set("Access-Control-Allow-Methods", MakeList(Methods_));
Response->set("Access-Control-Max-Age", "86400");
}
void RESTAPIHandler::SetCommonHeaders(bool CloseConnection) {
Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
Response->setChunkedTransferEncoding(true);
Response->setContentType("application/json");
if(CloseConnection) {
Response->set("Connection", "close");
Response->setKeepAlive(false);
} else {
Response->setKeepAlive(true);
Response->set("Connection", "Keep-Alive");
Response->set("Keep-Alive", "timeout=5, max=1000");
}
}
void RESTAPIHandler::ProcessOptions() {
AddCORS();
SetCommonHeaders();
Response->setContentLength(0);
Response->set("Access-Control-Allow-Credentials", "true");
Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK);
Response->set("Vary", "Origin, Access-Control-Request-Headers, Access-Control-Request-Method");
Response->send();
}
void RESTAPIHandler::PrepareResponse( Poco::Net::HTTPResponse::HTTPStatus Status,
bool CloseConnection) {
Response->setStatus(Status);
AddCORS();
SetCommonHeaders(CloseConnection);
}
void RESTAPIHandler::BadRequest(const std::string & Reason) {
PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
Poco::JSON::Object ErrorObject;
ErrorObject.set("ErrorCode",400);
ErrorObject.set("ErrorDetails",Request->getMethod());
ErrorObject.set("ErrorDescription",Reason.empty() ? "Command is missing parameters or wrong values." : Reason) ;
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
}
void RESTAPIHandler::InternalError(const std::string & Reason) {
PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
Poco::JSON::Object ErrorObject;
ErrorObject.set("ErrorCode",500);
ErrorObject.set("ErrorDetails",Request->getMethod());
ErrorObject.set("ErrorDescription",Reason.empty() ? "Please try later or review the data submitted." : Reason) ;
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
}
void RESTAPIHandler::UnAuthorized(const std::string & Reason) {
PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN);
Poco::JSON::Object ErrorObject;
ErrorObject.set("ErrorCode",403);
ErrorObject.set("ErrorDetails",Request->getMethod());
ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ;
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
}
void RESTAPIHandler::NotFound() {
PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
Poco::JSON::Object ErrorObject;
ErrorObject.set("ErrorCode",404);
ErrorObject.set("ErrorDetails",Request->getMethod());
ErrorObject.set("ErrorDescription","This resource does not exist.");
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
Logger_.debug(Poco::format("RES-NOTFOUND: User='%s' Method='%s' Path='%s",
Utils::FormatIPv6(Request->clientAddress().toString()),
Request->getMethod(),
Request->getURI()));
}
void RESTAPIHandler::OK() {
PrepareResponse();
if( Request->getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE ||
Request->getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) {
Response->send();
} else {
Poco::JSON::Object ErrorObject;
ErrorObject.set("Code", 0);
ErrorObject.set("Operation", Request->getMethod());
ErrorObject.set("Details", "Command completed.");
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
}
}
void RESTAPIHandler::SendFile(Poco::File & File, const std::string & UUID) {
Response->set("Content-Type","application/octet-stream");
Response->set("Content-Disposition", "attachment; filename=" + UUID );
Response->set("Content-Transfer-Encoding","binary");
Response->set("Accept-Ranges", "bytes");
Response->set("Cache-Control", "private");
Response->set("Pragma", "private");
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
Response->set("Content-Length", std::to_string(File.getSize()));
AddCORS();
Response->sendFile(File.path(),"application/octet-stream");
}
void RESTAPIHandler::SendFile(Poco::File & File) {
Poco::Path P(File.path());
auto MT = Utils::FindMediaType(File);
if(MT.Encoding==Utils::BINARY) {
Response->set("Content-Transfer-Encoding","binary");
Response->set("Accept-Ranges", "bytes");
}
Response->set("Cache-Control", "private");
Response->set("Pragma", "private");
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
AddCORS();
Response->sendFile(File.path(),MT.ContentType);
}
void RESTAPIHandler::SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name) {
auto MT = Utils::FindMediaType(Name);
if(MT.Encoding==Utils::BINARY) {
Response->set("Content-Transfer-Encoding","binary");
Response->set("Accept-Ranges", "bytes");
}
Response->set("Content-Disposition", "attachment; filename=" + Name );
Response->set("Accept-Ranges", "bytes");
Response->set("Cache-Control", "private");
Response->set("Pragma", "private");
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
AddCORS();
Response->sendFile(TempAvatar.path(),MT.ContentType);
}
void RESTAPIHandler::SendHTMLFileBack(Poco::File & File,
const Types::StringPairVec & FormVars) {
Response->set("Pragma", "private");
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
Response->set("Content-Length", std::to_string(File.getSize()));
AddCORS();
auto FormContent = Utils::LoadFile(File.path());
Utils::ReplaceVariables(FormContent, FormVars);
Response->setChunkedTransferEncoding(true);
Response->setContentType("text/html");
std::ostream& ostr = Response->send();
ostr << FormContent;
}
void RESTAPIHandler::ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection) {
PrepareResponse(Status, CloseConnection);
if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) {
Response->setContentLength(0);
Response->erase("Content-Type");
Response->setChunkedTransferEncoding(false);
}
Response->send();
}
bool RESTAPIHandler::ContinueProcessing() {
if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
ProcessOptions();
return false;
} else if (std::find(Methods_.begin(), Methods_.end(), Request->getMethod()) == Methods_.end()) {
BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod);
return false;
}
return true;
}
bool RESTAPIHandler::IsAuthorized() {
if(Internal_) {
auto Allowed = Daemon()->IsValidAPIKEY(*Request);
if(!Allowed) {
if(Server_.LogBadTokens(false)) {
Logger_.debug(Poco::format("I-REQ-DENIED(%s): Method='%s' Path='%s",
Utils::FormatIPv6(Request->clientAddress().toString()),
Request->getMethod(), Request->getURI()));
}
} else {
auto Id = Request->get("X-INTERNAL-NAME", "unknown");
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(Poco::format("I-REQ-ALLOWED(%s): User='%s' Method='%s' Path='%s",
Utils::FormatIPv6(Request->clientAddress().toString()), Id,
Request->getMethod(), Request->getURI()));
}
}
return Allowed;
} else {
if (SessionToken_.empty()) {
try {
Poco::Net::OAuth20Credentials Auth(*Request);
if (Auth.getScheme() == "Bearer") {
SessionToken_ = Auth.getBearerToken();
}
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
#ifdef TIP_SECURITY_SERVICE
if (AuthService()->IsAuthorized(*Request, SessionToken_, UserInfo_)) {
#else
if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) {
#endif
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s",
Utils::FormatIPv6(Request->clientAddress().toString()),
UserInfo_.userinfo.email,
Request->clientAddress().toString(),
Request->getMethod(),
Request->getURI()));
}
return true;
} else {
if(Server_.LogBadTokens(true)) {
Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s",
Utils::FormatIPv6(Request->clientAddress().toString()),
Request->getMethod(), Request->getURI()));
}
UnAuthorized();
}
return false;
}
}
void RESTAPIHandler::ReturnObject(Poco::JSON::Object &Object) {
PrepareResponse();
std::ostream &Answer = Response->send();
Poco::JSON::Stringifier::stringify(Object, Answer);
}
void RESTAPIHandler::ReturnCountOnly(uint64_t Count) {
Poco::JSON::Object Answer;
Answer.set("count", Count);
ReturnObject(Answer);
}
bool RESTAPIHandler::InitQueryBlock() {
if(QueryBlockInitialized_)
return true;
QueryBlockInitialized_=true;
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_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100);
QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, "");
QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, "");
QB_.Lifetime = GetBoolParameter(RESTAPI::Protocol::LIFETIME,false);
QB_.LogType = GetParameter(RESTAPI::Protocol::LOGTYPE,0);
QB_.LastOnly = GetBoolParameter(RESTAPI::Protocol::LASTONLY,false);
QB_.Newest = GetBoolParameter(RESTAPI::Protocol::NEWEST,false);
QB_.CountOnly = GetBoolParameter(RESTAPI::Protocol::COUNTONLY,false);
if(QB_.Offset<1)
QB_.Offset=1;
return true;
}
[[nodiscard]] uint64_t RESTAPIHandler::Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default){
if(Obj->has(Parameter))
return Obj->get(Parameter);
return Default;
}
[[nodiscard]] std::string RESTAPIHandler::GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default){
if(Obj->has(Parameter))
return Obj->get(Parameter).toString();
return Default;
}
[[nodiscard]] bool RESTAPIHandler::GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default){
if(Obj->has(Parameter))
return Obj->get(Parameter).toString()=="true";
return Default;
}
[[nodiscard]] uint64_t RESTAPIHandler::GetWhen(const Poco::JSON::Object::Ptr &Obj) {
return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj);
}
}

233
src/RESTAPI_handler.h Normal file
View File

@@ -0,0 +1,233 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRAL_RESTAPI_HANDLER_H
#define UCENTRAL_RESTAPI_HANDLER_H
#include "Poco/URI.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/NetException.h"
#include "Poco/Net/PartHandler.h"
#include "Poco/Logger.h"
#include "Poco/File.h"
#include "Poco/TemporaryFile.h"
#include "Poco/JSON/Object.h"
#include "Poco/CountingStream.h"
#include "Poco/NullStream.h"
#include "RESTAPI_SecurityObjects.h"
#include "RESTAPI_utils.h"
#include "RESTAPI_GenericServer.h"
namespace OpenWifi {
class RESTAPI_PartHandler: public Poco::Net::PartHandler
{
public:
RESTAPI_PartHandler():
_length(0)
{
}
void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override
{
_type = header.get("Content-Type", "(unspecified)");
if (header.has("Content-Disposition"))
{
std::string disp;
Poco::Net::NameValueCollection params;
Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params);
_name = params.get("name", "(unnamed)");
_fileName = params.get("filename", "(unnamed)");
}
Poco::CountingInputStream istr(stream);
Poco::NullOutputStream ostr;
Poco::StreamCopier::copyStream(istr, ostr);
_length = (int)istr.chars();
}
[[nodiscard]] int length() const
{
return _length;
}
[[nodiscard]] const std::string& name() const
{
return _name;
}
[[nodiscard]] const std::string& fileName() const
{
return _fileName;
}
[[nodiscard]] const std::string& contentType() const
{
return _type;
}
private:
int _length;
std::string _type;
std::string _name;
std::string _fileName;
};
class RESTAPIHandler : public Poco::Net::HTTPRequestHandler {
public:
struct QueryBlock {
uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ;
std::string SerialNumber, Filter, Select;
bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false;
};
typedef std::map<std::string, std::string> BindingMap;
RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods, RESTAPI_GenericServer & Server, bool Internal=false, bool AlwaysAuthorize=true)
: Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Server_(Server), Internal_(Internal), AlwaysAuthorize_(AlwaysAuthorize) {}
static bool ParseBindings(const std::string & Request, const std::list<const char *> & EndPoints, BindingMap &Keys);
void PrintBindings();
void ParseParameters();
void AddCORS();
void SetCommonHeaders(bool CloseConnection=false);
void ProcessOptions();
void
PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK,
bool CloseConnection = false);
bool ContinueProcessing();
bool IsAuthorized();
uint64_t GetParameter(const std::string &Name, uint64_t Default);
std::string GetParameter(const std::string &Name, const std::string &Default);
bool GetBoolParameter(const std::string &Name, bool Default);
void BadRequest(const std::string &Reason );
void InternalError(const std::string &Reason = "");
void UnAuthorized(const std::string &Reason = "");
void ReturnObject(Poco::JSON::Object &Object);
void NotFound();
void OK();
void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status,
bool CloseConnection=false);
void SendFile(Poco::File & File, const std::string & UUID);
void SendHTMLFileBack(Poco::File & File,
const Types::StringPairVec & FormVars);
void SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name);
void SendFile(Poco::File & File);
const std::string &GetBinding(const std::string &Name, const std::string &Default);
bool InitQueryBlock();
void ReturnCountOnly(uint64_t Count);
[[nodiscard]] static uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0);
[[nodiscard]] static std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default="");
[[nodiscard]] static bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false);
[[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj);
bool HasParameter(const std::string &QueryParameter, std::string &Value);
bool HasParameter(const std::string &QueryParameter, uint64_t & Value);
static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value);
static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value);
template<typename T> void ReturnObject(const char *Name, const std::vector<T> & Objects) {
Poco::JSON::Object Answer;
RESTAPI_utils::field_to_json(Answer,Name,Objects);
ReturnObject(Answer);
}
Poco::Logger & Logger() { return Logger_; }
void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) final;
virtual void DoGet() = 0 ;
virtual void DoDelete() = 0 ;
virtual void DoPost() = 0 ;
virtual void DoPut() = 0 ;
const Poco::JSON::Object::Ptr & ParseStream();
protected:
BindingMap Bindings_;
Poco::URI::QueryParameters Parameters_;
Poco::Logger &Logger_;
std::string SessionToken_;
SecurityObjects::UserInfoAndPolicy UserInfo_;
std::vector<std::string> Methods_;
QueryBlock QB_;
bool Internal_=false;
bool QueryBlockInitialized_=false;
Poco::Net::HTTPServerRequest *Request= nullptr;
Poco::Net::HTTPServerResponse *Response= nullptr;
bool AlwaysAuthorize_=true;
Poco::JSON::Parser IncomingParser_;
RESTAPI_GenericServer & Server_;
};
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
public:
RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server)
: RESTAPIHandler(bindings, L, std::vector<std::string>{}, Server) {}
inline void DoGet() override {};
inline void DoPost() override {};
inline void DoPut() override {};
inline void DoDelete() override {};
};
template<class T>
constexpr auto test_has_PathName_method(T*)
-> decltype( T::PathName() , std::true_type{} )
{
return std::true_type{};
}
constexpr auto test_has_PathName_method(...) -> std::false_type
{
return std::false_type{};
}
template<typename T, typename... Args>
RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) {
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
return new T(Bindings, Logger, Server, false);
}
if constexpr (sizeof...(Args) == 0) {
return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server);
} else {
return RESTAPI_Router<Args...>(RequestedPath, Bindings, Logger, Server);
}
}
template<typename T, typename... Args>
RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) {
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
return new T(Bindings, Logger, Server, true);
}
if constexpr (sizeof...(Args) == 0) {
return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server);
} else {
return RESTAPI_Router_I<Args...>(RequestedPath, Bindings, Logger, Server);
}
}
}
#endif //UCENTRAL_RESTAPI_HANDLER_H

View File

@@ -3,6 +3,8 @@
//
#include "RESTAPI_ouis.h"
#include "RESTAPI_protocol.h"
#include "Utils.h"
#include "OUIServer.h"
namespace OpenWifi {

View File

@@ -5,7 +5,7 @@
#ifndef UCENTRALGW_RESTAPI_OUIS_H
#define UCENTRALGW_RESTAPI_OUIS_H
#include "framework/MicroService.h"
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_ouis : public RESTAPIHandler {

View File

@@ -113,7 +113,6 @@ namespace OpenWifi::RESTAPI::Protocol {
static const char * NEWPASSWORD = "newPassword";
static const char * USERS = "users";
static const char * WITHEXTENDEDINFO = "withExtendedInfo";
static const char * ERRORTEXT = "errorText";
static const char * ERRORCODE = "errorCode";
@@ -128,12 +127,9 @@ namespace OpenWifi::RESTAPI::Protocol {
static const char * ACCESSPOLICY = "accessPolicy";
static const char * PASSWORDPOLICY = "passwordPolicy";
static const char * FORGOTPASSWORD = "forgotPassword";
static const char * RESENDMFACODE = "resendMFACode";
static const char * COMPLETEMFACHALLENGE = "completeMFAChallenge";
static const char * ME = "me";
static const char * TELEMETRY = "telemetry";
static const char * INTERVAL = "interval";
static const char * UI = "UI";
}

96
src/RESTAPI_server.cpp Normal file
View File

@@ -0,0 +1,96 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include "RESTAPI_server.h"
#include "Poco/URI.h"
#include "RESTAPI_BlackList.h"
#include "RESTAPI_command.h"
#include "RESTAPI_commands.h"
#include "RESTAPI_default_configuration.h"
#include "RESTAPI_default_configurations.h"
#include "RESTAPI_device_commandHandler.h"
#include "RESTAPI_device_handler.h"
#include "RESTAPI_devices_handler.h"
#include "RESTAPI_file.h"
#include "RESTAPI_system_command.h"
#include "RESTAPI_ouis.h"
#include "RESTAPI_deviceDashboardHandler.h"
#include "Utils.h"
#include "RESTAPI_webSocketServer.h"
#include "RESTAPI_TelemetryWebSocket.h"
namespace OpenWifi {
class RESTAPI_server *RESTAPI_server::instance_ = nullptr;
int RESTAPI_server::Start() {
Logger_.information("Starting.");
Server_.InitLogging();
for(const auto & Svr: ConfigServersList_) {
Logger_.information(Poco::format("Starting: %s:%s Keyfile:%s CertFile: %s", Svr.Address(), std::to_string(Svr.Port()),
Svr.KeyFile(),Svr.CertFile()));
auto Sock{Svr.CreateSecureSocket(Logger_)};
Svr.LogCert(Logger_);
if(!Svr.RootCA().empty())
Svr.LogCas(Logger_);
auto Params = new Poco::Net::HTTPServerParams;
Params->setMaxThreads(50);
Params->setMaxQueued(200);
Params->setKeepAlive(true);
auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new RESTAPIServerRequestHandlerFactory(Server_), Pool_, Sock, Params);
NewServer->start();
RESTServers_.push_back(std::move(NewServer));
}
return 0;
}
void RESTAPI_server::reinitialize(Poco::Util::Application &self) {
Daemon()->LoadConfigurationFile();
Logger_.information("Reinitializing.");
Stop();
Start();
}
void RESTAPI_server::Stop() {
Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ )
svr->stop();
RESTServers_.clear();
}
Poco::Net::HTTPRequestHandler *RESTAPIServerRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) {
Poco::URI uri(Request.getURI());
const auto & Path = uri.getPath();
RESTAPIHandler::BindingMap Bindings;
return RESTAPI_Router<
RESTAPI_devices_handler,
RESTAPI_device_handler,
RESTAPI_device_commandHandler,
RESTAPI_default_configurations,
RESTAPI_default_configuration,
RESTAPI_command,
RESTAPI_commands,
RESTAPI_ouis,
RESTAPI_file,
RESTAPI_system_command,
RESTAPI_deviceDashboardHandler,
RESTAPI_webSocketServer,
RESTAPI_BlackList,
RESTAPI_TelemetryWebSocket>(Path,Bindings,Logger_, Server_);
}
} // namespace

61
src/RESTAPI_server.h Normal file
View File

@@ -0,0 +1,61 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRAL_UCENTRALRESTAPISERVER_H
#define UCENTRAL_UCENTRALRESTAPISERVER_H
#include "SubSystemServer.h"
#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/NetException.h"
#include "RESTAPI_GenericServer.h"
namespace OpenWifi {
class RESTAPI_server : public SubSystemServer {
public:
int Start() override;
void Stop() override;
static RESTAPI_server *instance() {
if (instance_ == nullptr) {
instance_ = new RESTAPI_server;
}
return instance_;
}
void reinitialize(Poco::Util::Application &self) override;
private:
static RESTAPI_server *instance_;
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
Poco::ThreadPool Pool_;
RESTAPI_GenericServer Server_;
RESTAPI_server() noexcept: SubSystemServer("RESTAPIServer", "RESTAPIServer", "openwifi.restapi")
{
}
};
class RESTAPIServerRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
public:
explicit RESTAPIServerRequestHandlerFactory(RESTAPI_GenericServer &Server) :
Logger_(RESTAPI_server::instance()->Logger()),
Server_(Server){}
Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override;
private:
Poco::Logger &Logger_;
RESTAPI_GenericServer &Server_;
};
inline RESTAPI_server * RESTAPI_server() { return RESTAPI_server::instance(); }
} // namespace
#endif //UCENTRAL_UCENTRALRESTAPISERVER_H

View File

@@ -0,0 +1,146 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include "RESTAPI_system_command.h"
#include "Poco/Exception.h"
#include "Poco/JSON/Parser.h"
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormat.h"
#include "Daemon.h"
#include "RESTAPI_protocol.h"
#include "RESTAPI_errors.h"
#include <thread>
#include <chrono>
using namespace std::chrono_literals;
namespace OpenWifi {
void RESTAPI_system_command::DoPost() {
auto Obj = ParseStream();
if (Obj->has(RESTAPI::Protocol::COMMAND)) {
auto Command = Poco::toLower(Obj->get(RESTAPI::Protocol::COMMAND).toString());
if (Command == RESTAPI::Protocol::SETLOGLEVEL) {
if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) &&
Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) {
auto ParametersBlock = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS);
for (const auto &i : *ParametersBlock) {
Poco::JSON::Parser pp;
auto InnerObj = pp.parse(i).extract<Poco::JSON::Object::Ptr>();
if (InnerObj->has(RESTAPI::Protocol::TAG) &&
InnerObj->has(RESTAPI::Protocol::VALUE)) {
auto Name = GetS(RESTAPI::Protocol::TAG, InnerObj);
auto Value = GetS(RESTAPI::Protocol::VALUE, InnerObj);
Daemon()->SetSubsystemLogLevel(Name, Value);
Logger_.information(
Poco::format("Setting log level for %s at %s", Name, Value));
}
}
OK();
return;
}
} else if (Command == RESTAPI::Protocol::GETLOGLEVELS) {
auto CurrentLogLevels = Daemon()->GetLogLevels();
Poco::JSON::Object Result;
Poco::JSON::Array Array;
for (auto &[Name, Level] : CurrentLogLevels) {
Poco::JSON::Object Pair;
Pair.set(RESTAPI::Protocol::TAG, Name);
Pair.set(RESTAPI::Protocol::VALUE, Level);
Array.add(Pair);
}
Result.set(RESTAPI::Protocol::TAGLIST, Array);
ReturnObject(Result);
return;
} else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) {
Poco::JSON::Object Result;
Poco::JSON::Array LevelNamesArray;
const Types::StringVec &LevelNames = Daemon()->GetLogLevelNames();
for (const auto &i : LevelNames)
LevelNamesArray.add(i);
Result.set(RESTAPI::Protocol::LIST, LevelNamesArray);
ReturnObject(Result);
return;
} else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) {
Poco::JSON::Object Result;
Poco::JSON::Array LevelNamesArray;
const Types::StringVec &SubSystemNames = Daemon()->GetSubSystems();
for (const auto &i : SubSystemNames)
LevelNamesArray.add(i);
Result.set(RESTAPI::Protocol::LIST, LevelNamesArray);
ReturnObject(Result);
return;
} else if (Command == RESTAPI::Protocol::STATS) {
} else if (Command == RESTAPI::Protocol::RELOAD) {
if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) &&
Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) {
auto SubSystems = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS);
std::vector<std::string> Names;
for (const auto &i : *SubSystems)
Names.push_back(i.toString());
std::thread ReloadThread([Names](){
std::this_thread::sleep_for(10000ms);
for(const auto &i:Names) {
if(i=="daemon")
Daemon()->Reload();
else
Daemon()->Reload(i);
}
});
ReloadThread.detach();
}
OK();
return;
}
} else {
BadRequest(RESTAPI::Errors::InvalidCommand);
return;
}
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
}
void RESTAPI_system_command::DoGet() {
std::string Arg;
if(HasParameter("command",Arg) && Arg=="info") {
Poco::JSON::Object Answer;
Answer.set(RESTAPI::Protocol::VERSION, Daemon()->Version());
Answer.set(RESTAPI::Protocol::UPTIME, Daemon()->uptime().totalSeconds());
Answer.set(RESTAPI::Protocol::START, Daemon()->startTime().epochTime());
Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName());
Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount());
Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName());
Poco::JSON::Array Certificates;
auto SubSystems = Daemon()->GetFullSubSystems();
std::set<std::string> CertNames;
for(const auto &i:SubSystems) {
auto Hosts=i->HostSize();
for(uint64_t j=0;j<Hosts;++j) {
auto CertFileName = i->Host(j).CertFile();
if(!CertFileName.empty()) {
auto InsertResult = CertNames.insert(CertFileName);
if(InsertResult.second) {
Poco::JSON::Object Inner;
Inner.set("filename", CertFileName);
Poco::Crypto::X509Certificate C(CertFileName);
auto ExpiresOn = C.expiresOn();
Inner.set("expiresOn",ExpiresOn.timestamp().epochTime());
Certificates.add(Inner);
}
}
}
}
Answer.set("certificates", Certificates);
ReturnObject(Answer);
return;
}
BadRequest(RESTAPI::Errors::InvalidCommand);
}
}

View File

@@ -0,0 +1,32 @@
//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#ifndef UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H
#define UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H
#include "RESTAPI_handler.h"
namespace OpenWifi {
class RESTAPI_system_command : public RESTAPIHandler {
public:
RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/system"};}
void DoGet() final;
void DoPost() final;
void DoPut() final {};
void DoDelete() final {};
};
}
#endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H

17
src/RESTAPI_utils.cpp Normal file
View File

@@ -0,0 +1,17 @@
//
// Created by stephane bourque on 2021-07-05.
//
#include "RESTAPI_utils.h"
namespace OpenWifi::RESTAPI_utils {
void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr) {
std::string D = ObjStr.empty() ? "{}" : ObjStr;
Poco::JSON::Parser P;
Poco::Dynamic::Var result = P.parse(D);
const auto &DetailsObj = result.extract<Poco::JSON::Object::Ptr>();
Obj.set(ObjName, DetailsObj);
}
}

Some files were not shown because too many files have changed in this diff Show More