mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralfms.git
				synced 2025-10-31 02:37:46 +00:00 
			
		
		
		
	Compare commits
	
		
			81 Commits
		
	
	
		
			v2.2.0-RC1
			...
			release/v2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4d5cb5caf0 | ||
|   | bd16c21df0 | ||
|   | 4a3274b169 | ||
|   | 88f7ab6bfa | ||
|   | 153d4ee11e | ||
|   | 21cb6ae297 | ||
|   | a22acace01 | ||
|   | 8db90d4c36 | ||
|   | c6a9b72872 | ||
|   | e0507ceb82 | ||
|   | afc8b942cb | ||
|   | 5737ab7c00 | ||
|   | 1b59208fa2 | ||
|   | f5ee31bd8b | ||
|   | 9f9faafb1c | ||
|   | dc9d155b78 | ||
|   | b8a7a7aff9 | ||
|   | 3195679fee | ||
|   | d1a9556b27 | ||
|   | c45850b749 | ||
|   | ab859d1f77 | ||
|   | da5720cf8d | ||
|   | 9d32c0747b | ||
|   | 4c6fd11534 | ||
|   | 3e726e43c0 | ||
|   | 15ebb56fba | ||
|   | 1c4b659ac3 | ||
|   | bc8b8f1d95 | ||
|   | 9bfc9c2f5a | ||
|   | eb20ff33c2 | ||
|   | 41afaa9b3f | ||
|   | a4b2862dbe | ||
|   | 14409ecb75 | ||
|   | d716cbcf67 | ||
|   | fc84e8c0bb | ||
|   | 26f566e678 | ||
|   | 0b23050dc3 | ||
|   | fecfa3c042 | ||
|   | 758241cef9 | ||
|   | b995275c85 | ||
|   | a1dd0a6d4f | ||
|   | 6273d562af | ||
|   | 7977c73d24 | ||
|   | 8eae458aa4 | ||
|   | 07e4b0a39a | ||
|   | 6f2a9199fe | ||
|   | 603047577e | ||
|   | 3df9647c13 | ||
|   | b89a638e68 | ||
|   | 21460802cc | ||
|   | 4e2d1dec03 | ||
|   | 5352ab4f15 | ||
|   | cf369d83f4 | ||
|   | 5847f5828a | ||
|   | 6285fcf08a | ||
|   | db49d0b693 | ||
|   | d0f994fd35 | ||
|   | 98f22b5eb9 | ||
|   | 8c3a9cb535 | ||
|   | 6ad5e695ee | ||
|   | 15a46ddb49 | ||
|   | 4ebdaab40c | ||
|   | 41402bb55b | ||
|   | 8b5cf1398b | ||
|   | 84d7856e00 | ||
|   | e5d8e528f5 | ||
|   | 7f589fa36f | ||
|   | 8463db855b | ||
|   | e8cc2abf05 | ||
|   | 96a8b8cc45 | ||
|   | 855b1b8eec | ||
|   | c188aacc8b | ||
|   | 71b78fed34 | ||
|   | 10f1228ec1 | ||
|   | 2f938551f9 | ||
|   | b97f77000c | ||
|   | 8f30237933 | ||
|   | 80a0eb0be4 | ||
|   | b8d2daf1c1 | ||
|   | 143a2f8bba | ||
|   | d6a7ef3e4e | 
| @@ -1,5 +1,5 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(owfms VERSION 2.2.0) | ||||
| project(owfms VERSION 2.4.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
|  | ||||
| @@ -25,8 +25,18 @@ else() | ||||
|     file(WRITE build ${BUILD_NUM}) | ||||
| endif() | ||||
|  | ||||
| set(BUILD_SHARED_LIBS 1) | ||||
| add_definitions(-DAPP_VERSION="${CMAKE_PROJECT_VERSION}" -DBUILD_NUMBER="${BUILD_NUM}" -DAWS_CUSTOM_MEMORY_MANAGEMENT) | ||||
| 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) | ||||
|  | ||||
| set(Boost_USE_STATIC_LIBS OFF) | ||||
| set(Boost_USE_MULTITHREADED ON) | ||||
| @@ -48,47 +58,49 @@ 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( owfms | ||||
|         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_firmwaresHandler.cpp src/RESTAPI/RESTAPI_firmwaresHandler.h | ||||
|         src/RESTAPI/RESTAPI_firmwareHandler.cpp src/RESTAPI/RESTAPI_firmwareHandler.h | ||||
|         src/RESTAPI/RESTAPI_historyHandler.cpp src/RESTAPI/RESTAPI_historyHandler.h | ||||
|         src/RESTAPI/RESTAPI_firmwareAgeHandler.cpp src/RESTAPI/RESTAPI_firmwareAgeHandler.h | ||||
|         src/RESTAPI/RESTAPI_connectedDevicesHandler.cpp src/RESTAPI/RESTAPI_connectedDevicesHandler.h | ||||
|         src/RESTAPI/RESTAPI_deviceReportHandler.cpp src/RESTAPI/RESTAPI_deviceReportHandler.h | ||||
|         src/RESTAPI/RESTAPI_connectedDeviceHandler.cpp src/RESTAPI/RESTAPI_connectedDeviceHandler.h | ||||
|         src/storage/storage_tables.cpp | ||||
|         src/storage/storage_firmwares.cpp | ||||
|         src/storage/storage_firmwares.h src/storage/storage_history.cpp | ||||
|         src/storage/storage_history.h src/storage/storage_deviceTypes.cpp | ||||
|         src/storage/storage_deviceInfo.cpp src/storage/storage_deviceInfo.h | ||||
|         src/APIServers.cpp | ||||
|         src/Dashboard.cpp src/Dashboard.h | ||||
|         src/Daemon.cpp src/Daemon.h | ||||
|         src/StorageService.cpp src/StorageService.h | ||||
|                 src/storage_tables.cpp | ||||
|                 src/storage_setup.cpp | ||||
|                 src/SubSystemServer.cpp src/SubSystemServer.h | ||||
|                 src/RESTAPI_handler.cpp src/RESTAPI_handler.h | ||||
|                 src/storage_firmwares.cpp | ||||
|                 src/Utils.cpp src/Utils.h | ||||
|                 src/RESTAPI_server.cpp src/RESTAPI_server.h | ||||
|                 src/RESTAPI_firmwaresHandler.cpp src/RESTAPI_firmwaresHandler.h | ||||
|                 src/RESTAPI_firmwareHandler.cpp src/RESTAPI_firmwareHandler.h | ||||
|                 src/RESTAPI_GWobjects.cpp src/RESTAPI_GWobjects.h | ||||
|                 src/ALBHealthCheckServer.h | ||||
|         src/ManifestCreator.cpp src/ManifestCreator.h | ||||
|                 src/KafkaManager.cpp src/KafkaManager.h | ||||
|                 src/MicroService.h src/MicroService.cpp | ||||
|                 src/AuthClient.h src/AuthClient.cpp | ||||
|                 src/RESTAPI_SecurityObjects.cpp src/RESTAPI_SecurityObjects.h | ||||
|                 src/RESTAPI_system_command.cpp src/RESTAPI_system_command.h | ||||
|                 src/OpenAPIRequest.h src/OpenAPIRequest.cpp | ||||
|                 src/RESTAPI_InternalServer.cpp src/RESTAPI_InternalServer.h | ||||
|                 src/RESTAPI_utils.cpp src/RESTAPI_utils.h | ||||
|                 src/RESTAPI_FMSObjects.cpp src/RESTAPI_FMSObjects.h | ||||
|                 src/storage_firmwares.h src/storage_history.cpp | ||||
|                 src/storage_history.h src/storage_deviceTypes.cpp | ||||
|                 src/RESTAPI_historyHandler.cpp src/RESTAPI_historyHandler.h | ||||
|         src/framework/MicroService.h | ||||
|         src/NewConnectionHandler.cpp src/NewConnectionHandler.h | ||||
|         src/LatestFirmwareCache.cpp src/LatestFirmwareCache.h | ||||
|         src/DeviceCache.cpp src/DeviceCache.h | ||||
|                 src/RESTAPI_firmwareAgeHandler.cpp src/RESTAPI_firmwareAgeHandler.h | ||||
|                 src/storage_deviceInfo.cpp src/storage_deviceInfo.h | ||||
|                 src/RESTAPI_connectedDevicesHandler.cpp src/RESTAPI_connectedDevicesHandler.h | ||||
|         src/FirmwareCache.cpp src/FirmwareCache.h | ||||
|                 src/RESTAPI_connectedDeviceHandler.cpp src/RESTAPI_connectedDeviceHandler.h | ||||
|                 src/RESTAPI_deviceReportHandler.cpp src/RESTAPI_deviceReportHandler.h | ||||
|                 src/RESTAPI_GenericServer.cpp src/RESTAPI_GenericServer.h | ||||
|                 src/OpenWifiTypes.h | ||||
|                 src/RESTAPI_errors.h) | ||||
|         src/SDK/Prov_SDK.cpp src/SDK/Prov_SDK.h | ||||
|         src/AutoUpdater.cpp src/AutoUpdater.h src/SDK/GW_SDK.cpp src/SDK/GW_SDK.h | ||||
|         ) | ||||
|  | ||||
| target_link_libraries(owfms PUBLIC | ||||
|         ${Poco_LIBRARIES} ${MySQL_LIBRARIES} | ||||
|   | ||||
							
								
								
									
										24
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -3,14 +3,15 @@ FROM alpine AS builder | ||||
| RUN apk add --update --no-cache \ | ||||
|     openssl openssh \ | ||||
|     ncurses-libs \ | ||||
|     bash util-linux coreutils curl \ | ||||
|     make cmake gcc g++ libstdc++ libgcc git zlib-dev \ | ||||
|     bash util-linux coreutils curl libcurl \ | ||||
|     make cmake gcc g++ libstdc++ libgcc git zlib-dev nlohmann-json \ | ||||
|     openssl-dev boost-dev curl-dev util-linux-dev \ | ||||
|     unixodbc-dev postgresql-dev mariadb-dev \ | ||||
|     librdkafka-dev | ||||
|  | ||||
| RUN git clone https://github.com/stephb9959/poco /poco | ||||
| RUN git clone https://github.com/stephb9959/cppkafka /cppkafka | ||||
| RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator | ||||
| RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp /aws-sdk-cpp | ||||
|  | ||||
| WORKDIR /aws-sdk-cpp | ||||
| @@ -37,14 +38,24 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| WORKDIR /json-schema-validator | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| ADD CMakeLists.txt build /owfms/ | ||||
| ADD cmake /owfms/cmake | ||||
| ADD src /owfms/src | ||||
| ADD .git /owfms/.git | ||||
|  | ||||
| WORKDIR /owfms | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR /owfms/cmake-build | ||||
| RUN cmake .. | ||||
| RUN cmake .. \ | ||||
|           -Dcrypto_LIBRARY=/usr/lib/libcrypto.so \ | ||||
|           -DBUILD_SHARED_LIBS=ON | ||||
| RUN cmake --build . --config Release -j8 | ||||
|  | ||||
| FROM alpine | ||||
| @@ -59,7 +70,7 @@ RUN addgroup -S "$OWFMS_USER" && \ | ||||
| RUN mkdir /openwifi | ||||
| RUN mkdir -p "$OWFMS_ROOT" "$OWFMS_CONFIG" && \ | ||||
|     chown "$OWFMS_USER": "$OWFMS_ROOT" "$OWFMS_CONFIG" | ||||
| RUN apk add --update --no-cache librdkafka curl-dev mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates | ||||
| RUN apk add --update --no-cache librdkafka curl-dev mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates bash jq curl postgresql-client | ||||
|  | ||||
| COPY --from=builder /owfms/cmake-build/owfms /openwifi/owfms | ||||
| COPY --from=builder /cppkafka/cmake-build/src/lib/* /lib/ | ||||
| @@ -67,11 +78,14 @@ COPY --from=builder /poco/cmake-build/lib/* /lib/ | ||||
| COPY --from=builder /aws-sdk-cpp/cmake-build/aws-cpp-sdk-core/libaws-cpp-sdk-core.so /lib/ | ||||
| COPY --from=builder /aws-sdk-cpp/cmake-build/aws-cpp-sdk-s3/libaws-cpp-sdk-s3.so /lib/ | ||||
|  | ||||
| COPY owfms.properties.tmpl ${OWFMS_CONFIG}/ | ||||
| COPY owfms.properties.tmpl / | ||||
| 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 | ||||
|  | ||||
| EXPOSE 16004 17004 16104 | ||||
|  | ||||
| ENTRYPOINT ["/docker-entrypoint.sh"] | ||||
|   | ||||
| @@ -40,7 +40,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWFMS_CONFIG"/owfms.properties ]]; t | ||||
|   STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owfms"} \ | ||||
|   STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owfms"} \ | ||||
|   STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \ | ||||
|   envsubst < $OWFMS_CONFIG/owfms.properties.tmpl > $OWFMS_CONFIG/owfms.properties | ||||
|   envsubst < /owfms.properties.tmpl > $OWFMS_CONFIG/owfms.properties | ||||
| fi | ||||
|  | ||||
| if [ "$1" = '/openwifi/owfms' -a "$(id -u)" = '0' ]; then | ||||
|   | ||||
							
								
								
									
										2
									
								
								helm/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								helm/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,3 @@ | ||||
| *.swp | ||||
| Chart.lock | ||||
| charts | ||||
|   | ||||
| @@ -5,14 +5,14 @@ name: owfms | ||||
| version: 0.1.0 | ||||
| dependencies: | ||||
| - name: postgresql | ||||
|   repository: https://charts.bitnami.com/bitnami | ||||
|   repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/ | ||||
|   version: 10.9.2 | ||||
|   condition: postgresql.enabled | ||||
| - name: mysql | ||||
|   repository: https://charts.bitnami.com/bitnami | ||||
|   repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/ | ||||
|   version: 8.8.3 | ||||
|   condition: mysql.enabled | ||||
| - name: mariadb | ||||
|   repository: https://charts.bitnami.com/bitnami | ||||
|   repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/ | ||||
|   version: 9.4.2 | ||||
|   condition: mariadb.enabled | ||||
|   | ||||
| @@ -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-ucentralfms@helm?ref=main | ||||
| $ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralfms@helm/owfms-0.1.0.tgz?ref=main | ||||
| ``` | ||||
|  | ||||
| The command deploys the Firmware on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. | ||||
|   | ||||
| @@ -30,3 +30,13 @@ Create chart name and version as used by the chart label. | ||||
| {{- define "owfms.chart" -}} | ||||
| {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} | ||||
| {{- end -}} | ||||
|  | ||||
| {{- define "owfms.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 -}} | ||||
|   | ||||
| @@ -24,6 +24,9 @@ spec: | ||||
|     metadata: | ||||
|       annotations: | ||||
|         checksum/config: {{ include "owfms.config" . | sha256sum }} | ||||
|         {{- with .Values.podAnnotations }} | ||||
|         {{- toYaml . | nindent 8 }} | ||||
|         {{- end }} | ||||
|       labels: | ||||
|         app.kubernetes.io/name: {{ include "owfms.name" . }} | ||||
|         app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| {{- range $ingress, $ingressValue := .Values.ingresses }} | ||||
| {{- if $ingressValue.enabled }} | ||||
| --- | ||||
| apiVersion: extensions/v1beta1 | ||||
| apiVersion: {{ include "owfms.ingress.apiVersion" $root }} | ||||
| kind: Ingress | ||||
| metadata: | ||||
|   name: {{ include "owfms.fullname" $root }}-{{ $ingress }} | ||||
| @@ -36,11 +36,25 @@ spec: | ||||
|       paths: | ||||
|       {{- range $ingressValue.paths }} | ||||
|         - path: {{ .path }} | ||||
|           {{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }} | ||||
|           pathType: {{ .pathType | default "ImplementationSpecific" }} | ||||
|           {{- end }} | ||||
|           backend: | ||||
|             {{- if $root.Capabilities.APIVersions.Has "networking.k8s.io/v1" }} | ||||
|             service: | ||||
|               name: {{ include "owfms.fullname" $root }}-{{ .serviceName }} | ||||
|               port: | ||||
|               {{- if kindIs "string" .servicePort }} | ||||
|                 name: {{ .servicePort }} | ||||
|               {{- else }} | ||||
|                 number: {{ .servicePort }} | ||||
|               {{- end }} | ||||
|             {{- else }} | ||||
|             serviceName: {{ include "owfms.fullname" $root }}-{{ .serviceName }} | ||||
|             servicePort: {{ .servicePort }} | ||||
|             {{- end }} | ||||
|       {{- end }} | ||||
|   {{- end }} | ||||
|  | ||||
| {{- end }} | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ fullnameOverride: "" | ||||
| images: | ||||
|   owfms: | ||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owfms | ||||
|     tag: v2.2.0-RC1 | ||||
|     tag: v2.4.0 | ||||
|     pullPolicy: Always | ||||
| #    regcred: | ||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||
| @@ -35,9 +35,11 @@ checks: | ||||
|         path: / | ||||
|         port: 16104 | ||||
|     readiness: | ||||
|       httpGet: | ||||
|         path: / | ||||
|         port: 16104 | ||||
|       exec: | ||||
|         command: | ||||
|           - /readiness_check | ||||
|       failureThreshold: 1 | ||||
|  | ||||
|  | ||||
| ingresses: | ||||
|   restapi: | ||||
| @@ -49,6 +51,7 @@ ingresses: | ||||
|     - restapi.chart-example.local | ||||
|     paths: | ||||
|     - path: / | ||||
|       pathType: ImplementationSpecific | ||||
|       serviceName: owfms | ||||
|       servicePort: restapi | ||||
|  | ||||
| @@ -91,6 +94,8 @@ tolerations: [] | ||||
|  | ||||
| affinity: {} | ||||
|  | ||||
| podAnnotations: {} | ||||
|  | ||||
| persistence: | ||||
|   enabled: true | ||||
|   # storageClassName: "-" | ||||
| @@ -103,8 +108,16 @@ persistence: | ||||
| public_env_variables: | ||||
|   OWFMS_ROOT: /owfms-data | ||||
|   OWFMS_CONFIG: /owfms-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: {} | ||||
| 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 | ||||
|  | ||||
| configProperties: | ||||
|   # -> Public part | ||||
|   | ||||
| @@ -50,6 +50,16 @@ 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: | ||||
|   | ||||
| @@ -48,6 +48,7 @@ s3.key =  ******************************************* | ||||
| s3.retry = 60 | ||||
| s3.bucket.uri = ucentral-ap-firmware.s3.amazonaws.com | ||||
|  | ||||
| autoupdater.enabled = true | ||||
|  | ||||
| ############################# | ||||
| # Generic information for all micro services | ||||
|   | ||||
| @@ -112,7 +112,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 = $UCENTRALSEC_ROOT/logs/log | ||||
| logging.channels.c2.path = $OWFMS_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 | ||||
|   | ||||
							
								
								
									
										65
									
								
								readiness_check
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										65
									
								
								readiness_check
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| #!/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 owfms instance | ||||
|   export RESTAPI_PORT=$(grep 'openwifi.restapi.host.0.port' $OWFMS_CONFIG/owfms.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' $OWFMS_CONFIG/owfms.properties | awk -F '=' '{print $2}' | xargs | envsubst) | ||||
|   curl localhost:$ALB_PORT | ||||
| fi | ||||
| @@ -1,118 +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. | ||||
| // | ||||
|  | ||||
| #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 | ||||
							
								
								
									
										41
									
								
								src/APIServers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/APIServers.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-23. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| #include "RESTAPI/RESTAPI_firmwareHandler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwaresHandler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwareAgeHandler.h" | ||||
| #include "RESTAPI/RESTAPI_connectedDeviceHandler.h" | ||||
| #include "RESTAPI/RESTAPI_connectedDevicesHandler.h" | ||||
| #include "RESTAPI/RESTAPI_historyHandler.h" | ||||
| #include "RESTAPI/RESTAPI_deviceReportHandler.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_firmwaresHandler, | ||||
|             RESTAPI_firmwareHandler, | ||||
|             RESTAPI_system_command, | ||||
|             RESTAPI_firmwareAgeHandler, | ||||
|             RESTAPI_connectedDevicesHandler, | ||||
|             RESTAPI_connectedDeviceHandler, | ||||
|             RESTAPI_historyHandler, | ||||
|             RESTAPI_deviceReportHandler | ||||
|             >(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_firmwaresHandler, | ||||
|             RESTAPI_firmwareHandler, | ||||
|             RESTAPI_system_command, | ||||
|             RESTAPI_connectedDevicesHandler, | ||||
|             RESTAPI_connectedDeviceHandler | ||||
|             >(Path, Bindings, L, S); | ||||
|     } | ||||
| } | ||||
| @@ -1,93 +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 <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; | ||||
| 	} | ||||
| } | ||||
| @@ -1,45 +0,0 @@ | ||||
| // | ||||
| // 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 | ||||
							
								
								
									
										104
									
								
								src/AutoUpdater.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/AutoUpdater.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #include "AutoUpdater.h" | ||||
| #include "SDK/Prov_SDK.h" | ||||
| #include "SDK/GW_SDK.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
| #include "StorageService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     int AutoUpdater::Start() { | ||||
|         Running_ = true; | ||||
|         AutoUpdaterFrequency_ = MicroService::instance().ConfigGetInt("autoupdater.frequency",600); | ||||
|         AutoUpdaterEnabled_ = MicroService::instance().ConfigGetBool("autoupdater.enabled", false); | ||||
|         if(AutoUpdaterEnabled_) | ||||
|             Thr_.start(*this); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     void AutoUpdater::Stop() { | ||||
|         Running_ = false; | ||||
|         if(AutoUpdaterEnabled_) { | ||||
|             Thr_.wakeUp(); | ||||
|             Thr_.join(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void AutoUpdater::ToBeUpgraded(std::string serialNumber, std::string DeviceType) { | ||||
|         if(!AutoUpdaterEnabled_) | ||||
|             return; | ||||
|         std::lock_guard G(Mutex_); | ||||
|         Queue_.emplace_back(std::make_pair(std::move(serialNumber),std::move(DeviceType))); | ||||
|     } | ||||
|  | ||||
|     void AutoUpdater::run() { | ||||
|         while(Running_) { | ||||
|             Poco::Thread::trySleep(2000); | ||||
|             if(!Running_) | ||||
|                 break; | ||||
|             std::unique_lock    L(Mutex_); | ||||
|             while(!Queue_.empty() && Running_) { | ||||
|                 auto Entry = Queue_.front(); | ||||
|                 Queue_.pop_front(); | ||||
|                 try { | ||||
|                     Logger_.debug(Poco::format("Preparing to upgrade %s",Entry.first)); | ||||
|                     auto CacheEntry = Cache_.find(Entry.first); | ||||
|                     uint64_t Now = std::time(nullptr); | ||||
|                     std::string firmwareUpgrade; | ||||
|                     bool        firmwareRCOnly; | ||||
|                     if(CacheEntry == Cache_.end() || (CacheEntry->second.LastCheck-Now)>300) { | ||||
|                         //  get the firmware settings for that device. | ||||
|                         SerialCache     C; | ||||
|                         C.LastCheck = Now; | ||||
|                         if(OpenWifi::SDK::Prov::GetFirmwareOptions(Entry.first, firmwareUpgrade, firmwareRCOnly)) { | ||||
|                             Logger_.debug(Poco::format("Found firmware options for %s",Entry.first)); | ||||
|                             C.firmwareRCOnly = firmwareRCOnly; | ||||
|                             C.firmwareUpgrade = firmwareUpgrade; | ||||
|                         } else { | ||||
|                             Logger_.debug(Poco::format("Found no firmware options for %s",Entry.first)); | ||||
|                             C.firmwareRCOnly = firmwareRCOnly; | ||||
|                             C.firmwareUpgrade = firmwareUpgrade; | ||||
|                         } | ||||
|                         Cache_[Entry.first] = C; | ||||
|                     } else { | ||||
|  | ||||
|                     } | ||||
|  | ||||
|                     if(firmwareUpgrade=="no") { | ||||
|                         Logger_.information(Poco::format("Device %s not upgradable. Provisioning service settings.",Entry.first)); | ||||
|                         continue; | ||||
|                     } | ||||
|  | ||||
|                     LatestFirmwareCacheEntry    fwEntry; | ||||
|                     FMSObjects::Firmware        fwDetails; | ||||
|                     auto LF = LatestFirmwareCache()->FindLatestFirmware(Entry.second, fwEntry ); | ||||
|                     if(LF) { | ||||
|                         if(StorageService()->GetFirmware(fwEntry.Id,fwDetails)) { | ||||
|                             //  send the command to upgrade this device... | ||||
|                             Logger_.information(Poco::format("Upgrading %s to version %s", Entry.first, fwEntry.Revision)); | ||||
|                             if(OpenWifi::SDK::GW::SendFirmwareUpgradeCommand(Entry.first,fwDetails.uri)) { | ||||
|                                 Logger_.information(Poco::format("Upgrade command sent for %s",Entry.first)); | ||||
|                             } else { | ||||
|                                 Logger_.information(Poco::format("Upgrade command not sent for %s",Entry.first)); | ||||
|                             } | ||||
|                         } else { | ||||
|                             Logger_.information(Poco::format("Firmware for device %s (%s) cannot be found.", Entry.first, Entry.second )); | ||||
|                         } | ||||
|                     } else { | ||||
|                         Logger_.information(Poco::format("Firmware for device %s (%s) cannot be found.", Entry.first, Entry.second )); | ||||
|                     } | ||||
|                 } catch (...) { | ||||
|                     Logger_.information(Poco::format("Exception during auto update for device %s.", Entry.first )); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void AutoUpdater::reinitialize(Poco::Util::Application &self) { | ||||
|         Logger_.information("Reinitializing."); | ||||
|         Reset(); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										54
									
								
								src/AutoUpdater.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/AutoUpdater.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_AUTOUPDATER_H | ||||
| #define OWFMS_AUTOUPDATER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include <deque> | ||||
| #include "Poco/Util/Application.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| class AutoUpdater : public SubSystemServer, Poco::Runnable { | ||||
|     public: | ||||
|  | ||||
|     struct SerialCache { | ||||
|             uint64_t        LastCheck=0; | ||||
|             std::string     firmwareUpgrade; | ||||
|             bool            firmwareRCOnly=false; | ||||
|         }; | ||||
|  | ||||
|         static AutoUpdater *instance() { | ||||
|             static AutoUpdater *instance_ = new AutoUpdater; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         void run() final; | ||||
|         void ToBeUpgraded(std::string serialNumber, std::string DeviceType); | ||||
|         inline void Reset() { | ||||
|             std::lock_guard   G(Mutex_); | ||||
|             Cache_.clear(); | ||||
|             Queue_.clear(); | ||||
|         } | ||||
|         void reinitialize(Poco::Util::Application &self) final; | ||||
|  | ||||
|     private: | ||||
|         std::atomic_bool                                Running_=false; | ||||
|         Poco::Thread                                    Thr_; | ||||
|         std::map<std::string,SerialCache>               Cache_; | ||||
|         std::deque<std::pair<std::string,std::string>>  Queue_; | ||||
|         uint64_t                            AutoUpdaterFrequency_=600; | ||||
|         bool                                AutoUpdaterEnabled_=true; | ||||
|         explicit AutoUpdater() noexcept: | ||||
|         SubSystemServer("AutoUpdater", "AUTO-UPDATER", "autoupdater") | ||||
|             { | ||||
|             } | ||||
|     }; | ||||
|  | ||||
|     inline AutoUpdater * AutoUpdater() { return AutoUpdater::instance(); } | ||||
| } | ||||
|  | ||||
| #endif //OWFMS_AUTOUPDATER_H | ||||
| @@ -2,8 +2,6 @@ | ||||
| // Created by Stephane Bourque on 2021-05-07. | ||||
| // | ||||
|  | ||||
| #include <boost/algorithm/string.hpp> | ||||
|  | ||||
| #include <aws/core/Aws.h> | ||||
| #include <aws/s3/model/CreateBucketRequest.h> | ||||
| #include <aws/s3/model/PutObjectRequest.h> | ||||
| @@ -11,20 +9,14 @@ | ||||
| #include <aws/s3/model/PutBucketAclRequest.h> | ||||
| #include <aws/s3/model/GetBucketAclRequest.h> | ||||
|  | ||||
|  | ||||
| #include "Poco/Util/Application.h" | ||||
| #include "Poco/Net/SSLManager.h" | ||||
|  | ||||
| #include "Daemon.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_server.h" | ||||
| #include "RESTAPI_InternalServer.h" | ||||
| #include "ManifestCreator.h" | ||||
| #include "KafkaManager.h" | ||||
| #include "NewConnectionHandler.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
| #include "DeviceCache.h" | ||||
| #include "FirmwareCache.h" | ||||
| #include "AutoUpdater.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class Daemon *Daemon::instance_ = nullptr; | ||||
| @@ -36,14 +28,14 @@ namespace OpenWifi { | ||||
|                                    vDAEMON_CONFIG_ENV_VAR, | ||||
|                                    vDAEMON_APP_NAME, | ||||
|                                    vDAEMON_BUS_TIMER, | ||||
|                                    Types::SubSystemVec{Storage(), | ||||
|                                    SubSystemVec{ | ||||
|                                             StorageService(), | ||||
|                                             FirmwareCache(), | ||||
|                                             LatestFirmwareCache(), | ||||
|                                             DeviceCache(), | ||||
|                                             NewConnectionHandler(), | ||||
|                                                        RESTAPI_server(), | ||||
|                                                        RESTAPI_InternalServer(), | ||||
|                                                        ManifestCreator() | ||||
|                                             ManifestCreator(), | ||||
|                                             AutoUpdater() | ||||
|                                    }); | ||||
|         } | ||||
|         return instance_; | ||||
| @@ -53,6 +45,9 @@ namespace OpenWifi { | ||||
|         MicroService::initialize(*this); | ||||
|     } | ||||
|  | ||||
|     void MicroServicePostInitialization() { | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
|   | ||||
							
								
								
									
										18
									
								
								src/Daemon.h
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								src/Daemon.h
									
									
									
									
									
								
							| @@ -2,22 +2,12 @@ | ||||
| // Created by stephane bourque on 2021-05-07. | ||||
| // | ||||
|  | ||||
| #include <list> | ||||
|  | ||||
| #ifndef UCENTRALFWS_DAEMON_H | ||||
| #define UCENTRALFWS_DAEMON_H | ||||
|  | ||||
| #include "Poco/Util/Application.h" | ||||
| #include "Poco/Util/ServerApplication.h" | ||||
| #include "Poco/ErrorHandler.h" | ||||
| #include "Poco/UUIDGenerator.h" | ||||
| #include "Poco/Crypto/RSAKey.h" | ||||
| #include "Poco/Crypto/CipherFactory.h" | ||||
| #include "Poco/Crypto/Cipher.h" | ||||
|  | ||||
| #include "MicroService.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "Dashboard.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -35,7 +25,7 @@ namespace OpenWifi { | ||||
|                         const std::string & ConfigEnv, | ||||
|                         const std::string & AppName, | ||||
|                         uint64_t 	BusTimer, | ||||
|                         const Types::SubSystemVec & SubSystems) : | ||||
|                         const SubSystemVec & SubSystems) : | ||||
|                 MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {}; | ||||
|  | ||||
|         void initialize(Poco::Util::Application &self); | ||||
|   | ||||
| @@ -11,7 +11,7 @@ namespace OpenWifi { | ||||
|  | ||||
| 		if(LastRun_==0 || (Now-LastRun_)>120) { | ||||
| 			DB_.reset(); | ||||
|             Storage()->GenerateDeviceReport(DB_); | ||||
| 			StorageService()->GenerateDeviceReport(DB_); | ||||
| 			LastRun_ = Now; | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -5,8 +5,8 @@ | ||||
| #ifndef UCENTRALGW_DASHBOARD_H | ||||
| #define UCENTRALGW_DASHBOARD_H | ||||
|  | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class DeviceDashboard { | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #include "DeviceCache.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class DeviceCache *DeviceCache::instance_ = nullptr; | ||||
|  | ||||
|     int DeviceCache::Start() { | ||||
|         return 0; | ||||
|   | ||||
| @@ -6,8 +6,7 @@ | ||||
| #define UCENTRALFMS_DEVICECACHE_H | ||||
|  | ||||
| #include <string> | ||||
| #include "SubSystemServer.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -21,9 +20,7 @@ namespace OpenWifi { | ||||
|     class DeviceCache : public SubSystemServer { | ||||
|     public: | ||||
|         static DeviceCache *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new DeviceCache; | ||||
|             } | ||||
|             static DeviceCache *instance_ = new DeviceCache; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
| @@ -35,7 +32,6 @@ namespace OpenWifi { | ||||
|         bool GetDevice(const std::string &SerialNumber, DeviceCacheEntry & E); | ||||
|  | ||||
|     private: | ||||
|         static DeviceCache 	*instance_; | ||||
|         std::atomic_bool    Running_=false; | ||||
|         DeviceCacheMap      DeviceCache_; | ||||
|         explicit DeviceCache() noexcept: | ||||
|   | ||||
| @@ -5,7 +5,6 @@ | ||||
| #include "FirmwareCache.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class FirmwareCache *FirmwareCache::instance_ = nullptr; | ||||
|  | ||||
|     int FirmwareCache::Start() { | ||||
|         return 0; | ||||
|   | ||||
| @@ -8,8 +8,8 @@ | ||||
| #include <map> | ||||
| #include <memory> | ||||
|  | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "SubSystemServer.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -18,9 +18,7 @@ namespace OpenWifi { | ||||
|     class FirmwareCache: public SubSystemServer { | ||||
|     public: | ||||
|         static FirmwareCache *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new FirmwareCache; | ||||
|             } | ||||
|             static FirmwareCache *instance_= new FirmwareCache; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
| @@ -32,7 +30,6 @@ namespace OpenWifi { | ||||
|  | ||||
|  | ||||
|     private: | ||||
|         static FirmwareCache 	*instance_; | ||||
|         std::atomic_bool        Running_=false; | ||||
|         FirmwareCacheMap        Cache_; | ||||
|         explicit FirmwareCache() noexcept: | ||||
|   | ||||
| @@ -1,221 +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 <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 | ||||
| @@ -1,74 +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. | ||||
| // | ||||
|  | ||||
| #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 | ||||
| @@ -6,10 +6,9 @@ | ||||
| #include "StorageService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class LatestFirmwareCache *LatestFirmwareCache::instance_ = nullptr; | ||||
|  | ||||
|     int LatestFirmwareCache::Start() { | ||||
|         Storage()->PopulateLatestFirmwareCache(); | ||||
|         StorageService()->PopulateLatestFirmwareCache(); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -10,8 +10,8 @@ | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
| #include "Poco/JWT/Signer.h" | ||||
| #include "Poco/SHA2Engine.h" | ||||
| #include "RESTAPI_SecurityObjects.h" | ||||
| #include "SubSystemServer.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -25,9 +25,7 @@ namespace OpenWifi { | ||||
|     class LatestFirmwareCache : public SubSystemServer { | ||||
|     public: | ||||
|         static LatestFirmwareCache *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new LatestFirmwareCache; | ||||
|             } | ||||
|             static LatestFirmwareCache *instance_ = new LatestFirmwareCache; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
| @@ -42,7 +40,6 @@ namespace OpenWifi { | ||||
|         bool IsLatest(const std::string &DeviceType, const std::string &Revision); | ||||
|  | ||||
|     private: | ||||
|         static LatestFirmwareCache 	*instance_; | ||||
|         LatestFirmwareCacheMap      Cache_; | ||||
|         Types::StringSet            RevisionSet_; | ||||
|         Types::StringSet            DeviceSet_; | ||||
|   | ||||
| @@ -6,19 +6,15 @@ | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/JSON/Stringifier.h" | ||||
|  | ||||
| #include "ManifestCreator.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include <aws/s3/model/ListObjectsRequest.h> | ||||
| #include <aws/s3/model/ListObjectsV2Request.h> | ||||
| #include <aws/s3/model/GetObjectRequest.h> | ||||
|  | ||||
| #include "Daemon.h" | ||||
| #include "ManifestCreator.h" | ||||
| #include "StorageService.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class ManifestCreator *ManifestCreator::instance_ = nullptr; | ||||
|  | ||||
|     void ManifestCreator::run() { | ||||
|         Running_ = true; | ||||
| @@ -31,7 +27,7 @@ namespace OpenWifi { | ||||
|             FirstRun = false; | ||||
|             Logger_.information("Performing DB refresh"); | ||||
|             S3BucketContent BucketList; | ||||
|             Storage()->RemoveOldFirmware(); | ||||
|             StorageService()->RemoveOldFirmware(); | ||||
|             ReadBucket(BucketList); | ||||
|             if(!Running_) | ||||
|                 break; | ||||
| @@ -97,8 +93,13 @@ namespace OpenWifi { | ||||
|         for(auto &[Release,BucketEntry]:BucketContent) { | ||||
|             FMSObjects::Firmware    F; | ||||
|             auto R = Release; | ||||
|             if(BucketEntry.Valid && !Storage()->GetFirmwareByName(R,BucketEntry.Compatible,F)) { | ||||
|                 F.id = Daemon()->CreateUUID(); | ||||
|  | ||||
|             // skip staging releases. | ||||
|             if(BucketEntry.URI.find("-staging-")!=std::string::npos) | ||||
|                 continue; | ||||
|  | ||||
|             if(BucketEntry.Valid && !StorageService()->GetFirmwareByName(R,BucketEntry.Compatible,F)) { | ||||
|                 F.id = MicroService::instance().CreateUUID(); | ||||
|                 F.release = Release; | ||||
|                 F.size = BucketEntry.S3Size; | ||||
|                 F.created = std::time(nullptr); | ||||
| @@ -107,7 +108,7 @@ namespace OpenWifi { | ||||
|                 F.uri = BucketEntry.URI; | ||||
|                 F.revision = BucketEntry.Revision; | ||||
|                 F.deviceType = BucketEntry.Compatible; | ||||
|                 if(Storage()->AddFirmware(F)) { | ||||
|                 if(StorageService()->AddFirmware(F)) { | ||||
|                     Logger_.information(Poco::format("Adding firmware '%s'",Release)); | ||||
|                 } else { | ||||
|                 } | ||||
| @@ -117,14 +118,14 @@ namespace OpenWifi { | ||||
|     } | ||||
|  | ||||
|     int ManifestCreator::Start() { | ||||
|         S3BucketName_ = Daemon()->ConfigGetString("s3.bucketname"); | ||||
|         S3Region_ = Daemon()->ConfigGetString("s3.region"); | ||||
|         S3Secret_ = Daemon()->ConfigGetString("s3.secret"); | ||||
|         S3Key_ = Daemon()->ConfigGetString("s3.key"); | ||||
|         S3Retry_ = Daemon()->ConfigGetInt("s3.retry",60); | ||||
|         S3BucketName_ = MicroService::instance().ConfigGetString("s3.bucketname"); | ||||
|         S3Region_ = MicroService::instance().ConfigGetString("s3.region"); | ||||
|         S3Secret_ = MicroService::instance().ConfigGetString("s3.secret"); | ||||
|         S3Key_ = MicroService::instance().ConfigGetString("s3.key"); | ||||
|         S3Retry_ = MicroService::instance().ConfigGetInt("s3.retry",60); | ||||
|  | ||||
|         DBRefresh_ = Daemon()->ConfigGetInt("firmwaredb.refresh",30*60); | ||||
|         MaxAge_ = Daemon()->ConfigGetInt("firmwaredb.maxage",90) * 24 * 60 * 60; | ||||
|         DBRefresh_ = MicroService::instance().ConfigGetInt("firmwaredb.refresh",30*60); | ||||
|         MaxAge_ = MicroService::instance().ConfigGetInt("firmwaredb.maxage",90) * 24 * 60 * 60; | ||||
|  | ||||
|         AwsConfig_.enableTcpKeepAlive = true; | ||||
|         AwsConfig_.enableEndpointDiscovery = true; | ||||
| @@ -179,7 +180,7 @@ namespace OpenWifi { | ||||
|         static const std::string UPGRADE("-upgrade.bin"); | ||||
|  | ||||
|         std::string     URIBase = "https://"; | ||||
|         URIBase += Daemon()->ConfigGetString("s3.bucket.uri"); | ||||
|         URIBase += MicroService::instance().ConfigGetString("s3.bucket.uri"); | ||||
|  | ||||
|         Bucket.clear(); | ||||
|  | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include <aws/s3/S3Client.h> | ||||
| #include <aws/core/auth/AWSCredentials.h> | ||||
|  | ||||
| #include "SubSystemServer.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -31,9 +31,7 @@ namespace OpenWifi { | ||||
|     class ManifestCreator : public SubSystemServer, Poco::Runnable { | ||||
|     public: | ||||
|         static ManifestCreator *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new ManifestCreator; | ||||
|             } | ||||
|             static ManifestCreator *instance_ = new ManifestCreator; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -1,532 +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 <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; | ||||
| 	} | ||||
| } | ||||
| @@ -1,183 +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. | ||||
| // | ||||
|  | ||||
| #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); | ||||
|  | ||||
| 		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 | ||||
| @@ -3,24 +3,23 @@ | ||||
| // | ||||
|  | ||||
| #include "NewConnectionHandler.h" | ||||
| #include "Kafka_topics.h" | ||||
| #include "KafkaManager.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "framework/KafkaTopics.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "StorageService.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
| #include "Utils.h" | ||||
| #include "uCentralProtocol.h" | ||||
| #include "framework/uCentral_Protocol.h" | ||||
| #include "DeviceCache.h" | ||||
| #include "AutoUpdater.h" | ||||
|  | ||||
| /* | ||||
| { "system" : { "id" : 6715803232063 , "host" : "https://localhost:17002" } , | ||||
|  "payload" : "{"capabilities":{"compatible":"linksys_ea8300","model":"Linksys EA8300 (Dallas)","network":{"lan":["eth0"],"wan":["eth1"]},"platform":"ap","switch":{"switch0":{"enable":true,"ports":[{"device":"eth0","need_tag":false,"num":0,"want_untag":true},{"num":1,"role":"lan"},{"num":2,"role":"lan"},{"num":3,"role":"lan"},{"num":4,"role":"lan"}],"reset":true,"roles":[{"device":"eth0","ports":"1 2 3 4 0","role":"lan"}]}},"wifi":{"platform/soc/a000000.wifi":{"band":["2G"],"channels":[1,2,3,4,5,6,7,8,9,10,11],"frequencies":[2412,2417,2422,2427,2432,2437,2442,2447,2452,2457,2462],"ht_capa":6639,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"rx_ant":3,"tx_ant":3,"vht_capa":865687986},"platform/soc/a800000.wifi":{"band":["5G"],"channels":[36,40,44,48,52,56,60,64],"frequencies":[5180,5200,5220,5240,5260,5280,5300,5320],"ht_capa":6639,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"rx_ant":3,"tx_ant":3,"vht_capa":865687986},"soc/40000000.pci/pci0000:00/0000:00:00.0/0000:01:00.0":{"band":["5G"],"channels":[100,104,108,112,116,120,124,128,132,136,140,144,149,153,157,161,165],"frequencies":[5500,5520,5540,5560,5580,5600,5620,5640,5660,5680,5700,5720,5745,5765,5785,5805,5825],"ht_capa":6639,"htmode":["HT20","HT40","VHT20","VHT40","VHT80"],"rx_ant":3,"tx_ant":3,"vht_capa":865696178}}},"firmware":"OpenWrt 21.02-SNAPSHOT r16011+53-6fd65c6573 / TIP-devel-0825cb93","serial":"24f5a207a130","uuid":1623866223}} | ||||
|  */ | ||||
|  | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class NewConnectionHandler *NewConnectionHandler::instance_ = nullptr; | ||||
|  | ||||
|     void NewConnectionHandler::run() { | ||||
|         Running_ = true ; | ||||
| @@ -65,13 +64,21 @@ namespace OpenWifi { | ||||
|                                 auto Revision = Storage::TrimRevision(PayloadObj->get(uCentralProtocol::FIRMWARE).toString()); | ||||
|                                 // std::cout << "ConnectionEvent: SerialNumber: " << SerialNumber << " DeviceType: " << DeviceType << " Revision:" << Revision << std::endl; | ||||
|                                 FMSObjects::FirmwareAgeDetails  FA; | ||||
|                                 if(Storage()->ComputeFirmwareAge(DeviceType, Revision, FA)) { | ||||
|                                     Storage()->SetDeviceRevision(SerialNumber, Revision, DeviceType, EndPoint); | ||||
|                                 if(StorageService()->ComputeFirmwareAge(DeviceType, Revision, FA)) { | ||||
|                                     StorageService()->SetDeviceRevision(SerialNumber, Revision, DeviceType, EndPoint); | ||||
|                                     if(FA.age) | ||||
|                                         Logger_.information(Poco::format("Device %s connection. Firmware is %s older than latest",SerialNumber, Utils::SecondsToNiceText(FA.age))); | ||||
|                                         Logger_.information(Poco::format("Device %s connection. Firmware is %s older than latest.",SerialNumber, Utils::SecondsToNiceText(FA.age))); | ||||
|                                     else | ||||
|                                         Logger_.information(Poco::format("Device %s connection. Device firmware is up to date.",SerialNumber)); | ||||
|                                 } | ||||
|                                 else { | ||||
|                                     Logger_.information(Poco::format("Device %s connection. Firmware age cannot be determined",SerialNumber)); | ||||
|                                 } | ||||
|  | ||||
|                                 if(!LatestFirmwareCache()->IsLatest(DeviceType, Revision)) { | ||||
|                                     // std::cout << "Device (connection): " << SerialNumber << " to be upgraded ... " << std::endl; | ||||
|                                     AutoUpdater()->ToBeUpgraded(SerialNumber, DeviceType); | ||||
|                                 } | ||||
|                                 DeviceCache()->AddToCache(Serial, DeviceType, EndPoint, Revision); | ||||
|                             } | ||||
|                         } else if(PayloadObj->has(uCentralProtocol::DISCONNECTION)) { | ||||
| @@ -79,7 +86,7 @@ namespace OpenWifi { | ||||
|                             if(DisconnectMessage->has(uCentralProtocol::SERIALNUMBER) && DisconnectMessage->has(uCentralProtocol::TIMESTAMP)) { | ||||
|                                 auto SNum = DisconnectMessage->get(uCentralProtocol::SERIALNUMBER).toString(); | ||||
|                                 auto Timestamp = DisconnectMessage->get(uCentralProtocol::TIMESTAMP); | ||||
|                                 Storage()->SetDeviceDisconnected(SNum,EndPoint); | ||||
|                                 StorageService()->SetDeviceDisconnected(SNum,EndPoint); | ||||
|                                 // std::cout << "DISCONNECTION:" << SerialNumber << std::endl; | ||||
|                             } | ||||
|                         } else if(PayloadObj->has(uCentralProtocol::PING)) { | ||||
| @@ -91,8 +98,12 @@ namespace OpenWifi { | ||||
|                                 auto Revision = Storage::TrimRevision(PingMessage->get(uCentralProtocol::FIRMWARE).toString()); | ||||
|                                 auto Serial = PingMessage->get( uCentralProtocol::SERIALNUMBER).toString(); | ||||
|                                 auto DeviceType = PingMessage->get( uCentralProtocol::COMPATIBLE).toString(); | ||||
|                                 Storage()->SetDeviceRevision(Serial, Revision, DeviceType, EndPoint); | ||||
|                                 StorageService()->SetDeviceRevision(Serial, Revision, DeviceType, EndPoint); | ||||
|                                 DeviceCache()->AddToCache(Serial, DeviceType, EndPoint, Revision); | ||||
|                                 if(!LatestFirmwareCache()->IsLatest(DeviceType, Revision)) { | ||||
|                                     // std::cout << "Device(ping): " << SerialNumber << " to be upgraded ... " << std::endl; | ||||
|                                     AutoUpdater()->ToBeUpgraded(SerialNumber, DeviceType); | ||||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|   | ||||
| @@ -6,8 +6,8 @@ | ||||
| #define UCENTRALFMS_NEWCONNECTIONHANDLER_H | ||||
|  | ||||
|  | ||||
| #include "SubSystemServer.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -15,9 +15,7 @@ namespace OpenWifi { | ||||
|     public: | ||||
|  | ||||
|         static NewConnectionHandler *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new NewConnectionHandler; | ||||
|             } | ||||
|             static NewConnectionHandler *instance_ = new NewConnectionHandler; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
| @@ -29,7 +27,6 @@ namespace OpenWifi { | ||||
|         void ConnectionReceived( const std::string & Key, const std::string & Message); | ||||
|  | ||||
|     private: | ||||
|         static NewConnectionHandler      *instance_; | ||||
|         Poco::Thread                Worker_; | ||||
|         std::atomic_bool            Running_ = false; | ||||
|         int                         ConnectionWatcherId_=0; | ||||
|   | ||||
| @@ -1,68 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-07-01. | ||||
| // | ||||
| #include <iostream> | ||||
|  | ||||
| #include "OpenAPIRequest.h" | ||||
|  | ||||
| #include "Poco/Net/HTTPSClientSession.h" | ||||
| #include <Poco/Net/HTTPClientSession.h> | ||||
| #include <Poco/Net/HTTPRequest.h> | ||||
| #include <Poco/Net/HTTPResponse.h> | ||||
| #include <Poco/StreamCopier.h> | ||||
| #include <Poco/JSON/Parser.h> | ||||
| #include <Poco/Path.h> | ||||
| #include <Poco/URI.h> | ||||
| #include <Poco/Exception.h> | ||||
| #include "Utils.h" | ||||
| #include "Daemon.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	OpenAPIRequestGet::OpenAPIRequestGet( 	const std::string & ServiceType, | ||||
| 											const std::string & EndPoint, | ||||
| 									 		Types::StringPairVec & QueryData, | ||||
| 											uint64_t msTimeout): | ||||
|  		Type_(ServiceType), | ||||
|  		EndPoint_(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; | ||||
| 	} | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-07-01. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALGW_OPENAPIREQUEST_H | ||||
| #define UCENTRALGW_OPENAPIREQUEST_H | ||||
|  | ||||
| #include "Poco/JSON/Object.h" | ||||
|  | ||||
| #include "OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class OpenAPIRequestGet { | ||||
| 	  public: | ||||
| 		explicit OpenAPIRequestGet( const std::string & Type, | ||||
| 								   	const 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 | ||||
| @@ -3,10 +3,10 @@ | ||||
| //
 | ||||
| 
 | ||||
| #include "RESTAPI_connectedDeviceHandler.h" | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "RESTAPI_errors.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
| 
 | ||||
| @@ -14,16 +14,14 @@ namespace OpenWifi { | ||||
|         auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER,""); | ||||
| 
 | ||||
|         if(SerialNumber.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::MissingSerialNumber); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::MissingSerialNumber); | ||||
|         } | ||||
| 
 | ||||
|         FMSObjects::DeviceConnectionInformation DevInfo; | ||||
|         if(Storage()->GetDevice(SerialNumber, DevInfo)) { | ||||
|         if(StorageService()->GetDevice(SerialNumber, DevInfo)) { | ||||
|             Poco::JSON::Object  Answer; | ||||
|             DevInfo.to_json(Answer); | ||||
|             ReturnObject(Answer); | ||||
|             return; | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         NotFound(); | ||||
|     } | ||||
| @@ -5,7 +5,7 @@ | ||||
| #ifndef UCENTRALFMS_RESTAPI_CONNECTEDDEVICEHANDLER_H | ||||
| #define UCENTRALFMS_RESTAPI_CONNECTEDDEVICEHANDLER_H | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_connectedDeviceHandler : public RESTAPIHandler { | ||||
| @@ -2,27 +2,27 @@ | ||||
| // Created by stephane bourque on 2021-07-18.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "RESTAPI_connectedDevicesHandler.h" | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/JSON/Array.h" | ||||
| 
 | ||||
| #include "RESTAPI_connectedDevicesHandler.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     void RESTAPI_connectedDevicesHandler::DoGet() { | ||||
|         std::vector<FMSObjects::DeviceConnectionInformation> Devices; | ||||
|         Poco::JSON::Object AnswerObj; | ||||
|         Poco::JSON::Array AnswerArr; | ||||
|         if (Storage()->GetDevices(QB_.Offset, QB_.Limit, Devices)) { | ||||
|         if (StorageService()->GetDevices(QB_.Offset, QB_.Limit, Devices)) { | ||||
|             for (const auto &i:Devices) { | ||||
|                 Poco::JSON::Object Obj; | ||||
|                 i.to_json(Obj); | ||||
|                 AnswerArr.add(Obj); | ||||
|             } | ||||
|             AnswerObj.set(RESTAPI::Protocol::DEVICES, AnswerArr); | ||||
|             ReturnObject(AnswerObj); | ||||
|             return; | ||||
|             return ReturnObject(AnswerObj); | ||||
|         } | ||||
|         AnswerObj.set(RESTAPI::Protocol::DEVICES, AnswerArr); | ||||
|         ReturnObject(AnswerObj); | ||||
| @@ -6,7 +6,7 @@ | ||||
| #define UCENTRALFMS_RESTAPI_CONNECTEDDEVICESHANDLER_H | ||||
| 
 | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_connectedDevicesHandler : public RESTAPIHandler { | ||||
| @@ -4,7 +4,7 @@ | ||||
| 
 | ||||
| #include "RESTAPI_deviceReportHandler.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Daemon.h" | ||||
| 
 | ||||
| @@ -5,7 +5,7 @@ | ||||
| #ifndef UCENTRALFMS_RESTAPI_DEVICEREPORTHANDLER_H | ||||
| #define UCENTRALFMS_RESTAPI_DEVICEREPORTHANDLER_H | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_deviceReportHandler : public RESTAPIHandler { | ||||
| @@ -6,12 +6,10 @@ | ||||
| 
 | ||||
| #include "StorageService.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Daemon.h" | ||||
| #include "Utils.h" | ||||
| #include "DeviceCache.h" | ||||
| #include "uCentralProtocol.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "RESTAPI_errors.h" | ||||
| #include "framework/uCentral_Protocol.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     void RESTAPI_firmwareAgeHandler::DoGet() { | ||||
| @@ -22,7 +20,7 @@ namespace OpenWifi { | ||||
|                 DeviceCacheEntry E; | ||||
|                 if (DeviceCache()->GetDevice(i, E)) { | ||||
|                     FMSObjects::FirmwareAgeDetails FA; | ||||
|                     if(Storage()->ComputeFirmwareAge(E.deviceType,E.revision,FA)) { | ||||
|                     if(StorageService()->ComputeFirmwareAge(E.deviceType,E.revision,FA)) { | ||||
|                         Poco::JSON::Object  O; | ||||
|                         FA.to_json(O); | ||||
|                         O.set(uCentralProtocol::SERIALNUMBER,i); | ||||
| @@ -40,26 +38,23 @@ namespace OpenWifi { | ||||
|             } | ||||
|             Poco::JSON::Object Answer; | ||||
|             Answer.set(RESTAPI::Protocol::AGES, Objects); | ||||
|             ReturnObject(Answer); | ||||
|             return; | ||||
|             return ReturnObject(Answer); | ||||
|         } else { | ||||
|             auto DeviceType = GetParameter(RESTAPI::Protocol::DEVICETYPE, ""); | ||||
|             auto Revision = GetParameter(RESTAPI::Protocol::REVISION, ""); | ||||
| 
 | ||||
|             if (DeviceType.empty() || Revision.empty()) { | ||||
|                 BadRequest(RESTAPI::Errors::BothDeviceTypeRevision); | ||||
|                 return; | ||||
|                 return BadRequest(RESTAPI::Errors::BothDeviceTypeRevision); | ||||
|             } | ||||
| 
 | ||||
|             Revision = Storage::TrimRevision(Revision); | ||||
| 
 | ||||
|             FMSObjects::FirmwareAgeDetails FA; | ||||
|             if (Storage()->ComputeFirmwareAge(DeviceType, Revision, FA)) { | ||||
|             if (StorageService()->ComputeFirmwareAge(DeviceType, Revision, FA)) { | ||||
|                 Poco::JSON::Object Answer; | ||||
| 
 | ||||
|                 FA.to_json(Answer); | ||||
|                 ReturnObject(Answer); | ||||
|                 return; | ||||
|                 return ReturnObject(Answer); | ||||
|             } | ||||
|             NotFound(); | ||||
|         } | ||||
| @@ -5,7 +5,7 @@ | ||||
| #ifndef UCENTRALFMS_RESTAPI_FIRMWAREAGEHANDLER_H | ||||
| #define UCENTRALFMS_RESTAPI_FIRMWAREAGEHANDLER_H | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwareAgeHandler : public RESTAPIHandler { | ||||
| @@ -6,11 +6,9 @@ | ||||
| 
 | ||||
| #include "RESTAPI_firmwareHandler.h" | ||||
| #include "StorageService.h" | ||||
| #include "Daemon.h" | ||||
| #include "uCentralProtocol.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "RESTAPI_utils.h" | ||||
| #include "RESTAPI_errors.h" | ||||
| #include "framework/uCentral_Protocol.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     void | ||||
| @@ -18,15 +16,13 @@ namespace OpenWifi { | ||||
|         auto Obj = ParseStream(); | ||||
|         FMSObjects::Firmware F; | ||||
|         if (!F.from_json(Obj)) { | ||||
|             BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
|         F.id = Daemon()->CreateUUID(); | ||||
|         if(Storage()->AddFirmware(F)) { | ||||
|         F.id = MicroService::instance().CreateUUID(); | ||||
|         if(StorageService()->AddFirmware(F)) { | ||||
|             Poco::JSON::Object  Answer; | ||||
|             F.to_json(Answer); | ||||
|             ReturnObject(Answer); | ||||
|             return; | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::RecordNotCreated); | ||||
|     } | ||||
| @@ -36,16 +32,14 @@ namespace OpenWifi { | ||||
|         auto UUID = GetBinding(uCentralProtocol::ID, ""); | ||||
| 
 | ||||
|         if(UUID.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|         } | ||||
| 
 | ||||
|         FMSObjects::Firmware F; | ||||
|         if (Storage()->GetFirmware(UUID, F)) { | ||||
|         if (StorageService()->GetFirmware(UUID, F)) { | ||||
|             Poco::JSON::Object Object; | ||||
|             F.to_json(Object); | ||||
|             ReturnObject(Object); | ||||
|             return; | ||||
|             return ReturnObject(Object); | ||||
|         } | ||||
|         NotFound(); | ||||
|     } | ||||
| @@ -54,13 +48,11 @@ namespace OpenWifi { | ||||
|     RESTAPI_firmwareHandler::DoDelete() { | ||||
|         auto UUID = GetBinding(uCentralProtocol::ID, ""); | ||||
|         if(UUID.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|         } | ||||
| 
 | ||||
|         if (Storage()->DeleteFirmware(UUID)) { | ||||
|             OK(); | ||||
|             return; | ||||
|         if (StorageService()->DeleteFirmware(UUID)) { | ||||
|             return OK(); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::CouldNotBeDeleted); | ||||
|     } | ||||
| @@ -68,21 +60,18 @@ namespace OpenWifi { | ||||
|     void RESTAPI_firmwareHandler::DoPut() { | ||||
|         auto UUID = GetBinding(uCentralProtocol::ID, ""); | ||||
|         if(UUID.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::MissingUUID); | ||||
|         } | ||||
| 
 | ||||
|         FMSObjects::Firmware    F; | ||||
|         if(!Storage()->GetFirmware(UUID, F)) { | ||||
|             NotFound(); | ||||
|             return; | ||||
|         if(!StorageService()->GetFirmware(UUID, F)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
| 
 | ||||
|         auto Obj = ParseStream(); | ||||
|         FMSObjects::Firmware    NewFirmware; | ||||
|         if(!NewFirmware.from_json(Obj)) { | ||||
|             BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
| 
 | ||||
|         if(Obj->has(RESTAPI::Protocol::DESCRIPTION)) | ||||
| @@ -96,11 +85,10 @@ namespace OpenWifi { | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if(Storage()->UpdateFirmware(UUID, F)) { | ||||
|         if(StorageService()->UpdateFirmware(UUID, F)) { | ||||
|             Poco::JSON::Object  Answer; | ||||
|             F.to_json(Answer); | ||||
|             ReturnObject(Answer); | ||||
|             return; | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::RecordNotUpdated); | ||||
|     } | ||||
| @@ -5,7 +5,7 @@ | ||||
| #ifndef UCENTRALFWS_RESTAPI_FIRMWAREHANDLER_H | ||||
| #define UCENTRALFWS_RESTAPI_FIRMWAREHANDLER_H | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwareHandler : public RESTAPIHandler { | ||||
| @@ -5,7 +5,7 @@ | ||||
| #include "RESTAPI_firmwaresHandler.h" | ||||
| #include "StorageService.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     void | ||||
| @@ -24,8 +24,7 @@ namespace OpenWifi { | ||||
|             } | ||||
|             Poco::JSON::Object RetObj; | ||||
|             RetObj.set(RESTAPI::Protocol::DEVICETYPES, ObjectArray); | ||||
|             ReturnObject(RetObj); | ||||
|             return; | ||||
|             return ReturnObject(RetObj); | ||||
|         } | ||||
| 
 | ||||
|         if(RevisionSet) { | ||||
| @@ -36,8 +35,7 @@ namespace OpenWifi { | ||||
|             } | ||||
|             Poco::JSON::Object RetObj; | ||||
|             RetObj.set(RESTAPI::Protocol::REVISIONS, ObjectArray); | ||||
|             ReturnObject(RetObj); | ||||
|             return; | ||||
|             return ReturnObject(RetObj); | ||||
|         } | ||||
| 
 | ||||
|         // special cases: if latestOnly and deviceType
 | ||||
| @@ -45,22 +43,19 @@ namespace OpenWifi { | ||||
|             if(LatestOnly) { | ||||
|                 LatestFirmwareCacheEntry    Entry; | ||||
|                 if(!LatestFirmwareCache()->FindLatestFirmware(DeviceType,Entry)) { | ||||
|                     NotFound(); | ||||
|                     return; | ||||
|                     return NotFound(); | ||||
|                 } | ||||
| 
 | ||||
|                 FMSObjects::Firmware    F; | ||||
|                 if(Storage()->GetFirmware(Entry.Id,F)) { | ||||
|                 if(StorageService()->GetFirmware(Entry.Id,F)) { | ||||
|                     Poco::JSON::Object  Answer; | ||||
|                     F.to_json(Answer); | ||||
|                     ReturnObject(Answer); | ||||
|                     return; | ||||
|                     return ReturnObject(Answer); | ||||
|                 } | ||||
|                 NotFound(); | ||||
|                 return; | ||||
|                 return NotFound(); | ||||
|             } else { | ||||
|                 std::vector<FMSObjects::Firmware> List; | ||||
|                 if (Storage()->GetFirmwares(QB_.Offset, QB_.Limit, DeviceType, List)) { | ||||
|                 if (StorageService()->GetFirmwares(QB_.Offset, QB_.Limit, DeviceType, List)) { | ||||
|                     Poco::JSON::Array ObjectArray; | ||||
|                     for (const auto &i:List) { | ||||
|                         if(IdOnly) { | ||||
| @@ -73,11 +68,9 @@ namespace OpenWifi { | ||||
|                     } | ||||
|                     Poco::JSON::Object RetObj; | ||||
|                     RetObj.set(RESTAPI::Protocol::FIRMWARES, ObjectArray); | ||||
|                     ReturnObject(RetObj); | ||||
|                     return; | ||||
|                     return ReturnObject(RetObj); | ||||
|                 } else { | ||||
|                     NotFound(); | ||||
|                     return; | ||||
|                     return NotFound(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -85,7 +78,7 @@ namespace OpenWifi { | ||||
|         std::vector<FMSObjects::Firmware> List; | ||||
|         Poco::JSON::Array ObjectArray; | ||||
|         Poco::JSON::Object Answer; | ||||
|         if (Storage()->GetFirmwares(QB_.Offset, QB_.Limit, DeviceType, List)) { | ||||
|         if (StorageService()->GetFirmwares(QB_.Offset, QB_.Limit, DeviceType, List)) { | ||||
|             for (const auto &i:List) { | ||||
|                 if(IdOnly) { | ||||
|                     ObjectArray.add(i.id); | ||||
| @@ -5,7 +5,7 @@ | ||||
| #ifndef UCENTRALFWS_RESTAPI_FIRMWARESHANDLER_H | ||||
| #define UCENTRALFWS_RESTAPI_FIRMWARESHANDLER_H | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwaresHandler : public RESTAPIHandler { | ||||
| @@ -2,16 +2,10 @@ | ||||
| // Created by stephane bourque on 2021-07-13.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "RESTAPI_historyHandler.h" | ||||
| 
 | ||||
| //
 | ||||
| // Created by stephane bourque on 2021-05-09.
 | ||||
| //
 | ||||
| 
 | ||||
| #include "RESTAPI_historyHandler.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTAPI_protocol.h" | ||||
| #include "RESTAPI_errors.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     void | ||||
| @@ -19,12 +13,11 @@ namespace OpenWifi { | ||||
|         auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""); | ||||
| 
 | ||||
|         if(SerialNumber.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::MissingSerialNumber); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::MissingSerialNumber); | ||||
|         } | ||||
| 
 | ||||
|         FMSObjects::RevisionHistoryEntryVec H; | ||||
|         if (Storage()->GetHistory(SerialNumber, QB_.Offset, QB_.Limit, H)) { | ||||
|         if (StorageService()->GetHistory(SerialNumber, QB_.Offset, QB_.Limit, H)) { | ||||
|             Poco::JSON::Array A; | ||||
|             for (auto const &i:H) { | ||||
|                 Poco::JSON::Object O; | ||||
| @@ -33,8 +26,7 @@ namespace OpenWifi { | ||||
|             } | ||||
|             Poco::JSON::Object Answer; | ||||
|             Answer.set(RESTAPI::Protocol::HISTORY, A); | ||||
|             ReturnObject(Answer); | ||||
|             return; | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         NotFound(); | ||||
|     } | ||||
| @@ -43,13 +35,11 @@ namespace OpenWifi { | ||||
|         auto SerialNumber = GetBinding(RESTAPI::Protocol::SERIALNUMBER, ""); | ||||
|         auto Id = GetParameter(RESTAPI::Protocol::ID, ""); | ||||
|         if (SerialNumber.empty() || Id.empty()) { | ||||
|             BadRequest(RESTAPI::Errors::IdOrSerialEmpty); | ||||
|             return; | ||||
|             return BadRequest(RESTAPI::Errors::IdOrSerialEmpty); | ||||
|         } | ||||
| 
 | ||||
|         if (!Storage()->DeleteHistory(SerialNumber, Id)) { | ||||
|             OK(); | ||||
|             return; | ||||
|         if (!StorageService()->DeleteHistory(SerialNumber, Id)) { | ||||
|             return OK(); | ||||
|         } | ||||
|         NotFound(); | ||||
|     } | ||||
| @@ -6,7 +6,7 @@ | ||||
| #define UCENTRALFMS_RESTAPI_HISTORYHANDLER_H | ||||
| 
 | ||||
| 
 | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_historyHandler : public RESTAPIHandler { | ||||
| @@ -1,5 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-09-15. | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_GenericServer.h" | ||||
| @@ -1,78 +0,0 @@ | ||||
| // | ||||
| // 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 | ||||
| @@ -1,79 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-06-29. | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_InternalServer.h" | ||||
|  | ||||
| #include "Poco/URI.h" | ||||
|  | ||||
| #include "RESTAPI_firmwareHandler.h" | ||||
| #include "RESTAPI_firmwaresHandler.h" | ||||
| #include "RESTAPI_system_command.h" | ||||
| #include "RESTAPI_connectedDevicesHandler.h" | ||||
| #include "RESTAPI_connectedDeviceHandler.h" | ||||
|  | ||||
| #include "Utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class RESTAPI_InternalServer *RESTAPI_InternalServer::instance_ = nullptr; | ||||
|  | ||||
|     RESTAPI_InternalServer::RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi") | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     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_firmwaresHandler, | ||||
|                 RESTAPI_firmwareHandler, | ||||
|                 RESTAPI_system_command, | ||||
|                 RESTAPI_connectedDevicesHandler, | ||||
|                 RESTAPI_connectedDeviceHandler | ||||
|         >(Path, Bindings, Logger_, Server_); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| // | ||||
| // 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: | ||||
|         RESTAPI_InternalServer() noexcept; | ||||
|  | ||||
|         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_; | ||||
|     }; | ||||
|  | ||||
|     inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); }; | ||||
|  | ||||
|     class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
|     public: | ||||
|         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 | ||||
| @@ -1,479 +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 <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); | ||||
| 	} | ||||
| } | ||||
| @@ -1,233 +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. | ||||
| // | ||||
|  | ||||
| #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 | ||||
| @@ -1,89 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-05-09. | ||||
| // | ||||
|  | ||||
| #include "Poco/URI.h" | ||||
|  | ||||
| #include "RESTAPI_server.h" | ||||
| #include "Utils.h" | ||||
| #include "RESTAPI_handler.h" | ||||
|  | ||||
| #include "RESTAPI_firmwareHandler.h" | ||||
| #include "RESTAPI_firmwaresHandler.h" | ||||
| #include "RESTAPI_system_command.h" | ||||
| #include "RESTAPI_firmwareAgeHandler.h" | ||||
| #include "RESTAPI_connectedDeviceHandler.h" | ||||
| #include "RESTAPI_connectedDevicesHandler.h" | ||||
| #include "RESTAPI_historyHandler.h" | ||||
| #include "RESTAPI_deviceReportHandler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class RESTAPI_server *RESTAPI_server::instance_ = nullptr; | ||||
|  | ||||
|     RESTAPI_server::RESTAPI_server() noexcept: | ||||
|             SubSystemServer("RESTAPIServer", "RESTAPIServer", "openwifi.restapi") | ||||
|     { | ||||
|     } | ||||
|  | ||||
|     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 RequestHandlerFactory(Server_), Pool_, Sock, Params); | ||||
|             NewServer->start(); | ||||
|             RESTServers_.push_back(std::move(NewServer)); | ||||
|         } | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     Poco::Net::HTTPRequestHandler *RequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) { | ||||
|         Poco::URI uri(Request.getURI()); | ||||
|         auto *Path = uri.getPath().c_str(); | ||||
|         RESTAPIHandler::BindingMap Bindings; | ||||
|  | ||||
|         // std::cout << "Path: " << Request.getURI() << std::endl; | ||||
|  | ||||
|         return  RESTAPI_Router< | ||||
|                 RESTAPI_firmwaresHandler, | ||||
|                 RESTAPI_firmwareHandler, | ||||
|                 RESTAPI_system_command, | ||||
|                 RESTAPI_firmwareAgeHandler, | ||||
|                 RESTAPI_connectedDevicesHandler, | ||||
|                 RESTAPI_connectedDeviceHandler, | ||||
|                 RESTAPI_historyHandler, | ||||
|                 RESTAPI_deviceReportHandler | ||||
|                 >(Path,Bindings,Logger_, Server_); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_server::Stop() { | ||||
|         Logger_.information("Stopping "); | ||||
|         for( const auto & svr : RESTServers_ ) | ||||
|             svr->stop(); | ||||
|         RESTServers_.clear(); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_server::reinitialize(Poco::Util::Application &self) { | ||||
|         Daemon()->LoadConfigurationFile(); | ||||
|         Logger_.information("Reinitializing."); | ||||
|         Stop(); | ||||
|         Start(); | ||||
|     } | ||||
|  | ||||
| }  // namespace | ||||
| @@ -1,59 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-05-09. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFWS_RESTAPI_SERVER_H | ||||
| #define UCENTRALFWS_RESTAPI_SERVER_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 "SubSystemServer.h" | ||||
| #include "RESTAPI_GenericServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class RESTAPI_server : public SubSystemServer { | ||||
|  | ||||
|     public: | ||||
|         RESTAPI_server() noexcept; | ||||
|  | ||||
|         static RESTAPI_server *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new RESTAPI_server; | ||||
|             } | ||||
|             return instance_; | ||||
|         } | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         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_; | ||||
|     }; | ||||
|  | ||||
|     inline RESTAPI_server * RESTAPI_server() { return RESTAPI_server::instance(); }; | ||||
|  | ||||
|     class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
|         public: | ||||
|             RequestHandlerFactory(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_; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #endif //UCENTRALFWS_RESTAPI_SERVER_H | ||||
| @@ -1,146 +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 "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); | ||||
| 	} | ||||
| } | ||||
| @@ -1,32 +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. | ||||
| // | ||||
|  | ||||
| #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 | ||||
| @@ -1,17 +0,0 @@ | ||||
| // | ||||
| // 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); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -1,216 +0,0 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-07-05. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALGW_RESTAPI_UTILS_H | ||||
| #define UCENTRALGW_RESTAPI_UTILS_H | ||||
| #include <functional> | ||||
|  | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| namespace OpenWifi::RESTAPI_utils { | ||||
|  | ||||
| 	void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr); | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, bool V) { | ||||
| 		Obj.set(Field,V); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::string & S) { | ||||
| 		Obj.set(Field,S); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const char * S) { | ||||
| 		Obj.set(Field,S); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint64_t V) { | ||||
| 		Obj.set(Field,V); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringVec &V) { | ||||
| 		Poco::JSON::Array	A; | ||||
| 		for(const auto &i:V) | ||||
| 			A.add(i); | ||||
| 		Obj.set(Field,A); | ||||
| 	} | ||||
|  | ||||
|     inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::CountedMap &M) { | ||||
|         Poco::JSON::Array	A; | ||||
|         for(const auto &[Key,Value]:M) { | ||||
|             Poco::JSON::Object  O; | ||||
|             O.set("tag",Key); | ||||
|             O.set("value", Value); | ||||
|             A.add(O); | ||||
|         } | ||||
|         Obj.set(Field,A); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     template<typename T> void field_to_json(Poco::JSON::Object &Obj, | ||||
| 					   						const char *Field, | ||||
| 					   						const T &V, | ||||
| 											std::function<std::string(const T &)> F) { | ||||
| 		Obj.set(Field, F(V)); | ||||
| 	} | ||||
|  | ||||
| 	template<typename T> bool field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, T & V, | ||||
| 											std::function<T(const std::string &)> F) { | ||||
| 		if(Obj->has(Field)) | ||||
| 			V = F(Obj->get(Field).toString()); | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, std::string &S) { | ||||
| 		if(Obj->has(Field)) | ||||
| 			S = Obj->get(Field).toString(); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, uint64_t &V) { | ||||
| 		if(Obj->has(Field)) | ||||
| 			V = Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, bool &V) { | ||||
| 		if(Obj->has(Field)) | ||||
| 			V = (Obj->get(Field).toString() == "true"); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringVec &V) { | ||||
| 		if(Obj->isArray(Field)) { | ||||
| 			V.clear(); | ||||
| 			Poco::JSON::Array::Ptr A = Obj->getArray(Field); | ||||
| 			for(const auto &i:*A) { | ||||
| 				V.push_back(i.toString()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	template<class T> void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector<T> &Value) { | ||||
| 		Poco::JSON::Array Arr; | ||||
| 		for(const auto &i:Value) { | ||||
| 			Poco::JSON::Object	AO; | ||||
| 			i.to_json(AO); | ||||
| 			Arr.add(AO); | ||||
| 		} | ||||
| 		Obj.set(Field, Arr); | ||||
| 	} | ||||
|  | ||||
| 	template<class T> void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector<T> &Value) { | ||||
| 		if(Obj->isArray(Field)) { | ||||
| 			Poco::JSON::Array::Ptr	Arr = Obj->getArray(Field); | ||||
| 			for(auto &i:*Arr) { | ||||
| 				auto InnerObj = i.extract<Poco::JSON::Object::Ptr>(); | ||||
| 				T	NewItem; | ||||
| 				NewItem.from_json(InnerObj); | ||||
| 				Value.push_back(NewItem); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	template<class T> void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T &Value) { | ||||
| 		if(Obj->isObject(Field)) { | ||||
| 			Poco::JSON::Object::Ptr	A = Obj->getObject(Field); | ||||
| 			Value.from_json(A); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline std::string to_string(const Types::StringVec & ObjectArray) { | ||||
| 		Poco::JSON::Array OutputArr; | ||||
| 		if(ObjectArray.empty()) | ||||
| 			return "[]"; | ||||
| 		for(auto const &i:ObjectArray) { | ||||
| 			OutputArr.add(i); | ||||
| 		} | ||||
| 		std::ostringstream OS; | ||||
| 		Poco::JSON::Stringifier::condense(OutputArr,OS); | ||||
| 		return OS.str(); | ||||
| 	} | ||||
|  | ||||
| 	template<class T> std::string to_string(const std::vector<T> & ObjectArray) { | ||||
| 		Poco::JSON::Array OutputArr; | ||||
| 		if(ObjectArray.empty()) | ||||
| 			return "[]"; | ||||
| 		for(auto const &i:ObjectArray) { | ||||
| 			Poco::JSON::Object O; | ||||
| 			i.to_json(O); | ||||
| 			OutputArr.add(O); | ||||
| 		} | ||||
| 		std::ostringstream OS; | ||||
| 		Poco::JSON::Stringifier::condense(OutputArr,OS); | ||||
| 		return OS.str(); | ||||
| 	} | ||||
|  | ||||
| 	template<class T> std::string to_string(const T & Object) { | ||||
| 		Poco::JSON::Object OutputObj; | ||||
| 		Object.to_json(OutputObj); | ||||
| 		std::ostringstream OS; | ||||
| 		Poco::JSON::Stringifier::condense(OutputObj,OS); | ||||
| 		return OS.str(); | ||||
| 	} | ||||
|  | ||||
|     inline Types::StringVec to_object_array(const std::string & ObjectString) { | ||||
|  | ||||
|         Types::StringVec 	Result; | ||||
|         if(ObjectString.empty()) | ||||
|             return Result; | ||||
|  | ||||
|         try { | ||||
|             Poco::JSON::Parser P; | ||||
|             auto Object = P.parse(ObjectString).template extract<Poco::JSON::Array::Ptr>(); | ||||
|             for (auto const i : *Object) { | ||||
|                 Result.push_back(i.toString()); | ||||
|             } | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|         return Result; | ||||
|     } | ||||
|  | ||||
|     template<class T> std::vector<T> to_object_array(const std::string & ObjectString) { | ||||
|  | ||||
| 		std::vector<T>	Result; | ||||
| 		if(ObjectString.empty()) | ||||
| 			return Result; | ||||
|  | ||||
| 		try { | ||||
| 			Poco::JSON::Parser P; | ||||
| 			auto Object = P.parse(ObjectString).template extract<Poco::JSON::Array::Ptr>(); | ||||
| 			for (auto const i : *Object) { | ||||
| 				auto InnerObject = i.template extract<Poco::JSON::Object::Ptr>(); | ||||
| 				T Obj; | ||||
| 				Obj.from_json(InnerObject); | ||||
| 				Result.push_back(Obj); | ||||
| 			} | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| 	template<class T> T to_object(const std::string & ObjectString) { | ||||
| 		T	Result; | ||||
|  | ||||
| 		if(ObjectString.empty()) | ||||
| 			return Result; | ||||
|  | ||||
| 		Poco::JSON::Parser	P; | ||||
| 		auto Object = P.parse(ObjectString).template extract<Poco::JSON::Object::Ptr>(); | ||||
| 		Result.from_json(Object); | ||||
|  | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
|     template<class T> bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { | ||||
|         Poco::JSON::Parser IncomingParser; | ||||
|         auto RawObject = IncomingParser.parse(Request.stream()).extract<Poco::JSON::Object::Ptr>(); | ||||
|         Obj.from_json(RawObject); | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif // UCENTRALGW_RESTAPI_UTILS_H | ||||
| @@ -3,7 +3,7 @@ | ||||
| //
 | ||||
| 
 | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "RESTAPI_utils.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -9,7 +9,7 @@ | ||||
| 
 | ||||
| 
 | ||||
| #include "RESTAPI_SecurityObjects.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| 
 | ||||
| namespace OpenWifi::FMSObjects { | ||||
| 
 | ||||
| @@ -15,9 +15,7 @@ | ||||
| #endif | ||||
| 
 | ||||
| #include "RESTAPI_GWobjects.h" | ||||
| #include "RESTAPI_handler.h" | ||||
| #include "RESTAPI_utils.h" | ||||
| #include "Utils.h" | ||||
| #include "framework/MicroService.h" | ||||
| 
 | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -28,7 +26,7 @@ namespace OpenWifi::GWObjects { | ||||
| 	void Device::to_json(Poco::JSON::Object &Obj) const { | ||||
| 		field_to_json(Obj,"serialNumber", SerialNumber); | ||||
| #ifdef TIP_GATEWAY_SERVICE | ||||
| 		field_to_json(Obj,"deviceType", uCentral::Daemon::instance()->IdentifyDevice(Compatible)); | ||||
| 		field_to_json(Obj,"deviceType", Daemon::instance()->IdentifyDevice(Compatible)); | ||||
| #endif | ||||
| 		field_to_json(Obj,"macAddress", MACAddress); | ||||
| 		field_to_json(Obj,"manufacturer", Manufacturer); | ||||
| @@ -70,7 +68,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); | ||||
| @@ -149,7 +147,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); | ||||
| @@ -162,10 +160,22 @@ 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); | ||||
| 		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; | ||||
| 	} | ||||
| 
 | ||||
| 	void ConnectionState::to_json(Poco::JSON::Object &Obj) const { | ||||
| @@ -243,5 +253,11 @@ 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); | ||||
| 	}; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| @@ -59,11 +59,12 @@ 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; | ||||
| 	}; | ||||
| 
 | ||||
| 	struct Statistics { | ||||
| 		std::string SerialNumber; | ||||
| 		uint64_t 	UUID; | ||||
| 		std::string Data; | ||||
| 		uint64_t 	Recorded; | ||||
| @@ -71,6 +72,7 @@ namespace OpenWifi::GWObjects { | ||||
| 	}; | ||||
| 
 | ||||
| 	struct HealthCheck { | ||||
| 		std::string SerialNumber; | ||||
| 		uint64_t 	UUID; | ||||
| 		std::string Data; | ||||
| 		uint64_t 	Recorded; | ||||
| @@ -96,6 +98,7 @@ namespace OpenWifi::GWObjects { | ||||
| 			LOG_INFO = 6,	 /* informational */ | ||||
| 			LOG_DEBUG = 7	 /* debug-level messages */ | ||||
| 		}; | ||||
| 		std::string SerialNumber; | ||||
| 		std::string Log; | ||||
| 		std::string Data; | ||||
| 		uint64_t 	Severity; | ||||
| @@ -113,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 { | ||||
| @@ -139,11 +142,12 @@ 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 { | ||||
| @@ -179,6 +183,13 @@ 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
 | ||||
							
								
								
									
										569
									
								
								src/RESTObjects/RESTAPI_ProvObjects.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										569
									
								
								src/RESTObjects/RESTAPI_ProvObjects.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,569 @@ | ||||
| // | ||||
| //	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_ProvObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
|  | ||||
| namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void ObjectInfo::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"id",id); | ||||
|         field_to_json(Obj,"name",name); | ||||
|         field_to_json(Obj,"description",description); | ||||
|         field_to_json(Obj,"created",created); | ||||
|         field_to_json(Obj,"modified",modified); | ||||
|         field_to_json(Obj,"notes",notes); | ||||
|         field_to_json(Obj,"tags",tags); | ||||
|     } | ||||
|  | ||||
|     bool ObjectInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"id",id); | ||||
|             field_from_json(Obj,"name",name); | ||||
|             field_from_json(Obj,"description",description); | ||||
|             field_from_json(Obj,"created",created); | ||||
|             field_from_json(Obj,"modified",modified); | ||||
|             field_from_json(Obj,"notes",notes); | ||||
|             field_from_json(Obj,"tags",tags); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ManagementPolicyEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"users",users); | ||||
|         field_to_json( Obj,"resources",resources); | ||||
|         field_to_json( Obj,"access",access); | ||||
|         field_to_json( Obj,"policy",policy); | ||||
|     } | ||||
|  | ||||
|     bool ManagementPolicyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"users",users); | ||||
|             field_from_json( Obj,"resources",resources); | ||||
|             field_from_json( Obj,"access",access); | ||||
|             field_from_json( Obj,"policy",policy); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ManagementPolicy::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|         field_to_json(Obj, "inUse", inUse); | ||||
|         field_to_json(Obj, "entity", entity); | ||||
|     } | ||||
|  | ||||
|     bool ManagementPolicy::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json(Obj, "entries", entries); | ||||
|             field_from_json(Obj, "inUse", inUse); | ||||
|             field_from_json(Obj, "entity", entity); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Entity::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"venues",venues); | ||||
|         field_to_json( Obj,"children",children); | ||||
|         field_to_json( Obj,"contacts",contacts); | ||||
|         field_to_json( Obj,"locations",locations); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"devices",devices); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"sourceIP",sourceIP); | ||||
|     } | ||||
|  | ||||
|     bool Entity::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"venues",venues); | ||||
|             field_from_json( Obj,"children",children); | ||||
|             field_from_json( Obj,"contacts",contacts); | ||||
|             field_from_json( Obj,"locations",locations); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"devices",devices); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void DiGraphEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"child",child); | ||||
|     } | ||||
|  | ||||
|     bool DiGraphEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"child",child); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Venue::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"children",children); | ||||
|         field_to_json( Obj,"devices",devices); | ||||
|         field_to_json( Obj,"topology",topology); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"design",design); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"contact",contact); | ||||
|         field_to_json( Obj,"location",location); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"sourceIP",sourceIP); | ||||
|     } | ||||
|  | ||||
|     bool Venue::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"children",children); | ||||
|             field_from_json( Obj,"devices",devices); | ||||
|             field_from_json( Obj,"topology",topology); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"design",design); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"contact",contact); | ||||
|             field_from_json( Obj,"location",location); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void UserInfoDigest::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"id",id); | ||||
|         field_to_json( Obj,"entity",loginId); | ||||
|         field_to_json( Obj,"children",userType); | ||||
|     } | ||||
|  | ||||
|     bool UserInfoDigest::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"id",id); | ||||
|             field_from_json( Obj,"entity",loginId); | ||||
|             field_from_json( Obj,"children",userType); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ManagementRole::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"users",users); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|     } | ||||
|  | ||||
|     bool ManagementRole::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"users",users); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Location::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"type",OpenWifi::ProvObjects::to_string(type)); | ||||
|         field_to_json( Obj,"buildingName",buildingName); | ||||
|         field_to_json( Obj,"addressLines",addressLines); | ||||
|         field_to_json( Obj,"city",city); | ||||
|         field_to_json( Obj,"state",state); | ||||
|         field_to_json( Obj,"postal",postal); | ||||
|         field_to_json( Obj,"country",country); | ||||
|         field_to_json( Obj,"phones",phones); | ||||
|         field_to_json( Obj,"mobiles",mobiles); | ||||
|         field_to_json( Obj,"geoCode",geoCode); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool Location::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             std::string tmp_type; | ||||
|             field_from_json( Obj,"type", tmp_type); | ||||
|             type = location_from_string(tmp_type); | ||||
|             field_from_json( Obj,"buildingName",buildingName); | ||||
|             field_from_json( Obj,"addressLines",addressLines); | ||||
|             field_from_json( Obj,"city",city); | ||||
|             field_from_json( Obj,"state",state); | ||||
|             field_from_json( Obj,"postal",postal); | ||||
|             field_from_json( Obj,"country",country); | ||||
|             field_from_json( Obj,"phones",phones); | ||||
|             field_from_json( Obj,"mobiles",mobiles); | ||||
|             field_from_json( Obj,"geoCode",geoCode); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Contact::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"type", to_string(type)); | ||||
|         field_to_json( Obj,"title",title); | ||||
|         field_to_json( Obj,"salutation",salutation); | ||||
|         field_to_json( Obj,"firstname",firstname); | ||||
|         field_to_json( Obj,"lastname",lastname); | ||||
|         field_to_json( Obj,"initials",initials); | ||||
|         field_to_json( Obj,"visual",visual); | ||||
|         field_to_json( Obj,"mobiles",mobiles); | ||||
|         field_to_json( Obj,"phones",phones); | ||||
|         field_to_json( Obj,"primaryEmail",primaryEmail); | ||||
|         field_to_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|         field_to_json( Obj,"accessPIN",accessPIN); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool Contact::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             std::string tmp_type; | ||||
|             field_from_json( Obj,"type", tmp_type); | ||||
|             type = contact_from_string(tmp_type); | ||||
|             field_from_json( Obj,"title",title); | ||||
|             field_from_json( Obj,"salutation",salutation); | ||||
|             field_from_json( Obj,"firstname",firstname); | ||||
|             field_from_json( Obj,"lastname",lastname); | ||||
|             field_from_json( Obj,"initials",initials); | ||||
|             field_from_json( Obj,"visual",visual); | ||||
|             field_from_json( Obj,"mobiles",mobiles); | ||||
|             field_from_json( Obj,"phones",phones); | ||||
|             field_from_json( Obj,"primaryEmail",primaryEmail); | ||||
|             field_from_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|             field_from_json( Obj,"accessPIN",accessPIN); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void InventoryTag::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json(Obj, "serialNumber", serialNumber); | ||||
|         field_to_json(Obj, "venue", venue); | ||||
|         field_to_json(Obj, "entity", entity); | ||||
|         field_to_json(Obj, "subscriber", subscriber); | ||||
|         field_to_json(Obj, "deviceType", deviceType); | ||||
|         field_to_json(Obj, "qrCode", qrCode); | ||||
|         field_to_json(Obj, "geoCode", geoCode); | ||||
|         field_to_json(Obj, "location", location); | ||||
|         field_to_json(Obj, "contact", contact); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"serialNumber",serialNumber); | ||||
|             field_from_json( Obj,"venue",venue); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"subscriber",subscriber); | ||||
|             field_from_json( Obj,"deviceType",deviceType); | ||||
|             field_from_json(Obj, "qrCode", qrCode); | ||||
|             field_from_json( Obj,"geoCode",geoCode); | ||||
|             field_from_json( Obj,"location",location); | ||||
|             field_from_json( Obj,"contact",contact); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"name", name); | ||||
|         field_to_json( Obj,"description", description); | ||||
|         field_to_json( Obj,"weight", weight); | ||||
|         field_to_json( Obj,"configuration", configuration); | ||||
|     } | ||||
|  | ||||
|     bool DeviceConfigurationElement::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"name",name); | ||||
|             field_from_json( Obj,"description",description); | ||||
|             field_from_json( Obj,"weight",weight); | ||||
|             field_from_json( Obj,"configuration",configuration); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void DeviceConfiguration::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceTypes",deviceTypes); | ||||
|         field_to_json( Obj,"configuration",configuration); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"variables",variables); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|         field_to_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|     } | ||||
|  | ||||
|     bool DeviceConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceTypes",deviceTypes); | ||||
|             field_from_json( Obj,"configuration",configuration); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"variables",variables); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|             field_from_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Report::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "snapshot", snapShot); | ||||
|         field_to_json(Obj, "devices", tenants); | ||||
|     }; | ||||
|  | ||||
|     void Report::reset() { | ||||
|         tenants.clear(); | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "uuid", uuid); | ||||
|         field_to_json(Obj, "name", name); | ||||
|         field_to_json(Obj, "description", description); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"uuid",uuid); | ||||
|             field_from_json( Obj,"name",name); | ||||
|             field_from_json( Obj,"description",description); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "type", type); | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"type",type); | ||||
|             field_from_json( Obj,"entries",entries); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntryMapList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntryMapList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"entries",entries); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void UserList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "list", list); | ||||
|     } | ||||
|  | ||||
|     bool UserList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "list", list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ObjectACL::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "users", users); | ||||
|         field_to_json(Obj, "access", access); | ||||
|     } | ||||
|  | ||||
|     bool ObjectACL::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "users", users); | ||||
|             field_from_json(Obj, "access", access); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     void ObjectACLList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "list", list); | ||||
|     } | ||||
|  | ||||
|     bool ObjectACLList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "list", list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Map::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"data",data); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"creator",creator); | ||||
|         field_to_json( Obj,"visibility",visibility); | ||||
|         field_to_json( Obj,"access",access); | ||||
|     } | ||||
|  | ||||
|     bool Map::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"data",data); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"creator",creator); | ||||
|             field_from_json( Obj,"visibility",visibility); | ||||
|             field_from_json( Obj,"access",access); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void MapList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"list",list); | ||||
|     } | ||||
|  | ||||
|     bool MapList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"list",list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = std::time(nullptr); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|  | ||||
|         if(I.name.empty()) | ||||
|             return false; | ||||
|  | ||||
|        if(O->has("description")) | ||||
|             I.description = O->get("description").toString(); | ||||
|         SecurityObjects::MergeNotes(O,U,I.notes); | ||||
|         SecurityObjects::NoteInfoVec N; | ||||
|         for(auto &i:I.notes) { | ||||
|             if(i.note.empty()) | ||||
|                 continue; | ||||
|             N.push_back(SecurityObjects::NoteInfo{.created=Now,.createdBy=U.email,.note=i.note}); | ||||
|         } | ||||
|         I.modified = Now; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = std::time(nullptr); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|  | ||||
|         if(I.name.empty()) | ||||
|             return false; | ||||
|  | ||||
|         if(O->has("description")) | ||||
|             I.description = O->get("description").toString(); | ||||
|  | ||||
|         SecurityObjects::NoteInfoVec N; | ||||
|         for(auto &i:I.notes) { | ||||
|             if(i.note.empty()) | ||||
|                 continue; | ||||
|             N.push_back(SecurityObjects::NoteInfo{.created=Now,.createdBy=U.email,.note=i.note}); | ||||
|         } | ||||
|         I.notes = N; | ||||
|         I.modified = I.created = Now; | ||||
|         I.id = MicroService::instance().CreateUUID(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
							
								
								
									
										374
									
								
								src/RESTObjects/RESTAPI_ProvObjects.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										374
									
								
								src/RESTObjects/RESTAPI_ProvObjects.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,374 @@ | ||||
| // | ||||
| //	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 OWPROV_RESTAPI_PROVOBJECTS_H | ||||
| #define OWPROV_RESTAPI_PROVOBJECTS_H | ||||
|  | ||||
| #include <string> | ||||
| #include "RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     enum FIRMWARE_UPGRADE_RULES { | ||||
|         dont_upgrade, | ||||
|         upgrade_inherit, | ||||
|         upgrade_release_only, | ||||
|         upgrade_latest | ||||
|     }; | ||||
|  | ||||
|     struct ObjectInfo { | ||||
|         Types::UUID_t   id; | ||||
|         std::string     name; | ||||
|         std::string     description; | ||||
|         SecurityObjects::NoteInfoVec notes; | ||||
|         uint64_t        created=0; | ||||
|         uint64_t        modified=0; | ||||
|         Types::TagList  tags; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ManagementPolicyEntry { | ||||
|         Types::UUIDvec_t users; | ||||
|         Types::UUIDvec_t resources; | ||||
|         Types::StringVec access; | ||||
|         std::string policy; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ManagementPolicy { | ||||
|         ObjectInfo          info; | ||||
|         std::vector<ManagementPolicyEntry>  entries; | ||||
|         Types::StringVec    inUse; | ||||
|         Types::UUID_t       entity; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<ManagementPolicy>      ManagementPolicyVec; | ||||
|  | ||||
|     struct Entity { | ||||
|         ObjectInfo              info; | ||||
|         Types::UUID_t           parent; | ||||
|         Types::UUIDvec_t        children; | ||||
|         Types::UUIDvec_t        venues; | ||||
|         Types::UUIDvec_t        contacts;       // all contacts associated in this entity | ||||
|         Types::UUIDvec_t        locations;      // all locations associated in this entity | ||||
|         Types::UUID_t           managementPolicy; | ||||
|         Types::UUIDvec_t        deviceConfiguration; | ||||
|         Types::UUIDvec_t        devices; | ||||
|         std::string             rrm; | ||||
|         Types::StringVec        sourceIP; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<Entity>      EntityVec; | ||||
|  | ||||
|     struct DiGraphEntry { | ||||
|         Types::UUID_t parent; | ||||
|         Types::UUID_t child; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     typedef std::vector<DiGraphEntry>   DiGraph; | ||||
|  | ||||
|     struct Venue { | ||||
|         ObjectInfo          info; | ||||
|         Types::UUID_t       entity; | ||||
|         Types::UUID_t       parent; | ||||
|         Types::UUIDvec_t    children; | ||||
|         Types::UUID_t       managementPolicy; | ||||
|         Types::UUIDvec_t    devices; | ||||
|         DiGraph             topology; | ||||
|         std::string         design; | ||||
|         Types::UUIDvec_t    deviceConfiguration; | ||||
|         std::string         contact; | ||||
|         std::string         location; | ||||
|         std::string         rrm; | ||||
|         Types::StringVec    sourceIP; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<Venue>      VenueVec; | ||||
|  | ||||
|     struct UserInfoDigest { | ||||
|         std::string     id; | ||||
|         std::string     loginId; | ||||
|         std::string     userType; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ManagementRole { | ||||
|         ObjectInfo          info; | ||||
|         Types::UUID_t       managementPolicy; | ||||
|         Types::UUIDvec_t    users; | ||||
|         Types::StringVec    inUse; | ||||
|         Types::UUID_t       entity; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<ManagementRole>      ManagementRoleVec; | ||||
|  | ||||
|     enum LocationType { | ||||
|         LT_SERVICE, LT_EQUIPMENT, LT_AUTO, LT_MANUAL, | ||||
|         LT_SPECIAL, LT_UNKNOWN, LT_CORPORATE | ||||
|     }; | ||||
|  | ||||
|     inline std::string to_string(LocationType L) { | ||||
|         switch(L) { | ||||
|             case LT_SERVICE: return "SERVICE"; | ||||
|             case LT_EQUIPMENT: return "EQUIPMENT"; | ||||
|             case LT_AUTO: return "AUTO"; | ||||
|             case LT_MANUAL: return "MANUAL"; | ||||
|             case LT_SPECIAL: return "SPECIAL"; | ||||
|             case LT_UNKNOWN: return "UNKNOWN"; | ||||
|             case LT_CORPORATE: return "CORPORATE"; | ||||
|             default: return "UNKNOWN"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     inline LocationType location_from_string(const std::string &S) { | ||||
|         if(!Poco::icompare(S,"SERVICE")) | ||||
|             return LT_SERVICE; | ||||
|         else if(!Poco::icompare(S,"EQUIPMENT")) | ||||
|             return LT_EQUIPMENT; | ||||
|         else if(!Poco::icompare(S,"AUTO")) | ||||
|             return LT_AUTO; | ||||
|         else if(!Poco::icompare(S,"MANUAL")) | ||||
|             return LT_MANUAL; | ||||
|         else if(!Poco::icompare(S,"SPECIAL")) | ||||
|             return LT_SPECIAL; | ||||
|         else if(!Poco::icompare(S,"UNKNOWN")) | ||||
|             return LT_UNKNOWN; | ||||
|         else if(!Poco::icompare(S,"CORPORATE")) | ||||
|             return LT_CORPORATE; | ||||
|         return LT_UNKNOWN; | ||||
|     } | ||||
|  | ||||
|     struct Location { | ||||
|         ObjectInfo          info; | ||||
|         LocationType        type; | ||||
|         std::string         buildingName; | ||||
|         Types::StringVec    addressLines; | ||||
|         std::string         city; | ||||
|         std::string         state; | ||||
|         std::string         postal; | ||||
|         std::string         country; | ||||
|         Types::StringVec    phones; | ||||
|         Types::StringVec    mobiles; | ||||
|         std::string         geoCode; | ||||
|         Types::StringVec    inUse; | ||||
|         Types::UUID_t       entity; | ||||
|         Types::UUID_t       managementPolicy; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<Location>      LocationVec; | ||||
|  | ||||
|     enum ContactType { | ||||
|         CT_SUBSCRIBER, CT_USER, CT_INSTALLER, CT_CSR, CT_MANAGER, | ||||
|         CT_BUSINESSOWNER, CT_TECHNICIAN, CT_CORPORATE, CT_UNKNOWN | ||||
|     }; | ||||
|  | ||||
|     inline std::string to_string(ContactType L) { | ||||
|         switch(L) { | ||||
|             case CT_SUBSCRIBER: return "SUBSCRIBER"; | ||||
|             case CT_USER: return "USER"; | ||||
|             case CT_INSTALLER: return "INSTALLER"; | ||||
|             case CT_CSR: return "CSR"; | ||||
|             case CT_MANAGER: return "MANAGER"; | ||||
|             case CT_BUSINESSOWNER: return "BUSINESSOWNER"; | ||||
|             case CT_TECHNICIAN: return "TECHNICIAN"; | ||||
|             case CT_CORPORATE: return "CORPORATE"; | ||||
|             case CT_UNKNOWN: return "UNKNOWN"; | ||||
|             default: return "UNKNOWN"; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     inline ContactType contact_from_string(const std::string &S) { | ||||
|         if(!Poco::icompare(S,"SUBSCRIBER")) | ||||
|             return CT_SUBSCRIBER; | ||||
|         else if(!Poco::icompare(S,"USER")) | ||||
|             return CT_USER; | ||||
|         else if(!Poco::icompare(S,"INSTALLER")) | ||||
|             return CT_INSTALLER; | ||||
|         else if(!Poco::icompare(S,"CSR")) | ||||
|             return CT_CSR; | ||||
|         else if(!Poco::icompare(S,"BUSINESSOWNER")) | ||||
|             return CT_BUSINESSOWNER; | ||||
|         else if(!Poco::icompare(S,"TECHNICIAN")) | ||||
|             return CT_TECHNICIAN; | ||||
|         else if(!Poco::icompare(S,"CORPORATE")) | ||||
|             return CT_CORPORATE; | ||||
|         else if(!Poco::icompare(S,"UNKNOWN")) | ||||
|             return CT_UNKNOWN; | ||||
|         return CT_UNKNOWN; | ||||
|     } | ||||
|  | ||||
|     struct Contact { | ||||
|         ObjectInfo  info; | ||||
|         ContactType type=CT_USER; | ||||
|         std::string title; | ||||
|         std::string salutation; | ||||
|         std::string firstname; | ||||
|         std::string lastname; | ||||
|         std::string initials; | ||||
|         std::string visual; | ||||
|         Types::StringVec mobiles; | ||||
|         Types::StringVec phones; | ||||
|         std::string primaryEmail; | ||||
|         std::string secondaryEmail; | ||||
|         std::string accessPIN; | ||||
|         Types::StringVec inUse; | ||||
|         Types::UUID_t   entity; | ||||
|         Types::UUID_t   managementPolicy; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<Contact>      ContactVec; | ||||
|  | ||||
|     struct DeviceConfigurationElement { | ||||
|         std::string name; | ||||
|         std::string description; | ||||
|         uint64_t    weight; | ||||
|         std::string configuration; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<DeviceConfigurationElement> DeviceConfigurationElementVec; | ||||
|  | ||||
|     struct DeviceConfiguration { | ||||
|     ObjectInfo                          info; | ||||
|         Types::UUID_t                   managementPolicy; | ||||
|         Types::StringVec                deviceTypes; | ||||
|         DeviceConfigurationElementVec   configuration; | ||||
|         Types::StringVec                inUse; | ||||
|         Types::StringPairVec            variables; | ||||
|         std::string                     rrm; | ||||
|         std::string                     firmwareUpgrade; | ||||
|         bool                            firmwareRCOnly=false; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<DeviceConfiguration>      DeviceConfigurationVec; | ||||
|  | ||||
|     struct InventoryTag { | ||||
|         ObjectInfo      info; | ||||
|         std::string     serialNumber; | ||||
|         std::string     venue; | ||||
|         std::string     entity; | ||||
|         std::string     subscriber; | ||||
|         std::string     deviceType; | ||||
|         std::string     qrCode; | ||||
|         std::string     geoCode; | ||||
|         std::string     location; | ||||
|         std::string     contact; | ||||
|         std::string     deviceConfiguration; | ||||
|         std::string     rrm; | ||||
|         Types::UUID_t   managementPolicy; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|     typedef std::vector<InventoryTag>      InventoryTagVec; | ||||
|  | ||||
|     struct Report { | ||||
|         uint64_t            snapShot=0; | ||||
|         Types::CountedMap   tenants; | ||||
|  | ||||
|         void        reset(); | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|     }; | ||||
|  | ||||
|     struct ExpandedUseEntry { | ||||
|         std::string uuid; | ||||
|         std::string name; | ||||
|         std::string description; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ExpandedUseEntryList { | ||||
|         std::string                     type; | ||||
|         std::vector<ExpandedUseEntry>   entries; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ExpandedUseEntryMapList { | ||||
|         std::vector<ExpandedUseEntryList>    entries; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct UserList { | ||||
|         std::vector<std::string>    list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ObjectACL { | ||||
|         UserList        users; | ||||
|         std::string     access; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ObjectACLList { | ||||
|         std::vector<ObjectACL>  list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct Map { | ||||
|         ObjectInfo          info; | ||||
|         std::string         data; | ||||
|         std::string         entity; | ||||
|         std::string         creator; | ||||
|         std::string         visibility; | ||||
|         ObjectACLList       access; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct MapList { | ||||
|         std::vector<Map>    list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||
|     bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif //OWPROV_RESTAPI_PROVOBJECTS_H | ||||
| @@ -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,21 +58,28 @@ namespace OpenWifi::SecurityObjects { | ||||
|             return CSR; | ||||
|         else if (!Poco::icompare(U, "system")) | ||||
|             return SYSTEM; | ||||
|         else if (!Poco::icompare(U, "special")) | ||||
|             return SPECIAL; | ||||
|         else if (!Poco::icompare(U, "installer")) | ||||
|             return INSTALLER; | ||||
|         else if (!Poco::icompare(U, "noc")) | ||||
|             return NOC; | ||||
|         else if (!Poco::icompare(U, "accounting")) | ||||
|             return ACCOUNTING; | ||||
|         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 SPECIAL: return "special"; | ||||
|             case ADMIN: return "admin"; | ||||
|             default: return "unknown"; | ||||
|             case INSTALLER: return "installer"; | ||||
|             case NOC: return "noc"; | ||||
|             case ACCOUNTING: return "accounting"; | ||||
|             case UNKNOWN: | ||||
|             default: | ||||
|                 return "unknown"; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @@ -125,6 +132,94 @@ 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); | ||||
| @@ -292,40 +387,53 @@ 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 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) { | ||||
| 	    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); | ||||
| 	            } | ||||
| 	        } | ||||
| 	        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(...) { | ||||
| 
 | ||||
| 		} | ||||
| @@ -341,7 +449,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); | ||||
| @@ -349,6 +457,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 			field_from_json(Obj,"policy",policy); | ||||
| 			field_from_json(Obj,"role",role); | ||||
| 			field_from_json(Obj,"notes",notes); | ||||
| 			return true; | ||||
| 		} catch(...) { | ||||
| 
 | ||||
| 		} | ||||
| @@ -359,9 +468,47 @@ 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(...) { | ||||
| 
 | ||||
| 	    } | ||||
| @@ -10,7 +10,7 @@ | ||||
| #define UCENTRAL_RESTAPI_SECURITYOBJECTS_H | ||||
| 
 | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "OpenWifiTypes.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| 
 | ||||
| namespace OpenWifi::SecurityObjects { | ||||
| 
 | ||||
| @@ -42,7 +42,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	}; | ||||
| 
 | ||||
|     enum USER_ROLE { | ||||
|         UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, SPECIAL | ||||
|         UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, INSTALLER, NOC, ACCOUNTING | ||||
|     }; | ||||
| 
 | ||||
|     USER_ROLE UserTypeFromString(const std::string &U); | ||||
| @@ -53,10 +53,53 @@ 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; | ||||
| @@ -81,7 +124,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		bool suspended = false; | ||||
| 		bool blackListed = false; | ||||
|         USER_ROLE userRole; | ||||
| 		std::string userTypeProprietaryInfo; | ||||
|         UserLoginLoginExtensions userTypeProprietaryInfo; | ||||
| 		std::string securityPolicy; | ||||
| 		uint64_t securityPolicyChange = 0 ; | ||||
| 		std::string currentPassword; | ||||
| @@ -94,7 +137,9 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	}; | ||||
| 	typedef std::vector<UserInfo>   UserInfoVec; | ||||
| 
 | ||||
| 	bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes); | ||||
| 	// 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); | ||||
| 
 | ||||
| 	struct InternalServiceInfo { | ||||
| 		std::string privateURI; | ||||
| @@ -155,7 +200,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; | ||||
| 
 | ||||
| @@ -167,14 +212,37 @@ 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); | ||||
| 		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); | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										39
									
								
								src/SDK/GW_SDK.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/SDK/GW_SDK.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-05. | ||||
| // | ||||
|  | ||||
| #include "GW_SDK.h" | ||||
| #include "Daemon.h" | ||||
| #include "Poco/Net/HTTPResponse.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::GW { | ||||
|  | ||||
|     bool SendFirmwareUpgradeCommand( const std::string & serialNumber, const std::string & URI, uint64_t When  ) { | ||||
|         Types::StringPairVec    QueryData; | ||||
|         Poco::JSON::Object      Body; | ||||
|  | ||||
|         Body.set("serialNumber", serialNumber); | ||||
|         Body.set("uri", URI); | ||||
|         Body.set("when",0); | ||||
|  | ||||
|         OpenWifi::OpenAPIRequestPost R(OpenWifi::uSERVICE_GATEWAY, | ||||
|                                        "/api/v1/device/" + serialNumber + "/upgrade" , | ||||
|                                       QueryData, | ||||
|                                       Body, | ||||
|                                       10000); | ||||
|         Poco::JSON::Object::Ptr Response; | ||||
|         if(R.Do(Response) == Poco::Net::HTTPResponse::HTTP_OK) { | ||||
|             std::ostringstream os; | ||||
|             Poco::JSON::Stringifier::stringify(Response,os); | ||||
|             std::cout << "FirmwareUpgradeCommand - good - response: " << os.str() << std::endl; | ||||
|             return true; | ||||
|         } else { | ||||
|             std::ostringstream os; | ||||
|             Poco::JSON::Stringifier::stringify(Response,os); | ||||
|             std::cout << "FirmwareUpgradeCommand - bad - response: " << os.str() << std::endl; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/SDK/GW_SDK.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/SDK/GW_SDK.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-05. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_GW_SDK_H | ||||
| #define OWFMS_GW_SDK_H | ||||
|  | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::GW { | ||||
|  | ||||
|     bool SendFirmwareUpgradeCommand( const std::string & serialNumber, const std::string & URI, uint64_t When = 0 ); | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif //OWFMS_GW_SDK_H | ||||
							
								
								
									
										42
									
								
								src/SDK/Prov_SDK.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/SDK/Prov_SDK.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::Prov { | ||||
|     bool GetFirmwareOptions( const std::string & serialNumber, std::string &firmwareUpgrade, | ||||
|                              bool &firmwareRCOnly) { | ||||
|  | ||||
|         Types::StringPairVec    QueryData; | ||||
|         QueryData.push_back(std::make_pair("firmwareOptions","true")); | ||||
|  | ||||
|         OpenWifi::OpenAPIRequestGet R(  OpenWifi::uSERVICE_PROVISIONING, | ||||
|                                         "/api/v1/inventory/" +serialNumber, | ||||
|                                       QueryData, | ||||
|                                       10000); | ||||
|         firmwareUpgrade="no"; | ||||
|         firmwareRCOnly=false; | ||||
|         Poco::JSON::Object::Ptr Response; | ||||
|         if(R.Do(Response) == Poco::Net::HTTPResponse::HTTP_OK) { | ||||
|             std::cout << "Received options... " << std::endl; | ||||
|             std::ostringstream os; | ||||
|             Poco::JSON::Stringifier::stringify(Response,os); | ||||
|             std::cout << "Firmware option response - good - Response: " << os.str() << std::endl; | ||||
|  | ||||
|             if(Response->has("firmwareUpgrade")) | ||||
|                 firmwareUpgrade = Response->get("firmwareUpgrade").toString(); | ||||
|             if(Response->has("firmwareRCOnly")) | ||||
|                 firmwareRCOnly = Response->get("firmwareRCOnly").toString()=="true"; | ||||
|             return true; | ||||
|         } else { | ||||
|             std::cout << "Failed Received options... " << std::endl; | ||||
|             std::ostringstream os; | ||||
|             Poco::JSON::Stringifier::stringify(Response,os); | ||||
|             std::cout << "Firmware option response - bad- Response: " << os.str() << std::endl; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/SDK/Prov_SDK.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/SDK/Prov_SDK.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_PROV_SDK_H | ||||
| #define OWFMS_PROV_SDK_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::Prov { | ||||
|  | ||||
|     bool GetFirmwareOptions( const std::string & serialNumber, std::string &firmwareUpgrade, | ||||
|                              bool &firmwareRCOnly); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif //OWFMS_PROV_SDK_H | ||||
| @@ -6,54 +6,16 @@ | ||||
| //	Arilia Wireless Inc. | ||||
| // | ||||
|  | ||||
| #include <fstream> | ||||
| #include "StorageService.h" | ||||
| #include "Poco/Util/Application.h" | ||||
|  | ||||
| #include "Daemon.h" | ||||
|  | ||||
| #include "Utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class Storage *Storage::instance_ = nullptr; | ||||
|  | ||||
| 	std::string Storage::ConvertParams(const std::string & S) const { | ||||
| 		std::string R; | ||||
|  | ||||
| 		R.reserve(S.size()*2+1); | ||||
|  | ||||
| 		if(false) { | ||||
| 			auto Idx=1; | ||||
| 			for(auto const & i:S) | ||||
| 			{ | ||||
| 				if(i=='?') { | ||||
| 					R += '$'; | ||||
| 					R.append(std::to_string(Idx++)); | ||||
| 				} else { | ||||
| 					R += i; | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			R = S; | ||||
| 		} | ||||
| 		return R; | ||||
| 	} | ||||
|  | ||||
|     int Storage::Start() { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
|         Logger_.setLevel(Poco::Message::PRIO_NOTICE); | ||||
|         Logger_.notice("Starting."); | ||||
|         std::string DBType = Daemon()->ConfigGetString("storage.type"); | ||||
|  | ||||
|         if (DBType == "sqlite") { | ||||
|             Setup_SQLite(); | ||||
|         } else if (DBType == "postgresql") { | ||||
|             Setup_PostgreSQL(); | ||||
|         } else if (DBType == "mysql") { | ||||
|             Setup_MySQL(); | ||||
|         } | ||||
|         StorageClass::Start(); | ||||
|  | ||||
|         Create_Tables(); | ||||
|  | ||||
| @@ -63,6 +25,7 @@ namespace OpenWifi { | ||||
|     void Storage::Stop() { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|         Logger_.notice("Stopping."); | ||||
|         StorageClass::Stop(); | ||||
|     } | ||||
|  | ||||
|     std::string Storage::TrimRevision(const std::string &R) { | ||||
|   | ||||
| @@ -9,35 +9,20 @@ | ||||
| #ifndef UCENTRAL_USTORAGESERVICE_H | ||||
| #define UCENTRAL_USTORAGESERVICE_H | ||||
|  | ||||
| #include "Poco/Data/Session.h" | ||||
| #include "Poco/Data/SessionPool.h" | ||||
| #include "Poco/Data/SQLite/Connector.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/StorageClass.h" | ||||
|  | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "SubSystemServer.h" | ||||
|  | ||||
| #include "storage_firmwares.h" | ||||
| #include "storage_history.h" | ||||
| #include "storage_deviceTypes.h" | ||||
| #include "storage_deviceInfo.h" | ||||
|  | ||||
| #ifndef SMALL_BUILD | ||||
| #include "Poco/Data/PostgreSQL/Connector.h" | ||||
| #include "Poco/Data/MySQL/Connector.h" | ||||
| #endif | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "storage/storage_firmwares.h" | ||||
| #include "storage/storage_history.h" | ||||
| #include "storage/storage_deviceTypes.h" | ||||
| #include "storage/storage_deviceInfo.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class Storage : public SubSystemServer { | ||||
|     class Storage : public StorageClass { | ||||
|     public: | ||||
|  | ||||
|         enum StorageType { | ||||
|             sqlite, | ||||
|             pgsql, | ||||
|             mysql | ||||
|         }; | ||||
|  | ||||
|         int Create_Tables(); | ||||
|         int Create_Firmwares(); | ||||
|         int Create_History(); | ||||
| @@ -63,20 +48,7 @@ namespace OpenWifi { | ||||
|         int 	Start() override; | ||||
|         void 	Stop() override; | ||||
|  | ||||
|         [[nodiscard]] std::string ConvertParams(const std::string &S) const; | ||||
|         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||
|             if(dbType_==sqlite) { | ||||
|                 return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; | ||||
|             } else if(dbType_==pgsql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|             } else if(dbType_==mysql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|             } | ||||
|             return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|         } | ||||
|  | ||||
|         bool SetDeviceRevision(std::string &SerialNumber, std::string & Revision, std::string & DeviceType, std::string &EndPoint); | ||||
|  | ||||
|         bool AddHistory( std::string & SerialNumber, std::string &DeviceType, std::string & PreviousRevision, std::string & NewVersion); | ||||
|         bool DeleteHistory( std::string & SerialNumber, std::string &Id); | ||||
|  | ||||
| @@ -87,34 +59,15 @@ namespace OpenWifi { | ||||
|         bool GenerateDeviceReport(FMSObjects::DeviceReport &Report); | ||||
|         static std::string TrimRevision(const std::string &R); | ||||
|         static Storage *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new Storage; | ||||
|             } | ||||
|             static Storage *instance_ = new Storage; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
| 	  private: | ||||
| 		static Storage      							    *instance_; | ||||
| 		std::unique_ptr<Poco::Data::SessionPool>            Pool_= nullptr; | ||||
|         StorageType 										dbType_ = sqlite; | ||||
|         std::unique_ptr<Poco::Data::SQLite::Connector>  	SQLiteConn_= nullptr; | ||||
| #ifndef SMALL_BUILD | ||||
|         std::unique_ptr<Poco::Data::PostgreSQL::Connector>  PostgresConn_= nullptr; | ||||
|         std::unique_ptr<Poco::Data::MySQL::Connector>       MySQLConn_= nullptr; | ||||
| #endif | ||||
|  | ||||
|         Storage() noexcept: | ||||
|                 SubSystemServer("Storage", "STORAGE-SVR", "storage") | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         int 	Setup_SQLite(); | ||||
|         int 	Setup_MySQL(); | ||||
|         int 	Setup_PostgreSQL(); | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     inline Storage * Storage() { return Storage::instance(); }; | ||||
|     inline class Storage * StorageService() { return Storage::instance(); }; | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
|   | ||||
| @@ -1,306 +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 "SubSystemServer.h" | ||||
| #include "Daemon.h" | ||||
|  | ||||
| #include "Poco/Net/X509Certificate.h" | ||||
| #include "Poco/DateTimeFormatter.h" | ||||
| #include "Poco/DateTimeFormat.h" | ||||
| #include "Poco/Net/PrivateKeyPassphraseHandler.h" | ||||
| #include "Poco/Net/SSLManager.h" | ||||
|  | ||||
| #include "openssl/ssl.h" | ||||
|  | ||||
| #include "Daemon.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| SubSystemServer::SubSystemServer(std::string Name, const std::string &LoggingPrefix, | ||||
| 								 std::string SubSystemConfigPrefix) | ||||
| 	: Name_(std::move(Name)), Logger_(Poco::Logger::get(LoggingPrefix)), | ||||
| 	  SubSystemConfigPrefix_(std::move(SubSystemConfigPrefix)) { | ||||
| 	Logger_.setLevel(Poco::Message::PRIO_NOTICE); | ||||
| } | ||||
|  | ||||
| void SubSystemServer::initialize(Poco::Util::Application &self) { | ||||
| 	Logger_.notice("Initializing..."); | ||||
| 	auto i = 0; | ||||
| 	bool good = true; | ||||
|  | ||||
| 	ConfigServersList_.clear(); | ||||
| 	while (good) { | ||||
| 		std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; | ||||
|  | ||||
| 		std::string address{root + "address"}; | ||||
| 		if (Daemon()->ConfigGetString(address, "").empty()) { | ||||
| 			good = false; | ||||
| 		} else { | ||||
| 			std::string port{root + "port"}; | ||||
| 			std::string key{root + "key"}; | ||||
| 			std::string key_password{root + "key.password"}; | ||||
| 			std::string cert{root + "cert"}; | ||||
| 			std::string name{root + "name"}; | ||||
| 			std::string backlog{root + "backlog"}; | ||||
| 			std::string rootca{root + "rootca"}; | ||||
| 			std::string issuer{root + "issuer"}; | ||||
| 			std::string clientcas(root + "clientcas"); | ||||
| 			std::string cas{root + "cas"}; | ||||
|  | ||||
| 			std::string level{root + "security"}; | ||||
| 			Poco::Net::Context::VerificationMode M = Poco::Net::Context::VERIFY_RELAXED; | ||||
|  | ||||
| 			auto L = Daemon()->ConfigGetString(level, ""); | ||||
|  | ||||
| 			if (L == "strict") { | ||||
| 				M = Poco::Net::Context::VERIFY_STRICT; | ||||
| 			} else if (L == "none") { | ||||
| 				M = Poco::Net::Context::VERIFY_NONE; | ||||
| 			} else if (L == "relaxed") { | ||||
| 				M = Poco::Net::Context::VERIFY_RELAXED; | ||||
| 			} else if (L == "once") | ||||
| 				M = Poco::Net::Context::VERIFY_ONCE; | ||||
|  | ||||
| 			PropertiesFileServerEntry entry(Daemon()->ConfigGetString(address, ""), | ||||
| 											Daemon()->ConfigGetInt(port, 0), | ||||
| 											Daemon()->ConfigPath(key, ""), | ||||
| 											Daemon()->ConfigPath(cert, ""), | ||||
| 											Daemon()->ConfigPath(rootca, ""), | ||||
| 											Daemon()->ConfigPath(issuer, ""), | ||||
| 											Daemon()->ConfigPath(clientcas, ""), | ||||
| 											Daemon()->ConfigPath(cas, ""), | ||||
| 											Daemon()->ConfigGetString(key_password, ""), | ||||
| 											Daemon()->ConfigGetString(name, ""), M, | ||||
| 											(int)Daemon()->ConfigGetInt(backlog, 64)); | ||||
| 			ConfigServersList_.push_back(entry); | ||||
| 			i++; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void SubSystemServer::uninitialize() { | ||||
| } | ||||
|  | ||||
| void SubSystemServer::reinitialize(Poco::Util::Application &self) { | ||||
| 	Logger_.information("Reloading of this subsystem is not supported."); | ||||
| } | ||||
|  | ||||
| void SubSystemServer::defineOptions(Poco::Util::OptionSet &options) {} | ||||
|  | ||||
| class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { | ||||
|   public: | ||||
| 	explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): | ||||
| 		PrivateKeyPassphraseHandler(true), | ||||
| 		Logger_(Logger), | ||||
| 	 	Password_(Password) {} | ||||
| 		void onPrivateKeyRequested(const void * pSender,std::string & privateKey) { | ||||
| 			Logger_.information("Returning key passphrase."); | ||||
| 			privateKey = Password_; | ||||
| 		}; | ||||
|   private: | ||||
| 	std::string Password_; | ||||
| 	Poco::Logger & Logger_; | ||||
| }; | ||||
|  | ||||
| Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco::Logger &L) const { | ||||
| 	Poco::Net::Context::Params P; | ||||
|  | ||||
| 	P.verificationMode = level_; | ||||
| 	P.verificationDepth = 9; | ||||
| 	P.loadDefaultCAs = root_ca_.empty(); | ||||
| 	P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; | ||||
| 	P.dhUse2048Bits = true; | ||||
| 	P.caLocation = cas_; | ||||
|  | ||||
| 	auto Context = Poco::AutoPtr<Poco::Net::Context>(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P)); | ||||
|  | ||||
| 	if(!key_file_password_.empty()) { | ||||
| 		auto PassphraseHandler = Poco::SharedPtr<MyPrivateKeyPassphraseHandler>( new MyPrivateKeyPassphraseHandler(key_file_password_,L)); | ||||
| 		Poco::Net::SSLManager::instance().initializeServer(PassphraseHandler, nullptr,Context); | ||||
| 	} | ||||
|  | ||||
| 	if (!cert_file_.empty() && !key_file_.empty()) { | ||||
| 		Poco::Crypto::X509Certificate Cert(cert_file_); | ||||
| 		Poco::Crypto::X509Certificate Root(root_ca_); | ||||
|  | ||||
| 		Context->useCertificate(Cert); | ||||
| 		Context->addChainCertificate(Root); | ||||
|  | ||||
| 		Context->addCertificateAuthority(Root); | ||||
|  | ||||
| 		if (level_ == Poco::Net::Context::VERIFY_STRICT) { | ||||
| 			if (issuer_cert_file_.empty()) { | ||||
| 				L.fatal("In strict mode, you must supply ans issuer certificate"); | ||||
| 			} | ||||
| 			if (client_cas_.empty()) { | ||||
| 				L.fatal("In strict mode, client cas must be supplied"); | ||||
| 			} | ||||
| 			Poco::Crypto::X509Certificate Issuing(issuer_cert_file_); | ||||
| 			Context->addChainCertificate(Issuing); | ||||
| 			Context->addCertificateAuthority(Issuing); | ||||
| 		} | ||||
|  | ||||
| 		Poco::Crypto::RSAKey Key("", key_file_, key_file_password_); | ||||
| 		Context->usePrivateKey(Key); | ||||
|  | ||||
| 		SSL_CTX *SSLCtx = Context->sslContext(); | ||||
| 		if (!SSL_CTX_check_private_key(SSLCtx)) { | ||||
| 			L.fatal(Poco::format("Wrong Certificate(%s) for Key(%s)", cert_file_, key_file_)); | ||||
| 		} | ||||
|  | ||||
| 		SSL_CTX_set_verify(SSLCtx, SSL_VERIFY_PEER, nullptr); | ||||
|  | ||||
| 		if (level_ == Poco::Net::Context::VERIFY_STRICT) { | ||||
| 			SSL_CTX_set_client_CA_list(SSLCtx, SSL_load_client_CA_file(client_cas_.c_str())); | ||||
| 		} | ||||
| 		SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT); | ||||
| 		SSL_CTX_dane_enable(SSLCtx); | ||||
|  | ||||
| 		Context->enableSessionCache(); | ||||
| 		Context->setSessionCacheSize(0); | ||||
| 		Context->setSessionTimeout(10); | ||||
| 		Context->enableExtendedCertificateVerification(true); | ||||
| 		Context->disableStatelessSessionResumption(); | ||||
| 	} | ||||
|  | ||||
| 	if (address_ == "*") { | ||||
| 		Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( | ||||
| 			Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 | ||||
| 											  : Poco::Net::AddressFamily::IPv4)); | ||||
| 		Poco::Net::SocketAddress SockAddr(Addr, port_); | ||||
|  | ||||
| 		return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); | ||||
| 	} else { | ||||
| 		Poco::Net::IPAddress Addr(address_); | ||||
| 		Poco::Net::SocketAddress SockAddr(Addr, port_); | ||||
|  | ||||
| 		return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void PropertiesFileServerEntry::LogCertInfo(Poco::Logger &L, | ||||
| 											const Poco::Crypto::X509Certificate &C) { | ||||
|  | ||||
| 	L.information("============================================================================================="); | ||||
| 	L.information(Poco::format(">          Issuer: %s", C.issuerName())); | ||||
| 	L.information("---------------------------------------------------------------------------------------------"); | ||||
| 	L.information(Poco::format(">     Common Name: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); | ||||
| 	L.information(Poco::format(">         Country: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); | ||||
| 	L.information(Poco::format(">        Locality: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); | ||||
| 	L.information(Poco::format(">      State/Prov: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); | ||||
| 	L.information(Poco::format(">        Org name: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); | ||||
| 	L.information( | ||||
| 		Poco::format(">        Org unit: %s", | ||||
| 					 C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); | ||||
| 	L.information( | ||||
| 		Poco::format(">           Email: %s", | ||||
| 					 C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); | ||||
| 	L.information(Poco::format(">         Serial#: %s", | ||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); | ||||
| 	L.information("---------------------------------------------------------------------------------------------"); | ||||
| 	L.information(Poco::format(">         Subject: %s", C.subjectName())); | ||||
| 	L.information("---------------------------------------------------------------------------------------------"); | ||||
| 	L.information(Poco::format(">     Common Name: %s", | ||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); | ||||
| 	L.information(Poco::format(">         Country: %s", | ||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); | ||||
| 	L.information(Poco::format(">        Locality: %s", | ||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); | ||||
| 	L.information( | ||||
| 		Poco::format(">      State/Prov: %s", | ||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); | ||||
| 	L.information( | ||||
| 		Poco::format(">        Org name: %s", | ||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); | ||||
| 	L.information( | ||||
| 		Poco::format(">        Org unit: %s", | ||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); | ||||
| 	L.information( | ||||
| 		Poco::format(">           Email: %s", | ||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); | ||||
| 	L.information(Poco::format(">         Serial#: %s", | ||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); | ||||
| 	L.information("---------------------------------------------------------------------------------------------"); | ||||
| 	L.information(Poco::format(">  Signature Algo: %s", C.signatureAlgorithm())); | ||||
| 	auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); | ||||
| 	L.information(Poco::format(">      Valid from: %s", From)); | ||||
| 	auto Expires = | ||||
| 		Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); | ||||
| 	L.information(Poco::format(">      Expires on: %s", Expires)); | ||||
| 	L.information(Poco::format(">         Version: %d", (int)C.version())); | ||||
| 	L.information(Poco::format(">        Serial #: %s", C.serialNumber())); | ||||
| 	L.information("============================================================================================="); | ||||
| } | ||||
|  | ||||
| void PropertiesFileServerEntry::LogCert(Poco::Logger &L) const { | ||||
| 	try { | ||||
| 		Poco::Crypto::X509Certificate C(cert_file_); | ||||
| 		L.information("============================================================================================="); | ||||
| 		L.information("============================================================================================="); | ||||
| 		L.information(Poco::format("Certificate Filename: %s", cert_file_)); | ||||
| 		LogCertInfo(L, C); | ||||
| 		L.information("============================================================================================="); | ||||
|  | ||||
| 		if (!issuer_cert_file_.empty()) { | ||||
| 			Poco::Crypto::X509Certificate C1(issuer_cert_file_); | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information(Poco::format("Issues Certificate Filename: %s", issuer_cert_file_)); | ||||
| 			LogCertInfo(L, C1); | ||||
| 			L.information("============================================================================================="); | ||||
| 		} | ||||
|  | ||||
| 		if (!client_cas_.empty()) { | ||||
| 			std::vector<Poco::Crypto::X509Certificate> Certs = | ||||
| 				Poco::Net::X509Certificate::readPEM(client_cas_); | ||||
|  | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information(Poco::format("Client CAs Filename: %s", client_cas_)); | ||||
| 			L.information("============================================================================================="); | ||||
| 			auto i = 1; | ||||
| 			for (const auto &C3 : Certs) { | ||||
| 				L.information(Poco::format(" Index: %d", i)); | ||||
| 				L.information("============================================================================================="); | ||||
| 				LogCertInfo(L, C3); | ||||
| 				i++; | ||||
| 			} | ||||
| 			L.information("============================================================================================="); | ||||
| 		} | ||||
|  | ||||
| 	} catch (const Poco::Exception &E) { | ||||
| 		L.log(E); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void PropertiesFileServerEntry::LogCas(Poco::Logger &L) const { | ||||
| 	try { | ||||
| 		std::vector<Poco::Crypto::X509Certificate> Certs = | ||||
| 			Poco::Net::X509Certificate::readPEM(root_ca_); | ||||
|  | ||||
| 		L.information("============================================================================================="); | ||||
| 		L.information("============================================================================================="); | ||||
| 		L.information(Poco::format("CA Filename: %s", root_ca_)); | ||||
| 		L.information("============================================================================================="); | ||||
| 		auto i = 1; | ||||
| 		for (const auto &C : Certs) { | ||||
| 			L.information(Poco::format(" Index: %d", i)); | ||||
| 			L.information("============================================================================================="); | ||||
| 			LogCertInfo(L, C); | ||||
| 			i++; | ||||
| 		} | ||||
| 		L.information("============================================================================================="); | ||||
| 	} catch (const Poco::Exception &E) { | ||||
| 		L.log(E); | ||||
| 	} | ||||
| } | ||||
| } | ||||
| @@ -1,95 +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. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRAL_SUBSYSTEMSERVER_H | ||||
| #define UCENTRAL_SUBSYSTEMSERVER_H | ||||
|  | ||||
| #include <mutex> | ||||
|  | ||||
| #include "Poco/Util/Application.h" | ||||
| #include "Poco/Util/Option.h" | ||||
| #include "Poco/Util/OptionSet.h" | ||||
| #include "Poco/Util/HelpFormatter.h" | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/Net/SecureServerSocket.h" | ||||
|  | ||||
| #include "Poco/Net/X509Certificate.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| class PropertiesFileServerEntry { | ||||
|   public: | ||||
| 	PropertiesFileServerEntry(std::string Address, uint32_t port, std::string Key_file, | ||||
| 							  std::string Cert_file, std::string RootCa, std::string Issuer, | ||||
| 							  std::string ClientCas, std::string Cas, | ||||
| 							  std::string Key_file_password = "", std::string Name = "", | ||||
| 							  Poco::Net::Context::VerificationMode M = | ||||
| 								  Poco::Net::Context::VerificationMode::VERIFY_RELAXED, | ||||
| 							  int backlog = 64) | ||||
| 		: address_(std::move(Address)), port_(port), key_file_(std::move(Key_file)), | ||||
| 		  cert_file_(std::move(Cert_file)), root_ca_(std::move(RootCa)), | ||||
| 		  issuer_cert_file_(std::move(Issuer)), client_cas_(std::move(ClientCas)), | ||||
| 		  cas_(std::move(Cas)), key_file_password_(std::move(Key_file_password)), | ||||
| 		  name_(std::move(Name)), level_(M), backlog_(backlog){}; | ||||
|  | ||||
| 	[[nodiscard]] const std::string &Address() const { return address_; }; | ||||
| 	[[nodiscard]] uint32_t Port() const { return port_; }; | ||||
| 	[[nodiscard]] const std::string &KeyFile() const { return key_file_; }; | ||||
| 	[[nodiscard]] const std::string &CertFile() const { return cert_file_; }; | ||||
| 	[[nodiscard]] const std::string &RootCA() const { return root_ca_; }; | ||||
| 	[[nodiscard]] const std::string &KeyFilePassword() const { return key_file_password_; }; | ||||
| 	[[nodiscard]] const std::string &IssuerCertFile() const { return issuer_cert_file_; }; | ||||
| 	[[nodiscard]] const std::string &Name() const { return name_; }; | ||||
| 	[[nodiscard]] Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const; | ||||
| 	[[nodiscard]] int Backlog() const { return backlog_; } | ||||
| 	void LogCert(Poco::Logger &L) const; | ||||
| 	void LogCas(Poco::Logger &L) const; | ||||
| 	static void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C); | ||||
|  | ||||
|   private: | ||||
| 	std::string address_; | ||||
| 	std::string cert_file_; | ||||
| 	std::string key_file_; | ||||
| 	std::string root_ca_; | ||||
| 	std::string key_file_password_; | ||||
| 	std::string issuer_cert_file_; | ||||
| 	std::string client_cas_; | ||||
| 	std::string cas_; | ||||
| 	uint32_t port_; | ||||
| 	std::string name_; | ||||
| 	int backlog_; | ||||
| 	Poco::Net::Context::VerificationMode level_; | ||||
| }; | ||||
|  | ||||
| class SubSystemServer : public Poco::Util::Application::Subsystem { | ||||
|  | ||||
|   public: | ||||
| 	SubSystemServer(std::string Name, const std::string &LoggingName, std::string SubSystemPrefix); | ||||
| 	void initialize(Poco::Util::Application &self) override; | ||||
| 	void uninitialize() override; | ||||
| 	void reinitialize(Poco::Util::Application &self) override; | ||||
| 	void defineOptions(Poco::Util::OptionSet &options) override; | ||||
| 	inline const std::string & Name() const { return Name_; }; | ||||
| 	const char * name() const override { return Name_.c_str(); } | ||||
|  | ||||
| 	const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; | ||||
| 	uint64_t HostSize() const { return ConfigServersList_.size(); } | ||||
| 	Poco::Logger &Logger() { return Logger_; }; | ||||
| 	void SetLoggingLevel(Poco::Message::Priority NewPriority) { Logger_.setLevel(NewPriority); } | ||||
| 	int GetLoggingLevel() { return Logger_.getLevel(); } | ||||
| 	virtual int Start() = 0; | ||||
| 	virtual void Stop() = 0; | ||||
|  | ||||
|   protected: | ||||
| 	std::recursive_mutex Mutex_; | ||||
| 	Poco::Logger 		&Logger_; | ||||
| 	std::string 		Name_; | ||||
| 	std::vector<PropertiesFileServerEntry> ConfigServersList_; | ||||
| 	std::string 		SubSystemConfigPrefix_; | ||||
| }; | ||||
| } | ||||
| #endif //UCENTRAL_SUBSYSTEMSERVER_H | ||||
							
								
								
									
										491
									
								
								src/Utils.cpp
									
									
									
									
									
								
							
							
						
						
									
										491
									
								
								src/Utils.cpp
									
									
									
									
									
								
							| @@ -1,491 +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 <stdexcept> | ||||
| #include <fstream> | ||||
| #include <cstdlib> | ||||
| #include <regex> | ||||
| #include <random> | ||||
| #include <chrono> | ||||
|  | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include "Poco/Exception.h" | ||||
| #include "Poco/DateTimeFormat.h" | ||||
| #include "Poco/DateTimeFormatter.h" | ||||
| #include "Poco/DateTime.h" | ||||
| #include "Poco/DateTimeParser.h" | ||||
| #include "Poco/StringTokenizer.h" | ||||
| #include "Poco/Message.h" | ||||
| #include "Poco/File.h" | ||||
| #include "Poco/StreamCopier.h" | ||||
| #include "Poco/Path.h" | ||||
|  | ||||
| #include "uCentralProtocol.h" | ||||
| #include "Daemon.h" | ||||
|  | ||||
| namespace OpenWifi::Utils { | ||||
|  | ||||
| 	[[nodiscard]] bool ValidSerialNumber(const std::string &Serial) { | ||||
| 		return ((Serial.size() < uCentralProtocol::SERIAL_NUMBER_LENGTH) && | ||||
| 				std::all_of(Serial.begin(),Serial.end(),[](auto i){return std::isxdigit(i);})); | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter ) { | ||||
| 		std::vector<std::string> ReturnList; | ||||
|  | ||||
| 		unsigned long P=0; | ||||
|  | ||||
| 		while(P<List.size()) | ||||
| 		{ | ||||
| 			unsigned long P2 = List.find_first_of(Delimiter, P); | ||||
| 			if(P2==std::string::npos) { | ||||
| 				ReturnList.push_back(List.substr(P)); | ||||
| 				break; | ||||
| 			} | ||||
| 			else | ||||
| 				ReturnList.push_back(List.substr(P,P2-P)); | ||||
| 			P=P2+1; | ||||
| 		} | ||||
| 		return ReturnList; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string FormatIPv6(const std::string & I ) | ||||
| 	{ | ||||
| 		if(I.substr(0,8) == "[::ffff:") | ||||
| 		{ | ||||
| 			unsigned long PClosingBracket = I.find_first_of(']'); | ||||
|  | ||||
| 			std::string ip = I.substr(8, PClosingBracket-8); | ||||
| 			std::string port = I.substr(PClosingBracket+1); | ||||
| 			return ip + port; | ||||
| 		} | ||||
|  | ||||
| 		return I; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string SerialToMAC(const std::string &Serial) { | ||||
| 		std::string R = Serial; | ||||
|  | ||||
| 		if(R.size()<12) | ||||
| 			padTo(R,12,'0'); | ||||
| 		else if (R.size()>12) | ||||
| 			R = R.substr(0,12); | ||||
|  | ||||
| 		char buf[18]; | ||||
|  | ||||
| 		buf[0] = R[0]; buf[1] = R[1] ; buf[2] = ':' ; | ||||
| 		buf[3] = R[2] ; buf[4] = R[3]; buf[5] = ':' ; | ||||
| 		buf[6] = R[4]; buf[7] = R[5] ; buf[8] = ':' ; | ||||
| 		buf[9] = R[6] ; buf[10]= R[7]; buf[11] = ':'; | ||||
| 		buf[12] = R[8] ; buf[13]= R[9]; buf[14] = ':'; | ||||
| 		buf[15] = R[10] ; buf[16]= R[11];buf[17] = 0; | ||||
|  | ||||
| 		return buf; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string ToHex(const std::vector<unsigned char> & B) { | ||||
| 		std::string R; | ||||
| 		R.reserve(B.size()*2); | ||||
|  | ||||
| 		static const char hex[] = "0123456789abcdef"; | ||||
|  | ||||
| 		for(const auto &i:B) | ||||
| 		{ | ||||
| 			R += (hex[ (i & 0xf0) >> 4]); | ||||
| 			R += (hex[ (i & 0x0f) ]); | ||||
| 		} | ||||
|  | ||||
| 		return R; | ||||
| 	} | ||||
|  | ||||
| 	inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||||
| 	inline static const char kPadCharacter = '='; | ||||
|  | ||||
| 	std::string base64encode(const byte *input, unsigned long size) { | ||||
| 		std::string encoded; | ||||
| 		encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); | ||||
|  | ||||
| 		std::uint32_t temp; | ||||
|  | ||||
| 		std::size_t i; | ||||
|  | ||||
| 		int ee = (int)(size/3); | ||||
|  | ||||
| 		for (i = 0; i < 3*ee; ++i) { | ||||
| 			temp = input[i++] << 16; | ||||
| 			temp += input[i++] << 8; | ||||
| 			temp += input[i]; | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]); | ||||
| 		} | ||||
|  | ||||
| 		switch (size % 3) { | ||||
| 		case 1: | ||||
| 			temp = input[i] << 16; | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); | ||||
| 			encoded.append(2, kPadCharacter); | ||||
| 			break; | ||||
| 		case 2: | ||||
| 			temp = input[i++] << 16; | ||||
| 			temp += input[i] << 8; | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); | ||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); | ||||
| 			encoded.append(1, kPadCharacter); | ||||
| 			break; | ||||
| 		} | ||||
|  | ||||
| 		return encoded; | ||||
| 	} | ||||
|  | ||||
| 	std::vector<byte> base64decode(const std::string& input) | ||||
| 	{ | ||||
| 		if(input.length() % 4) | ||||
| 			throw std::runtime_error("Invalid base64 length!"); | ||||
|  | ||||
| 		std::size_t padding=0; | ||||
|  | ||||
| 		if(input.length()) | ||||
| 		{ | ||||
| 			if(input[input.length() - 1] == kPadCharacter) padding++; | ||||
| 			if(input[input.length() - 2] == kPadCharacter) padding++; | ||||
| 		} | ||||
|  | ||||
| 		std::vector<byte> decoded; | ||||
| 		decoded.reserve(((input.length() / 4) * 3) - padding); | ||||
|  | ||||
| 		std::uint32_t temp=0; | ||||
| 		auto it = input.begin(); | ||||
|  | ||||
| 		while(it < input.end()) | ||||
| 		{ | ||||
| 			for(std::size_t i = 0; i < 4; ++i) | ||||
| 			{ | ||||
| 				temp <<= 6; | ||||
| 				if     (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; | ||||
| 				else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; | ||||
| 				else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; | ||||
| 				else if(*it == 0x2B)                temp |= 0x3E; | ||||
| 				else if(*it == 0x2F)                temp |= 0x3F; | ||||
| 				else if(*it == kPadCharacter) | ||||
| 				{ | ||||
| 					switch(input.end() - it) | ||||
| 					{ | ||||
| 					case 1: | ||||
| 						decoded.push_back((temp >> 16) & 0x000000FF); | ||||
| 						decoded.push_back((temp >> 8 ) & 0x000000FF); | ||||
| 						return decoded; | ||||
| 					case 2: | ||||
| 						decoded.push_back((temp >> 10) & 0x000000FF); | ||||
| 						return decoded; | ||||
| 					default: | ||||
| 						throw std::runtime_error("Invalid padding in base64!"); | ||||
| 					} | ||||
| 				} | ||||
| 				else throw std::runtime_error("Invalid character in base64!"); | ||||
|  | ||||
| 				++it; | ||||
| 			} | ||||
|  | ||||
| 			decoded.push_back((temp >> 16) & 0x000000FF); | ||||
| 			decoded.push_back((temp >> 8 ) & 0x000000FF); | ||||
| 			decoded.push_back((temp      ) & 0x000000FF); | ||||
| 		} | ||||
|  | ||||
| 		return decoded; | ||||
| 	} | ||||
|  | ||||
| 	std::string to_RFC3339(uint64_t t) | ||||
| 	{ | ||||
| 		if(t==0) | ||||
| 			return ""; | ||||
| 		return Poco::DateTimeFormatter::format(Poco::DateTime(Poco::Timestamp::fromEpochTime(t)), Poco::DateTimeFormat::ISO8601_FORMAT); | ||||
| 	} | ||||
|  | ||||
| 	uint64_t from_RFC3339(const std::string &TimeString) | ||||
| 	{ | ||||
| 		if(TimeString.empty() || TimeString=="0") | ||||
| 			return 0; | ||||
|  | ||||
| 		try { | ||||
| 			int             TZ; | ||||
| 			Poco::DateTime  DT = Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FORMAT,TimeString,TZ); | ||||
| 			return DT.timestamp().epochTime(); | ||||
| 		} | ||||
| 		catch( const Poco::Exception & E ) | ||||
| 		{ | ||||
|  | ||||
| 		} | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds) { | ||||
| 		Poco::StringTokenizer	TimeTokens(Time,":",Poco::StringTokenizer::TOK_TRIM); | ||||
|  | ||||
| 		Hours =  Minutes = Hours = 0 ; | ||||
| 		if(TimeTokens.count()==1) { | ||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); | ||||
| 		} else if(TimeTokens.count()==2) { | ||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); | ||||
| 			Minutes = std::atoi(TimeTokens[1].c_str()); | ||||
| 		} else if(TimeTokens.count()==3) { | ||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); | ||||
| 			Minutes = std::atoi(TimeTokens[1].c_str()); | ||||
| 			Seconds = std::atoi(TimeTokens[2].c_str()); | ||||
| 		} else | ||||
| 			return false; | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day) { | ||||
| 		Poco::StringTokenizer	DateTokens(Time,"-",Poco::StringTokenizer::TOK_TRIM); | ||||
|  | ||||
| 		Year =  Month = Day = 0 ; | ||||
| 		if(DateTokens.count()==3) { | ||||
| 			Year 	= std::atoi(DateTokens[0].c_str()); | ||||
| 			Month 	= std::atoi(DateTokens[1].c_str()); | ||||
| 			Day 	= std::atoi(DateTokens[2].c_str()); | ||||
| 		} else | ||||
| 			return false; | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2) { | ||||
| 		if(H1<H2) | ||||
| 			return true; | ||||
| 		if(H1>H2) | ||||
| 			return false; | ||||
| 		if(M1<M2) | ||||
| 			return true; | ||||
| 		if(M2>M1) | ||||
| 			return false; | ||||
| 		if(S1<=S2) | ||||
| 			return true; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	std::string LogLevelToString(int Level) { | ||||
| 		switch(Level) { | ||||
| 			case Poco::Message::PRIO_DEBUG: return "debug"; | ||||
| 			case Poco::Message::PRIO_INFORMATION: return "information"; | ||||
| 			case Poco::Message::PRIO_FATAL: return "fatal"; | ||||
| 			case Poco::Message::PRIO_WARNING: return "warning"; | ||||
| 			case Poco::Message::PRIO_NOTICE: return "notice"; | ||||
| 			case Poco::Message::PRIO_CRITICAL: return "critical"; | ||||
| 			case Poco::Message::PRIO_ERROR: return "error"; | ||||
| 			case Poco::Message::PRIO_TRACE: return "trace"; | ||||
| 			default: return "none"; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits) { | ||||
| 		auto S1_i = SerialNumberToInt(S1); | ||||
| 		auto S2_i = SerialNumberToInt(S2); | ||||
| 		return ((S1_i>>Bits)==(S2_i>>Bits)); | ||||
| 	} | ||||
|  | ||||
| 	uint64_t SerialNumberToInt(const std::string & S) { | ||||
| 		uint64_t R=0; | ||||
|  | ||||
| 		for(const auto &i:S) | ||||
| 			if(i>='0' && i<='9') { | ||||
| 				R <<= 4; | ||||
| 				R += (i-'0'); | ||||
| 			} else if(i>='a' && i<='f') { | ||||
| 				R <<= 4; | ||||
| 				R += (i-'a') + 10 ; | ||||
| 			} else if(i>='A' && i<='F') { | ||||
| 				R <<= 4; | ||||
| 				R += (i-'A') + 10 ; | ||||
| 			} | ||||
| 		return R; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t SerialNumberToOUI(const std::string & S) { | ||||
| 		uint64_t Result = 0 ; | ||||
| 		int Digits=0; | ||||
|  | ||||
| 		for(const auto &i:S) { | ||||
| 			if(std::isxdigit(i)) { | ||||
| 				if(i>='0' && i<='9') { | ||||
| 					Result <<=4; | ||||
| 					Result += i-'0'; | ||||
| 				} else if(i>='A' && i<='F') { | ||||
| 					Result <<=4; | ||||
| 					Result += i-'A'+10; | ||||
| 				} else if(i>='a' && i<='f') { | ||||
| 					Result <<=4; | ||||
| 					Result += i-'a'+10; | ||||
| 				} | ||||
| 				Digits++; | ||||
| 				if(Digits==6) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t GetDefaultMacAsInt64() { | ||||
| 		uint64_t Result=0; | ||||
| 		auto IFaceList = Poco::Net::NetworkInterface::list(); | ||||
|  | ||||
| 		for(const auto &iface:IFaceList) { | ||||
| 			if(iface.isRunning() && !iface.isLoopback()) { | ||||
| 				auto MAC = iface.macAddress(); | ||||
| 				for (auto const &i : MAC) { | ||||
| 					Result <<= 8; | ||||
| 					Result += (uint8_t)i; | ||||
| 				} | ||||
| 				if (Result != 0) | ||||
| 					break; | ||||
| 			} | ||||
| 		} | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| 	void SaveSystemId(uint64_t Id) { | ||||
| 		try { | ||||
| 			std::ofstream O; | ||||
| 			O.open(Daemon()->DataDir() + "/system.id",std::ios::binary | std::ios::trunc); | ||||
| 			O << Id; | ||||
| 			O.close(); | ||||
| 		} catch (...) | ||||
| 		{ | ||||
| 			std::cout << "Could not save system ID" << std::endl; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	uint64_t InitializeSystemId() { | ||||
| 		std::random_device	RDev; | ||||
| 		std::srand(RDev()); | ||||
| 		std::chrono::high_resolution_clock	Clock; | ||||
| 		auto Now = Clock.now().time_since_epoch().count(); | ||||
| 		auto S = (GetDefaultMacAsInt64() + std::rand() + Now)  ; | ||||
| 		SaveSystemId(S); | ||||
| 		std::cout << "ID: " << S << std::endl; | ||||
| 		return S; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t GetSystemId() { | ||||
| 		uint64_t ID=0; | ||||
|  | ||||
| 		// if the system ID file exists, open and read it. | ||||
| 		Poco::File	SID( Daemon()->DataDir() + "/system.id"); | ||||
| 		try { | ||||
| 			if (SID.exists()) { | ||||
| 				std::ifstream I; | ||||
| 				I.open(SID.path()); | ||||
| 				I >> ID; | ||||
| 				I.close(); | ||||
| 				if (ID == 0) | ||||
| 					return InitializeSystemId(); | ||||
| 				return ID; | ||||
| 			} else { | ||||
| 				return InitializeSystemId(); | ||||
| 			} | ||||
| 		} catch (...) { | ||||
| 			return InitializeSystemId(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	bool ValidEMailAddress(const std::string &email) { | ||||
| 		// define a regular expression | ||||
| 		const std::regex pattern | ||||
| 			("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); | ||||
|  | ||||
| 		// try to match the string with the regular expression | ||||
| 		return std::regex_match(email, pattern); | ||||
| 	} | ||||
|  | ||||
| 	std::string LoadFile( const Poco::File & F) { | ||||
| 	    std::string Result; | ||||
| 	    try { | ||||
| 	        std::ostringstream OS; | ||||
| 	        std::ifstream   IF(F.path()); | ||||
| 	        Poco::StreamCopier::copyStream(IF, OS); | ||||
| 	        Result = OS.str(); | ||||
| 	    } catch (...) { | ||||
|  | ||||
| 	    } | ||||
| 	    return Result; | ||||
| 	} | ||||
|  | ||||
|     void ReplaceVariables( std::string & Content , const Types::StringPairVec & P) { | ||||
| 	    for(const auto &[Variable,Value]:P) { | ||||
| 	        Poco::replaceInPlace(Content,"${" + Variable + "}", Value); | ||||
| 	    } | ||||
| 	} | ||||
|  | ||||
|     MediaTypeEncoding FindMediaType(const Poco::File &F) { | ||||
| 	    const auto E = Poco::Path(F.path()).getExtension(); | ||||
| 	    if(E=="png") | ||||
| 	        return MediaTypeEncoding{   .Encoding = BINARY, | ||||
|                                         .ContentType = "image/png" }; | ||||
| 	    if(E=="gif") | ||||
|             return MediaTypeEncoding{   .Encoding = BINARY, | ||||
|                                         .ContentType = "image/gif" }; | ||||
|         if(E=="jpeg" || E=="jpg") | ||||
|             return MediaTypeEncoding{   .Encoding = BINARY, | ||||
|                                         .ContentType = "image/jpeg" }; | ||||
|         if(E=="svg" || E=="svgz") | ||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, | ||||
|                                         .ContentType = "image/svg+xml" }; | ||||
|         if(E=="html") | ||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, | ||||
|                                         .ContentType = "text/html" }; | ||||
|         if(E=="css") | ||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, | ||||
|                                         .ContentType = "text/css" }; | ||||
|         if(E=="js") | ||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, | ||||
|                                         .ContentType = "application/javascript" }; | ||||
|         return MediaTypeEncoding{       .Encoding = BINARY, | ||||
|                                         .ContentType = "application/octet-stream" }; | ||||
| 	} | ||||
|  | ||||
| 	std::string BinaryFileToHexString(const Poco::File &F) { | ||||
|         static const char hex[] = "0123456789abcdef"; | ||||
|         std::string Result; | ||||
|         try { | ||||
|             std::ifstream IF(F.path()); | ||||
|  | ||||
|             int Count = 0; | ||||
|             while (IF.good()) { | ||||
|                 if (Count) | ||||
|                     Result += ", "; | ||||
|                 if ((Count % 32) == 0) | ||||
|                     Result += "\r\n"; | ||||
|                 Count++; | ||||
|                 unsigned char C = IF.get(); | ||||
|                 Result += "0x"; | ||||
|                 Result += (char) (hex[(C & 0xf0) >> 4]); | ||||
|                 Result += (char) (hex[(C & 0x0f)]); | ||||
|             } | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return Result; | ||||
| 	} | ||||
|  | ||||
|     std::string SecondsToNiceText(uint64_t Seconds) { | ||||
| 	    std::string Result; | ||||
| 	    int Days = Seconds / (24*60*60); | ||||
| 	    Seconds -= Days * (24*60*60); | ||||
| 	    int Hours= Seconds / (60*60); | ||||
| 	    Seconds -= Hours * (60*60); | ||||
| 	    int Minutes = Seconds / 60; | ||||
| 	    Seconds -= Minutes * 60; | ||||
| 	    Result = std::to_string(Days) +" days, " + std::to_string(Hours) + ":" + std::to_string(Minutes) + ":" + std::to_string(Seconds); | ||||
| 	    return Result; | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										87
									
								
								src/Utils.h
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								src/Utils.h
									
									
									
									
									
								
							| @@ -1,87 +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. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALGW_UTILS_H | ||||
| #define UCENTRALGW_UTILS_H | ||||
|  | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <iomanip> | ||||
| #include <sstream> | ||||
|  | ||||
| #include "Poco/Net/NetworkInterface.h" | ||||
| #include "Poco/Net/IPAddress.h" | ||||
| #include "Poco/String.h" | ||||
| #include "Poco/File.h" | ||||
| #include "OpenWifiTypes.h" | ||||
|  | ||||
| #define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; }; | ||||
|  | ||||
| namespace OpenWifi::Utils { | ||||
|  | ||||
|     enum MediaTypeEncodings { | ||||
|         PLAIN, | ||||
|         BINARY, | ||||
|         BASE64 | ||||
|     }; | ||||
|     struct MediaTypeEncoding { | ||||
|         MediaTypeEncodings  Encoding=PLAIN; | ||||
|         std::string         ContentType; | ||||
|     }; | ||||
|  | ||||
| 	[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter=','); | ||||
| 	[[nodiscard]] std::string FormatIPv6(const std::string & I ); | ||||
| 	inline void padTo(std::string& str, size_t num, char paddingChar = '\0') { | ||||
| 		str.append(num - str.length() % num, paddingChar); | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string SerialToMAC(const std::string &Serial); | ||||
| 	[[nodiscard]] std::string ToHex(const std::vector<unsigned char> & B); | ||||
|  | ||||
| 	using byte = std::uint8_t; | ||||
|  | ||||
| 	[[nodiscard]] std::string base64encode(const byte *input, unsigned long size); | ||||
| 	std::vector<byte> base64decode(const std::string& input); | ||||
|  | ||||
| //	[[nodiscard]] std::string to_RFC3339(uint64_t t); | ||||
| //	[[nodiscard]] uint64_t from_RFC3339(const std::string &t); | ||||
|  | ||||
| 	bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds); | ||||
| 	bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day); | ||||
| 	bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2); | ||||
|  | ||||
| 	[[nodiscard]] bool ValidSerialNumber(const std::string &Serial); | ||||
| 	[[nodiscard]] std::string LogLevelToString(int Level); | ||||
|  | ||||
| 	[[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int extrabits=2); | ||||
| 	[[nodiscard]] uint64_t SerialNumberToInt(const std::string & S); | ||||
| 	[[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S); | ||||
|  | ||||
| 	[[nodiscard]] uint64_t GetDefaultMacAsInt64(); | ||||
| 	[[nodiscard]] uint64_t GetSystemId(); | ||||
|  | ||||
| 	[[nodiscard]] bool ValidEMailAddress(const std::string &E); | ||||
| 	[[nodiscard]] std::string LoadFile( const Poco::File & F); | ||||
|     void ReplaceVariables( std::string & Content , const Types::StringPairVec & P); | ||||
|  | ||||
|     [[nodiscard]] MediaTypeEncoding FindMediaType(const Poco::File &F); | ||||
|     [[nodiscard]] std::string BinaryFileToHexString( const Poco::File &F); | ||||
|     [[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds); | ||||
|  | ||||
| 	template< typename T > | ||||
| 	std::string int_to_hex( T i ) | ||||
| 	{ | ||||
| 		std::stringstream stream; | ||||
| 		stream << std::setfill ('0') << std::setw(12) | ||||
| 		<< std::hex << i; | ||||
| 		return stream.str(); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| } | ||||
| #endif // UCENTRALGW_UTILS_H | ||||
							
								
								
									
										2479
									
								
								src/framework/ConfigurationValidator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2479
									
								
								src/framework/ConfigurationValidator.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										46
									
								
								src/framework/ConfigurationValidator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/framework/ConfigurationValidator.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-09-14. | ||||
| // | ||||
|  | ||||
| #ifndef OWPROV_CONFIGURATIONVALIDATOR_H | ||||
| #define OWPROV_CONFIGURATIONVALIDATOR_H | ||||
|  | ||||
| #include <nlohmann/json-schema.hpp> | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| using nlohmann::json; | ||||
| using nlohmann::json_schema::json_validator; | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class ConfigurationValidator : public  SubSystemServer { | ||||
|     public: | ||||
|  | ||||
|         static ConfigurationValidator *instance() { | ||||
|             if(instance_== nullptr) | ||||
|                 instance_ = new ConfigurationValidator; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         bool Validate(const std::string &C, std::string &Error); | ||||
|         static void my_format_checker(const std::string &format, const std::string &value); | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         void reinitialize(Poco::Util::Application &self) override; | ||||
|  | ||||
|     private: | ||||
|         static  ConfigurationValidator * instance_; | ||||
|         bool            Initialized_=false; | ||||
|         bool            Working_=false; | ||||
|         void            Init(); | ||||
|         std::unique_ptr<json_validator>  Validator_=std::make_unique<json_validator>(nullptr, my_format_checker); | ||||
|  | ||||
|         ConfigurationValidator(): | ||||
|             SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     inline ConfigurationValidator * ConfigurationValidator() { return ConfigurationValidator::instance(); } | ||||
|     inline bool ValidateUCentralConfiguration(const std::string &C, std::string &Error) { return ConfigurationValidator::instance()->Validate(C, Error); } | ||||
| } | ||||
|  | ||||
| #endif //OWPROV_CONFIGURATIONVALIDATOR_H | ||||
							
								
								
									
										273
									
								
								src/framework/CountryCodes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								src/framework/CountryCodes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,273 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-08. | ||||
| // | ||||
|  | ||||
| #ifndef OWPROV_COUNTRYCODES_H | ||||
| #define OWPROV_COUNTRYCODES_H | ||||
|  | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <utility> | ||||
|  | ||||
| namespace OpenWifi { | ||||
|      | ||||
|     struct CountryInfo { | ||||
|         std::string     code; | ||||
|         std::string     name; | ||||
|     }; | ||||
|      | ||||
|     inline static const std::vector<CountryInfo> CountryCodes { | ||||
|         { .code= "US", .name= "United States" }, | ||||
|         { .code= "GB", .name= "United Kingdom" }, | ||||
|         { .code= "CA", .name= "Canada" }, | ||||
|         { .code= "AF", .name= "Afghanistan" }, | ||||
|         { .code= "AX", .name= "Aland Islands" }, | ||||
|         { .code= "AL", .name= "Albania" }, | ||||
|         { .code= "DZ", .name= "Algeria" }, | ||||
|         { .code= "AS", .name= "American Samoa" }, | ||||
|         { .code= "AD", .name= "Andorra" }, | ||||
|         { .code= "AO", .name= "Angola" }, | ||||
|         { .code= "AI", .name= "Anguilla" }, | ||||
|         { .code= "AQ", .name= "Antarctica" }, | ||||
|         { .code= "AG", .name= "Antigua And Barbuda" }, | ||||
|         { .code= "AR", .name= "Argentina" }, | ||||
|         { .code= "AM", .name= "Armenia" }, | ||||
|         { .code= "AN", .name= "Netherlands Antilles" }, | ||||
|         { .code= "AW", .name= "Aruba" }, | ||||
|         { .code= "AU", .name= "Australia" }, | ||||
|         { .code= "AT", .name= "Austria" }, | ||||
|         { .code= "AZ", .name= "Azerbaijan" }, | ||||
|         { .code= "BS", .name= "Bahamas" }, | ||||
|         { .code= "BH", .name= "Bahrain" }, | ||||
|         { .code= "BD", .name= "Bangladesh" }, | ||||
|         { .code= "BB", .name= "Barbados" }, | ||||
|         { .code= "BY", .name= "Belarus" }, | ||||
|         { .code= "BE", .name= "Belgium" }, | ||||
|         { .code= "BZ", .name= "Belize" }, | ||||
|         { .code= "BJ", .name= "Benin" }, | ||||
|         { .code= "BM", .name= "Bermuda" }, | ||||
|         { .code= "BT", .name= "Bhutan" }, | ||||
|         { .code= "BO", .name= "Bolivia" }, | ||||
|         { .code= "BA", .name= "Bosnia And Herzegovina" }, | ||||
|         { .code= "BW", .name= "Botswana" }, | ||||
|         { .code= "BV", .name= "Bouvet Island" }, | ||||
|         { .code= "BR", .name= "Brazil" }, | ||||
|         { .code= "IO", .name= "British Indian Ocean Territory" }, | ||||
|         { .code= "BN", .name= "Brunei Darussalam" }, | ||||
|         { .code= "BG", .name= "Bulgaria" }, | ||||
|         { .code= "BF", .name= "Burkina Faso" }, | ||||
|         { .code= "BI", .name= "Burundi" }, | ||||
|         { .code= "KH", .name= "Cambodia" }, | ||||
|         { .code= "CM", .name= "Cameroon" }, | ||||
|         { .code= "CA", .name= "Canada" }, | ||||
|         { .code= "CV", .name= "Cape Verde" }, | ||||
|         { .code= "KY", .name= "Cayman Islands" }, | ||||
|         { .code= "CF", .name= "Central African Republic" }, | ||||
|         { .code= "TD", .name= "Chad" }, | ||||
|         { .code= "CL", .name= "Chile" }, | ||||
|         { .code= "CN", .name= "China" }, | ||||
|         { .code= "CX", .name= "Christmas Island" }, | ||||
|         { .code= "CC", .name= "Cocos (Keeling) Islands" }, | ||||
|         { .code= "CO", .name= "Colombia" }, | ||||
|         { .code= "KM", .name= "Comoros" }, | ||||
|         { .code= "CG", .name= "Congo" }, | ||||
|         { .code= "CD", .name= "Congo, Democratic Republic" }, | ||||
|         { .code= "CK", .name= "Cook Islands" }, | ||||
|         { .code= "CR", .name= "Costa Rica" }, | ||||
|         { .code= "CI", .name= "Cote D\"Ivoire" }, | ||||
|         { .code= "HR", .name= "Croatia" }, | ||||
|         { .code= "CU", .name= "Cuba" }, | ||||
|         { .code= "CY", .name= "Cyprus" }, | ||||
|         { .code= "CZ", .name= "Czech Republic" }, | ||||
|         { .code= "DK", .name= "Denmark" }, | ||||
|         { .code= "DJ", .name= "Djibouti" }, | ||||
|         { .code= "DM", .name= "Dominica" }, | ||||
|         { .code= "DO", .name= "Dominican Republic" }, | ||||
|         { .code= "EC", .name= "Ecuador" }, | ||||
|         { .code= "EG", .name= "Egypt" }, | ||||
|         { .code= "SV", .name= "El Salvador" }, | ||||
|         { .code= "GQ", .name= "Equatorial Guinea" }, | ||||
|         { .code= "ER", .name= "Eritrea" }, | ||||
|         { .code= "EE", .name= "Estonia" }, | ||||
|         { .code= "ET", .name= "Ethiopia" }, | ||||
|         { .code= "FK", .name= "Falkland Islands (Malvinas)" }, | ||||
|         { .code= "FO", .name= "Faroe Islands" }, | ||||
|         { .code= "FJ", .name= "Fiji" }, | ||||
|         { .code= "FI", .name= "Finland" }, | ||||
|         { .code= "FR", .name= "France" }, | ||||
|         { .code= "GF", .name= "French Guiana" }, | ||||
|         { .code= "PF", .name= "French Polynesia" }, | ||||
|         { .code= "TF", .name= "French Southern Territories" }, | ||||
|         { .code= "GA", .name= "Gabon" }, | ||||
|         { .code= "GM", .name= "Gambia" }, | ||||
|         { .code= "GE", .name= "Georgia" }, | ||||
|         { .code= "DE", .name= "Germany" }, | ||||
|         { .code= "GH", .name= "Ghana" }, | ||||
|         { .code= "GI", .name= "Gibraltar" }, | ||||
|         { .code= "GR", .name= "Greece" }, | ||||
|         { .code= "GL", .name= "Greenland" }, | ||||
|         { .code= "GD", .name= "Grenada" }, | ||||
|         { .code= "GP", .name= "Guadeloupe" }, | ||||
|         { .code= "GU", .name= "Guam" }, | ||||
|         { .code= "GT", .name= "Guatemala" }, | ||||
|         { .code= "GG", .name= "Guernsey" }, | ||||
|         { .code= "GN", .name= "Guinea" }, | ||||
|         { .code= "GW", .name= "Guinea-Bissau" }, | ||||
|         { .code= "GY", .name= "Guyana" }, | ||||
|         { .code= "HT", .name= "Haiti" }, | ||||
|         { .code= "HM", .name= "Heard Island & Mcdonald Islands" }, | ||||
|         { .code= "VA", .name= "Holy See (Vatican City State)" }, | ||||
|         { .code= "HN", .name= "Honduras" }, | ||||
|         { .code= "HK", .name= "Hong Kong" }, | ||||
|         { .code= "HU", .name= "Hungary" }, | ||||
|         { .code= "IS", .name= "Iceland" }, | ||||
|         { .code= "IN", .name= "India" }, | ||||
|         { .code= "ID", .name= "Indonesia" }, | ||||
|         { .code= "IR", .name= "Iran, Islamic Republic Of" }, | ||||
|         { .code= "IQ", .name= "Iraq" }, | ||||
|         { .code= "IE", .name= "Ireland" }, | ||||
|         { .code= "IM", .name= "Isle Of Man" }, | ||||
|         { .code= "IL", .name= "Israel" }, | ||||
|         { .code= "IT", .name= "Italy" }, | ||||
|         { .code= "JM", .name= "Jamaica" }, | ||||
|         { .code= "JP", .name= "Japan" }, | ||||
|         { .code= "JE", .name= "Jersey" }, | ||||
|         { .code= "JO", .name= "Jordan" }, | ||||
|         { .code= "KZ", .name= "Kazakhstan" }, | ||||
|         { .code= "KE", .name= "Kenya" }, | ||||
|         { .code= "KI", .name= "Kiribati" }, | ||||
|         { .code= "KR", .name= "Korea" }, | ||||
|         { .code= "KW", .name= "Kuwait" }, | ||||
|         { .code= "KG", .name= "Kyrgyzstan" }, | ||||
|         { .code= "LA", .name= "Lao People\"s Democratic Republic" }, | ||||
|         { .code= "LV", .name= "Latvia" }, | ||||
|         { .code= "LB", .name= "Lebanon" }, | ||||
|         { .code= "LS", .name= "Lesotho" }, | ||||
|         { .code= "LR", .name= "Liberia" }, | ||||
|         { .code= "LY", .name= "Libyan Arab Jamahiriya" }, | ||||
|         { .code= "LI", .name= "Liechtenstein" }, | ||||
|         { .code= "LT", .name= "Lithuania" }, | ||||
|         { .code= "LU", .name= "Luxembourg" }, | ||||
|         { .code= "MO", .name= "Macao" }, | ||||
|         { .code= "MK", .name= "Macedonia" }, | ||||
|         { .code= "MG", .name= "Madagascar" }, | ||||
|         { .code= "MW", .name= "Malawi" }, | ||||
|         { .code= "MY", .name= "Malaysia" }, | ||||
|         { .code= "MV", .name= "Maldives" }, | ||||
|         { .code= "ML", .name= "Mali" }, | ||||
|         { .code= "MT", .name= "Malta" }, | ||||
|         { .code= "MH", .name= "Marshall Islands" }, | ||||
|         { .code= "MQ", .name= "Martinique" }, | ||||
|         { .code= "MR", .name= "Mauritania" }, | ||||
|         { .code= "MU", .name= "Mauritius" }, | ||||
|         { .code= "YT", .name= "Mayotte" }, | ||||
|         { .code= "MX", .name= "Mexico" }, | ||||
|         { .code= "FM", .name= "Micronesia, Federated States Of" }, | ||||
|         { .code= "MD", .name= "Moldova" }, | ||||
|         { .code= "MC", .name= "Monaco" }, | ||||
|         { .code= "MN", .name= "Mongolia" }, | ||||
|         { .code= "ME", .name= "Montenegro" }, | ||||
|         { .code= "MS", .name= "Montserrat" }, | ||||
|         { .code= "MA", .name= "Morocco" }, | ||||
|         { .code= "MZ", .name= "Mozambique" }, | ||||
|         { .code= "MM", .name= "Myanmar" }, | ||||
|         { .code= "NA", .name= "Namibia" }, | ||||
|         { .code= "NR", .name= "Nauru" }, | ||||
|         { .code= "NP", .name= "Nepal" }, | ||||
|         { .code= "NL", .name= "Netherlands" }, | ||||
|         { .code= "AN", .name= "Netherlands Antilles" }, | ||||
|         { .code= "NC", .name= "New Caledonia" }, | ||||
|         { .code= "NZ", .name= "New Zealand" }, | ||||
|         { .code= "NI", .name= "Nicaragua" }, | ||||
|         { .code= "NE", .name= "Niger" }, | ||||
|         { .code= "NG", .name= "Nigeria" }, | ||||
|         { .code= "NU", .name= "Niue" }, | ||||
|         { .code= "NF", .name= "Norfolk Island" }, | ||||
|         { .code= "MP", .name= "Northern Mariana Islands" }, | ||||
|         { .code= "NO", .name= "Norway" }, | ||||
|         { .code= "OM", .name= "Oman" }, | ||||
|         { .code= "PK", .name= "Pakistan" }, | ||||
|         { .code= "PW", .name= "Palau" }, | ||||
|         { .code= "PS", .name= "Palestinian Territory, Occupied" }, | ||||
|         { .code= "PA", .name= "Panama" }, | ||||
|         { .code= "PG", .name= "Papua New Guinea" }, | ||||
|         { .code= "PY", .name= "Paraguay" }, | ||||
|         { .code= "PE", .name= "Peru" }, | ||||
|         { .code= "PH", .name= "Philippines" }, | ||||
|         { .code= "PN", .name= "Pitcairn" }, | ||||
|         { .code= "PL", .name= "Poland" }, | ||||
|         { .code= "PT", .name= "Portugal" }, | ||||
|         { .code= "PR", .name= "Puerto Rico" }, | ||||
|         { .code= "QA", .name= "Qatar" }, | ||||
|         { .code= "RE", .name= "Reunion" }, | ||||
|         { .code= "RO", .name= "Romania" }, | ||||
|         { .code= "RU", .name= "Russian Federation" }, | ||||
|         { .code= "RW", .name= "Rwanda" }, | ||||
|         { .code= "BL", .name= "Saint Barthelemy" }, | ||||
|         { .code= "SH", .name= "Saint Helena" }, | ||||
|         { .code= "KN", .name= "Saint Kitts And Nevis" }, | ||||
|         { .code= "LC", .name= "Saint Lucia" }, | ||||
|         { .code= "MF", .name= "Saint Martin" }, | ||||
|         { .code= "PM", .name= "Saint Pierre And Miquelon" }, | ||||
|         { .code= "VC", .name= "Saint Vincent And Grenadines" }, | ||||
|         { .code= "WS", .name= "Samoa" }, | ||||
|         { .code= "SM", .name= "San Marino" }, | ||||
|         { .code= "ST", .name= "Sao Tome And Principe" }, | ||||
|         { .code= "SA", .name= "Saudi Arabia" }, | ||||
|         { .code= "SN", .name= "Senegal" }, | ||||
|         { .code= "RS", .name= "Serbia" }, | ||||
|         { .code= "SC", .name= "Seychelles" }, | ||||
|         { .code= "SL", .name= "Sierra Leone" }, | ||||
|         { .code= "SG", .name= "Singapore" }, | ||||
|         { .code= "SK", .name= "Slovakia" }, | ||||
|         { .code= "SI", .name= "Slovenia" }, | ||||
|         { .code= "SB", .name= "Solomon Islands" }, | ||||
|         { .code= "SO", .name= "Somalia" }, | ||||
|         { .code= "ZA", .name= "South Africa" }, | ||||
|         { .code= "GS", .name= "South Georgia And Sandwich Isl." }, | ||||
|         { .code= "ES", .name= "Spain" }, | ||||
|         { .code= "LK", .name= "Sri Lanka" }, | ||||
|         { .code= "SD", .name= "Sudan" }, | ||||
|         { .code= "SR", .name= "Suriname" }, | ||||
|         { .code= "SJ", .name= "Svalbard And Jan Mayen" }, | ||||
|         { .code= "SZ", .name= "Swaziland" }, | ||||
|         { .code= "SE", .name= "Sweden" }, | ||||
|         { .code= "CH", .name= "Switzerland" }, | ||||
|         { .code= "SY", .name= "Syrian Arab Republic" }, | ||||
|         { .code= "TW", .name= "Taiwan" }, | ||||
|         { .code= "TJ", .name= "Tajikistan" }, | ||||
|         { .code= "TZ", .name= "Tanzania" }, | ||||
|         { .code= "TH", .name= "Thailand" }, | ||||
|         { .code= "TL", .name= "Timor-Leste" }, | ||||
|         { .code= "TG", .name= "Togo" }, | ||||
|         { .code= "TK", .name= "Tokelau" }, | ||||
|         { .code= "TO", .name= "Tonga" }, | ||||
|         { .code= "TT", .name= "Trinidad And Tobago" }, | ||||
|         { .code= "TN", .name= "Tunisia" }, | ||||
|         { .code= "TR", .name= "Turkey" }, | ||||
|         { .code= "TM", .name= "Turkmenistan" }, | ||||
|         { .code= "TC", .name= "Turks And Caicos Islands" }, | ||||
|         { .code= "TV", .name= "Tuvalu" }, | ||||
|         { .code= "UG", .name= "Uganda" }, | ||||
|         { .code= "UA", .name= "Ukraine" }, | ||||
|         { .code= "AE", .name= "United Arab Emirates" }, | ||||
|         { .code= "GB", .name= "United Kingdom" }, | ||||
|         { .code= "US", .name= "United States" }, | ||||
|         { .code= "UM", .name= "United States Outlying Islands" }, | ||||
|         { .code= "UY", .name= "Uruguay" }, | ||||
|         { .code= "UZ", .name= "Uzbekistan" }, | ||||
|         { .code= "VU", .name= "Vanuatu" }, | ||||
|         { .code= "VE", .name= "Venezuela" }, | ||||
|         { .code= "VN", .name= "Viet Nam" }, | ||||
|         { .code= "VG", .name= "Virgin Islands, British" }, | ||||
|         { .code= "VI", .name= "Virgin Islands, U.S." }, | ||||
|         { .code= "WF", .name= "Wallis And Futuna" }, | ||||
|         { .code= "EH", .name= "Western Sahara" }, | ||||
|         { .code= "YE", .name= "Yemen" }, | ||||
|         { .code= "ZM", .name= "Zambia" }, | ||||
|         { .code= "ZW", .name= "Zimbabwe" } | ||||
|     }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif //OWPROV_COUNTRYCODES_H | ||||
| @@ -1,7 +1,10 @@ | ||||
| //
 | ||||
| // Created by stephane bourque on 2021-06-07.
 | ||||
| //	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_KAFKA_TOPICS_H | ||||
| #define UCENTRALGW_KAFKA_TOPICS_H | ||||
| 
 | ||||
							
								
								
									
										3906
									
								
								src/framework/MicroService.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3906
									
								
								src/framework/MicroService.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,8 +9,6 @@ | ||||
| #ifndef UCENTRALGW_UCENTRALTYPES_H | ||||
| #define UCENTRALGW_UCENTRALTYPES_H | ||||
| 
 | ||||
| #include "SubSystemServer.h" | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <map> | ||||
| @@ -29,13 +27,12 @@ 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; | ||||
| 
 | ||||
| @@ -47,9 +47,19 @@ 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
 | ||||
| @@ -113,6 +113,7 @@ 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"; | ||||
| @@ -127,9 +128,12 @@ 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"; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										166
									
								
								src/framework/StorageClass.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/framework/StorageClass.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,166 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-10-06. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Data/Session.h" | ||||
| #include "Poco/Data/SessionPool.h" | ||||
| #include "Poco/Data/SQLite/Connector.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
|  | ||||
| #ifndef SMALL_BUILD | ||||
| #include "Poco/Data/PostgreSQL/Connector.h" | ||||
| #include "Poco/Data/MySQL/Connector.h" | ||||
| #endif | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     enum DBType { | ||||
|         sqlite, | ||||
|         pgsql, | ||||
|         mysql | ||||
|     }; | ||||
|  | ||||
|     class StorageClass : public SubSystemServer { | ||||
|     public: | ||||
|         StorageClass() noexcept: | ||||
|             SubSystemServer("StorageClass", "STORAGE-SVR", "storage") | ||||
|         { | ||||
|         } | ||||
|  | ||||
|         int Start() override { | ||||
|             std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
|             Logger_.setLevel(Poco::Message::PRIO_NOTICE); | ||||
|             Logger_.notice("Starting."); | ||||
|             std::string DBType = MicroService::instance().ConfigGetString("storage.type"); | ||||
|  | ||||
|             if (DBType == "sqlite") { | ||||
|                 Setup_SQLite(); | ||||
|             } else if (DBType == "postgresql") { | ||||
|                 Setup_PostgreSQL(); | ||||
|             } else if (DBType == "mysql") { | ||||
|                 Setup_MySQL(); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         void Stop() override { | ||||
|             Pool_->shutdown(); | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||
|             if(dbType_==sqlite) { | ||||
|                 return " LIMIT " + std::to_string(From) + ", " + std::to_string(HowMany) + " "; | ||||
|             } else if(dbType_==pgsql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|             } else if(dbType_==mysql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|             } | ||||
|             return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|         } | ||||
|  | ||||
|         inline std::string ConvertParams(const std::string & S) const { | ||||
|             std::string R; | ||||
|             R.reserve(S.size()*2+1); | ||||
|             if(dbType_==pgsql) { | ||||
|                 auto Idx=1; | ||||
|                 for(auto const & i:S) | ||||
|                 { | ||||
|                     if(i=='?') { | ||||
|                         R += '$'; | ||||
|                         R.append(std::to_string(Idx++)); | ||||
|                     } else { | ||||
|                         R += i; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 R = S; | ||||
|             } | ||||
|             return R; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         inline int Setup_SQLite(); | ||||
|         inline int Setup_MySQL(); | ||||
|         inline int Setup_PostgreSQL(); | ||||
|  | ||||
|     protected: | ||||
|     	Poco::SharedPtr<Poco::Data::SessionPool>    Pool_; | ||||
|         Poco::Data::SQLite::Connector  	            SQLiteConn_; | ||||
|         Poco::Data::PostgreSQL::Connector           PostgresConn_; | ||||
|         Poco::Data::MySQL::Connector                MySQLConn_; | ||||
|         DBType                                      dbType_ = sqlite; | ||||
|     }; | ||||
|  | ||||
| #ifdef	SMALL_BUILD | ||||
|     int Service::Setup_MySQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||
|     int Service::Setup_PostgreSQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||
| #else | ||||
|  | ||||
|     inline int StorageClass::Setup_SQLite() { | ||||
|         Logger_.notice("SQLite StorageClass enabled."); | ||||
|         dbType_ = sqlite; | ||||
|         auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); | ||||
|         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); | ||||
|         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); | ||||
|         SQLiteConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 4, NumSessions, IdleTime)); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     inline int StorageClass::Setup_MySQL() { | ||||
|         Logger_.notice("MySQL StorageClass enabled."); | ||||
|         dbType_ = mysql; | ||||
|         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.mysql.maxsessions", 64); | ||||
|         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.mysql.idletime", 60); | ||||
|         auto Host = MicroService::instance().ConfigGetString("storage.type.mysql.host"); | ||||
|         auto Username = MicroService::instance().ConfigGetString("storage.type.mysql.username"); | ||||
|         auto Password = MicroService::instance().ConfigGetString("storage.type.mysql.password"); | ||||
|         auto Database = MicroService::instance().ConfigGetString("storage.type.mysql.database"); | ||||
|         auto Port = MicroService::instance().ConfigGetString("storage.type.mysql.port"); | ||||
|  | ||||
|         std::string ConnectionStr = | ||||
|                 "host=" + Host + | ||||
|                 ";user=" + Username + | ||||
|                 ";password=" + Password + | ||||
|                 ";db=" + Database + | ||||
|                 ";port=" + Port + | ||||
|                 ";compress=true;auto-reconnect=true"; | ||||
|  | ||||
|         MySQLConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(MySQLConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     inline int StorageClass::Setup_PostgreSQL() { | ||||
|         Logger_.notice("PostgreSQL StorageClass enabled."); | ||||
|         dbType_ = pgsql; | ||||
|         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.postgresql.maxsessions", 64); | ||||
|         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.postgresql.idletime", 60); | ||||
|         auto Host = MicroService::instance().ConfigGetString("storage.type.postgresql.host"); | ||||
|         auto Username = MicroService::instance().ConfigGetString("storage.type.postgresql.username"); | ||||
|         auto Password = MicroService::instance().ConfigGetString("storage.type.postgresql.password"); | ||||
|         auto Database = MicroService::instance().ConfigGetString("storage.type.postgresql.database"); | ||||
|         auto Port = MicroService::instance().ConfigGetString("storage.type.postgresql.port"); | ||||
|         auto ConnectionTimeout = MicroService::instance().ConfigGetString("storage.type.postgresql.connectiontimeout"); | ||||
|  | ||||
|         std::string ConnectionStr = | ||||
|                 "host=" + Host + | ||||
|                 " user=" + Username + | ||||
|                 " password=" + Password + | ||||
|                 " dbname=" + Database + | ||||
|                 " port=" + Port + | ||||
|                 " connect_timeout=" + ConnectionTimeout; | ||||
|  | ||||
|         PostgresConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(PostgresConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| } | ||||
							
								
								
									
										758
									
								
								src/framework/orm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										758
									
								
								src/framework/orm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,758 @@ | ||||
| // | ||||
| //	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 __OPENWIFI_ORM_H__ | ||||
| #define __OPENWIFI_ORM_H__ | ||||
|  | ||||
| #include <string> | ||||
| #include <memory> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <map> | ||||
| #include <vector> | ||||
| #include <array> | ||||
|  | ||||
| #include "Poco/Tuple.h" | ||||
| #include "Poco/Data/SessionPool.h" | ||||
| #include "Poco/Data/Statement.h" | ||||
| #include "Poco/Data/RecordSet.h" | ||||
| #include "Poco/Data/SQLite/Connector.h" | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/StringTokenizer.h" | ||||
| #include "StorageClass.h" | ||||
|  | ||||
| namespace ORM { | ||||
|  | ||||
|     enum FieldType { | ||||
|         FT_INT, | ||||
|         FT_BIGINT, | ||||
|         FT_TEXT, | ||||
|         FT_VARCHAR, | ||||
|         FT_BLOB | ||||
|     }; | ||||
|  | ||||
|     enum Indextype { | ||||
|         ASC, | ||||
|         DESC | ||||
|     }; | ||||
|  | ||||
|     struct Field { | ||||
|         std::string Name; | ||||
|         FieldType   Type; | ||||
|         int         Size=0; | ||||
|         bool        Index=false; | ||||
|  | ||||
|  | ||||
|         Field(std::string N, FieldType T, int S=0, bool Index=false) : | ||||
|             Name(std::move(N)), | ||||
|             Type(T), | ||||
|             Size(S), | ||||
|             Index(Index) {} | ||||
|  | ||||
|         explicit Field(std::string N) : | ||||
|             Name(std::move(N)) | ||||
|         { | ||||
|             Type = FT_TEXT; | ||||
|         } | ||||
|  | ||||
|         Field(std::string N, int S) : | ||||
|             Name(std::move(N)), Size(S) | ||||
|         { | ||||
|             if(Size>0 && Size<255) | ||||
|                 Type = FT_VARCHAR; | ||||
|             else | ||||
|                 Type = FT_TEXT; | ||||
|         } | ||||
|  | ||||
|         Field(std::string N, int S, bool I): | ||||
|             Name(std::move(N)), Size(S), Index(I) | ||||
|         { | ||||
|             if(Size>0 && Size<255) | ||||
|                 Type = FT_VARCHAR; | ||||
|             else | ||||
|                 Type = FT_TEXT; | ||||
|         } | ||||
|     }; | ||||
|     typedef std::vector<Field>  FieldVec; | ||||
|  | ||||
|     struct IndexEntry { | ||||
|         std::string     FieldName; | ||||
|         Indextype   Type; | ||||
|     }; | ||||
|     typedef std::vector<IndexEntry>     IndexEntryVec; | ||||
|  | ||||
|     struct Index { | ||||
|         std::string         Name; | ||||
|         IndexEntryVec   Entries; | ||||
|     }; | ||||
|     typedef std::vector<Index>      IndexVec; | ||||
|  | ||||
|     inline std::string FieldTypeToChar(OpenWifi::DBType Type, FieldType T, int Size=0) { | ||||
|         switch(T) { | ||||
|             case FT_INT:    return "INT"; | ||||
|             case FT_BIGINT: return "BIGINT"; | ||||
|             case FT_TEXT:   return "TEXT"; | ||||
|             case FT_VARCHAR: | ||||
|                 if(Size) | ||||
|                     return std::string("VARCHAR(") + std::to_string(Size) + std::string(")"); | ||||
|                 else | ||||
|                     return "TEXT"; | ||||
|                 case FT_BLOB: | ||||
|                     if(Type==OpenWifi::DBType::mysql) | ||||
|                         return "LONGBLOB"; | ||||
|                     else if(Type==OpenWifi::DBType::pgsql) | ||||
|                         return "BYTEA"; | ||||
|                     else if(Type==OpenWifi::DBType::sqlite) | ||||
|                         return "BLOB"; | ||||
|                     default: | ||||
|                         assert(false); | ||||
|                         return ""; | ||||
|         } | ||||
|         assert(false); | ||||
|         return ""; | ||||
|     } | ||||
|  | ||||
|     inline std::string Escape(const std::string &S) { | ||||
|         std::string R; | ||||
|  | ||||
|         for(const auto &i:S) | ||||
|             if(i=='\'') | ||||
|                 R += "''"; | ||||
|             else | ||||
|                 R += i; | ||||
|             return R; | ||||
|     } | ||||
|  | ||||
|     enum SqlComparison { EQ = 0, NEQ, LT, LTE, GT, GTE }; | ||||
|     enum SqlBinaryOp { AND = 0 , OR }; | ||||
|  | ||||
|     static const std::vector<std::string> BOPS{" and ", " or "}; | ||||
|     static const std::vector<std::string> SQLCOMPS{"=","!=","<","<=",">",">="}; | ||||
|  | ||||
|     inline std::string to_string(uint64_t V) { | ||||
|         return std::to_string(V); | ||||
|     } | ||||
|  | ||||
|     inline std::string to_string(int V) { | ||||
|         return std::to_string(V); | ||||
|     } | ||||
|  | ||||
|     inline std::string to_string(bool V) { | ||||
|         return std::to_string(V); | ||||
|     } | ||||
|  | ||||
|     inline std::string to_string(const std::string &S) { | ||||
|         return S; | ||||
|     } | ||||
|  | ||||
|     inline std::string to_string(const char * S) { | ||||
|         return S; | ||||
|     } | ||||
|  | ||||
|     template <typename RecordTuple, typename RecordType> class DB { | ||||
|     public: | ||||
|         DB( OpenWifi::DBType dbtype, | ||||
|             const char *TableName, | ||||
|             const FieldVec & Fields, | ||||
|             const IndexVec & Indexes, | ||||
|             Poco::Data::SessionPool & Pool, | ||||
|                 Poco::Logger &L, | ||||
|                 const char *Prefix): | ||||
|                 Type(dbtype), | ||||
|                 DBName(TableName), | ||||
|                 Pool_(Pool), | ||||
|                 Logger_(L), | ||||
|                 Prefix_(Prefix) | ||||
|         { | ||||
|             bool first = true; | ||||
|             int  Place=0; | ||||
|  | ||||
|             assert( RecordTuple::length == Fields.size()); | ||||
|  | ||||
|             for(const auto &i:Fields) { | ||||
|  | ||||
|                 FieldNames_[i.Name] = Place; | ||||
|                 if(!first) { | ||||
|                     CreateFields_ += ", "; | ||||
|                     SelectFields_ += ", "; | ||||
|                     UpdateFields_ += ", "; | ||||
|                     SelectList_ += ", "; | ||||
|                 } else { | ||||
|                     SelectList_ += "("; | ||||
|                 } | ||||
|  | ||||
|                 CreateFields_ += i.Name + " " + FieldTypeToChar(Type, i.Type,i.Size) + (i.Index ? " unique primary key" : ""); | ||||
|                 SelectFields_ += i.Name ; | ||||
|                 UpdateFields_ += i.Name + "=?"; | ||||
|                 SelectList_ += "?"; | ||||
|                 first = false; | ||||
|                 Place++; | ||||
|             } | ||||
|             SelectList_ += ")"; | ||||
|  | ||||
|             if(!Indexes.empty()) { | ||||
|                 if(Type==OpenWifi::DBType::sqlite || Type==OpenWifi::DBType::pgsql) { | ||||
|                     for(const auto &j:Indexes) { | ||||
|                         std::string IndexLine; | ||||
|  | ||||
|                         IndexLine = std::string("CREATE INDEX IF NOT EXISTS ") + j.Name + std::string(" ON ") + DBName + " ("; | ||||
|                         bool first_entry=true; | ||||
|                         for(const auto &k:j.Entries) { | ||||
|                             assert(FieldNames_.find(k.FieldName) != FieldNames_.end()); | ||||
|                             if(!first_entry) { | ||||
|                                 IndexLine += " , "; | ||||
|                             } | ||||
|                             first_entry = false; | ||||
|                             IndexLine += k.FieldName + std::string(" ") + std::string(k.Type == Indextype::ASC ? "ASC" : "DESC") ; | ||||
|                         } | ||||
|                         IndexLine += " );"; | ||||
|                         IndexCreation += IndexLine; | ||||
|                     } | ||||
|                 } else if(Type==OpenWifi::DBType::mysql) { | ||||
|                     bool firstIndex = true; | ||||
|                     std::string IndexLine; | ||||
|                     for(const auto &j:Indexes) { | ||||
|                         if(!firstIndex) | ||||
|                             IndexLine += ", "; | ||||
|                         firstIndex = false; | ||||
|                         IndexLine += " INDEX " + j.Name + " ( " ; | ||||
|                         bool first_entry=true; | ||||
|                         for(const auto &k:j.Entries) { | ||||
|                             assert(FieldNames_.find(k.FieldName) != FieldNames_.end()); | ||||
|                             if(!first_entry) { | ||||
|                                 IndexLine += " ,"; | ||||
|                             } | ||||
|                             first_entry = false; | ||||
|                             IndexLine += k.FieldName + std::string(k.Type == Indextype::ASC ? " ASC" : " DESC"); | ||||
|                         } | ||||
|                         IndexLine += " ) "; | ||||
|                     } | ||||
|                     IndexCreation = IndexLine; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] const std::string & CreateFields() const { return CreateFields_; }; | ||||
|         [[nodiscard]] const std::string & SelectFields() const { return SelectFields_; }; | ||||
|         [[nodiscard]] const std::string & SelectList() const { return SelectList_; }; | ||||
|         [[nodiscard]] const std::string & UpdateFields() const { return UpdateFields_; }; | ||||
|  | ||||
|         inline std::string OP(const char *F, SqlComparison O , int V) { | ||||
|             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||
|             return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; | ||||
|         } | ||||
|  | ||||
|         inline std::string OP(const char *F, SqlComparison O , uint64_t V) { | ||||
|             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||
|             return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; | ||||
|         } | ||||
|  | ||||
|         std::string OP(const char *F, SqlComparison O , const std::string & V) { | ||||
|             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||
|             return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; | ||||
|         } | ||||
|  | ||||
|         std::string OP(const char *F, SqlComparison O , const char * V) { | ||||
|             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||
|             return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; | ||||
|         } | ||||
|  | ||||
|         static std::string OP( const std::string &P1, SqlBinaryOp BOP , const std::string &P2) { | ||||
|             return std::string("(")+P1 + BOPS[BOP] + P2 +")"; | ||||
|         } | ||||
|  | ||||
|         std::string OP( bool Paran, const std::string &P1, SqlBinaryOp BOP , const std::string &P2) { | ||||
|             return P1 + BOPS[BOP] + P2 +")"; | ||||
|         } | ||||
|  | ||||
|         template <typename... Others> std::string OP( bool ParanOpen, const std::string &P1, SqlBinaryOp BOP , const std::string &P2, Others... More) { | ||||
|             return P1 + BOPS[BOP] + OP(ParanOpen, P2, More...) + ")"; | ||||
|         } | ||||
|  | ||||
|         template <typename... Others> std::string OP( const std::string &P1, SqlBinaryOp BOP , const std::string &P2, Others... More) { | ||||
|             return std::string{"("} + P1 + BOPS[BOP] + OP(true, P2, More...); | ||||
|         } | ||||
|  | ||||
|         inline bool  Create() { | ||||
|             std::string S; | ||||
|  | ||||
|             if(Type==OpenWifi::DBType::mysql) { | ||||
|                 if(IndexCreation.empty()) | ||||
|                     S = "create table if not exists " + DBName +" ( " + CreateFields_ + " )" ; | ||||
|                 else | ||||
|                     S = "create table if not exists " + DBName +" ( " + CreateFields_ + " ), " + IndexCreation + " )"; | ||||
|             } else if (Type==OpenWifi::DBType::pgsql || Type==OpenWifi::DBType::sqlite) { | ||||
|                 S = "create table if not exists " + DBName + " ( " + CreateFields_ + " ); " + IndexCreation ; | ||||
|             } | ||||
|  | ||||
|             // std::cout << "CREATE-DB: " << S << std::endl; | ||||
|  | ||||
|             try { | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   CreateStatement(Session); | ||||
|  | ||||
|                 CreateStatement << S; | ||||
|                 CreateStatement.execute(); | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 std::cout << "Exception while creating DB: " << E.name() << std::endl; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] std::string ConvertParams(const std::string & S) const { | ||||
|             if(Type!=OpenWifi::DBType::pgsql) | ||||
|                 return S; | ||||
|  | ||||
|             std::string R; | ||||
|             R.reserve(S.size()*2+1); | ||||
|             auto Idx=1; | ||||
|             for(auto const & i:S) | ||||
|             { | ||||
|                 if(i=='?') { | ||||
|                     R += '$'; | ||||
|                     R.append(std::to_string(Idx++)); | ||||
|                 } else { | ||||
|                     R += i; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             return R; | ||||
|         } | ||||
|  | ||||
|         void Convert( RecordTuple &in , RecordType &out); | ||||
|         void Convert( RecordType &in , RecordTuple &out); | ||||
|  | ||||
|         inline const std::string & Prefix() { return Prefix_; }; | ||||
|  | ||||
|         bool CreateRecord( RecordType & R) { | ||||
|             try { | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Insert(Session); | ||||
|  | ||||
|                 RecordTuple RT; | ||||
|                 Convert(R, RT); | ||||
|                 std::string St = "insert into  " + DBName + " ( " + SelectFields_ + " ) values " + SelectList_; | ||||
|                 Insert  << ConvertParams(St) , | ||||
|                     Poco::Data::Keywords::use(RT); | ||||
|                 Insert.execute(); | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         template<typename T> bool GetRecord( const char * FieldName, T Value,  RecordType & R) { | ||||
|             try { | ||||
|  | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Select(Session); | ||||
|                 RecordTuple             RT; | ||||
|  | ||||
|                 std::string St = "select " + SelectFields_ + " from " + DBName + " where " + FieldName + "=?" ; | ||||
|  | ||||
|                 Select  << ConvertParams(St) , | ||||
|                     Poco::Data::Keywords::into(RT), | ||||
|                     Poco::Data::Keywords::use(Value); | ||||
|                 if(Select.execute()==1) { | ||||
|                     Convert(RT,R); | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         typedef std::vector<std::string> StringVec; | ||||
|  | ||||
|         template <  typename T, | ||||
|                 typename T0, typename T1> bool GR(const char *FieldName, T & R,T0 &V0, T1 &V1) { | ||||
|             try { | ||||
|  | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Select(Session); | ||||
|                 RecordTuple RT; | ||||
|  | ||||
|                 std::string St = "select " + SelectFields_ + " from " + DBName | ||||
|                                 + " where " + FieldName[0] + "=? and " + FieldName[1] + "=?"  ; | ||||
|                 Select  << ConvertParams(St) , | ||||
|                     Poco::Data::Keywords::into(RT), | ||||
|                     Poco::Data::Keywords::use(V0), | ||||
|                     Poco::Data::Keywords::use(V1); | ||||
|  | ||||
|                 if(Select.execute()==1) { | ||||
|                     Convert(RT,R); | ||||
|                     return true; | ||||
|                 } | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         typedef std::vector<RecordTuple> RecordList; | ||||
|  | ||||
|         bool GetRecords( uint64_t Offset, uint64_t HowMany, std::vector<RecordType> & Records, const std::string & Where = "", const std::string & OrderBy = "") { | ||||
|             try { | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Select(Session); | ||||
|                 RecordList RL; | ||||
|                 std::string St = "select " + SelectFields_ + " from " + DBName + | ||||
|                         (Where.empty() ? "" : " where " + Where) + OrderBy + | ||||
|                         ComputeRange(Offset, HowMany) ; | ||||
|  | ||||
|                 Select  << St , | ||||
|                     Poco::Data::Keywords::into(RL); | ||||
|  | ||||
|                 if(Select.execute()>0) { | ||||
|                     for(auto &i:RL) { | ||||
|                         RecordType  R; | ||||
|                         Convert(i, R); | ||||
|                         Records.template emplace_back(R); | ||||
|                     } | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         template <typename T> bool UpdateRecord( const char *FieldName, T & Value,  RecordType & R) { | ||||
|             try { | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Update(Session); | ||||
|  | ||||
|                 RecordTuple RT; | ||||
|  | ||||
|                 Convert(R, RT); | ||||
|  | ||||
|                 std::string St = "update " + DBName + " set " + UpdateFields_ + " where " + FieldName + "=?" ; | ||||
|                 Update  << ConvertParams(St) , | ||||
|                     Poco::Data::Keywords::use(RT), | ||||
|                     Poco::Data::Keywords::use(Value); | ||||
|                 Update.execute(); | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         template <typename T> bool GetNameAndDescription(const char *FieldName, T & Value, std::string & Name, std::string & Description ) { | ||||
|             try { | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Select(Session); | ||||
|                 RecordTuple             RT; | ||||
|  | ||||
|                 std::string St = "select " + SelectFields_ + " from " + DBName + " where " + FieldName + "=?" ; | ||||
|                 RecordType R; | ||||
|                 Select  << ConvertParams(St) , | ||||
|                 Poco::Data::Keywords::into(RT), | ||||
|                 Poco::Data::Keywords::use(Value); | ||||
|                 if(Select.execute()==1) { | ||||
|                     Convert(RT,R); | ||||
|                     Name = R.info.name; | ||||
|                     Description = R.info.description; | ||||
|                     return true; | ||||
|                 } | ||||
|                 return false; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         template <typename T> bool DeleteRecord( const char *FieldName, T Value) { | ||||
|             try { | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Delete(Session); | ||||
|  | ||||
|                 std::string St = "delete from " + DBName + " where " + FieldName + "=?" ; | ||||
|                 Delete  << ConvertParams(St) , | ||||
|                     Poco::Data::Keywords::use(Value); | ||||
|                 Delete.execute(); | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         bool DeleteRecords( const std::string & WhereClause ) { | ||||
|             try { | ||||
|                 assert( !WhereClause.empty()); | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Delete(Session); | ||||
|  | ||||
|                 std::string St = "delete from " + DBName + " where " + WhereClause; | ||||
|                 Delete  << St; | ||||
|                 Delete.execute(); | ||||
|                 return true; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         bool Exists(const char *FieldName, std::string & Value) { | ||||
|             try { | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 RecordType  R; | ||||
|                 if(GetRecord(FieldName,Value,R)) | ||||
|                     return true; | ||||
|                 return false; | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         bool Iterate( std::function<bool(const RecordType &R)> F) { | ||||
|             try { | ||||
|  | ||||
|                 uint64_t    Offset=1; | ||||
|                 uint64_t    Batch=50; | ||||
|                 bool Done=false; | ||||
|                 while(!Done) { | ||||
|                     std::vector<RecordType> Records; | ||||
|                     if(GetRecords(Offset,Batch,Records)) { | ||||
|                         for(const auto &i:Records) { | ||||
|                             if(!F(i)) | ||||
|                                 return true; | ||||
|                         } | ||||
|                         if(Records.size()<Batch) | ||||
|                             return true; | ||||
|                         Offset += Batch; | ||||
|                     } else { | ||||
|                         Done=true; | ||||
|                     } | ||||
|                 } | ||||
|                 return true; | ||||
|             } catch(const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         bool PrepareOrderBy(const std::string &OrderByList, std::string &OrderByString) { | ||||
|             auto items = Poco::StringTokenizer(OrderByList,","); | ||||
|             std::string ItemList; | ||||
|  | ||||
|             for(const auto &i:items) { | ||||
|                 auto T = Poco::StringTokenizer(i,":"); | ||||
|                 if(T.count()!=2) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if(T[1]!="a" && T[1]!="d") { | ||||
|                     return false; | ||||
|                 } | ||||
|                 if(!ItemList.empty()) | ||||
|                     ItemList += " , "; | ||||
|                 auto hint = FieldNames_.find(T[0]); | ||||
|                 if(hint==FieldNames_.end()) { | ||||
|                     return false; | ||||
|                 } | ||||
|                 ItemList += T[0] + (T[1]=="a" ? " ASC" : " DESC"); | ||||
|             } | ||||
|  | ||||
|             if(!ItemList.empty()) { | ||||
|                 OrderByString = " ORDER BY " + ItemList; | ||||
|             } | ||||
|  | ||||
|             std::cout << OrderByString << std::endl; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         uint64_t Count( const std::string & Where="" ) { | ||||
|             try { | ||||
|                 uint64_t Cnt=0; | ||||
|  | ||||
|                 Poco::Data::Session     Session = Pool_.get(); | ||||
|                 Poco::Data::Statement   Select(Session); | ||||
|  | ||||
|                 std::string st{"SELECT COUNT(*) FROM " + DBName + " " + (Where.empty() ? "" : (" where " + Where)) }; | ||||
|  | ||||
|                 Select << st , | ||||
|                     Poco::Data::Keywords::into(Cnt); | ||||
|                 Select.execute(); | ||||
|  | ||||
|                 return Cnt; | ||||
|  | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         template <typename X> bool ManipulateVectorMember( X T, const char *FieldName, std::string & ParentUUID, std::string & ChildUUID, bool Add) { | ||||
|             try { | ||||
|                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||
|  | ||||
|                 RecordType R; | ||||
|                 if(GetRecord(FieldName, ParentUUID, R)) { | ||||
|                     auto it = std::find((R.*T).begin(),(R.*T).end(),ChildUUID); | ||||
|                     if(Add) { | ||||
|                         if(it!=(R.*T).end() && *it == ChildUUID) | ||||
|                             return false; | ||||
|                         (R.*T).push_back(ChildUUID); | ||||
|                         std::sort((R.*T).begin(),(R.*T).end()); | ||||
|                     } else { | ||||
|                         if(it!=(R.*T).end() && *it == ChildUUID) | ||||
|                             (R.*T).erase(it); | ||||
|                         else | ||||
|                             return false; | ||||
|                     } | ||||
|                     UpdateRecord(FieldName, ParentUUID, R); | ||||
|                     return true; | ||||
|                 } | ||||
|             } catch (const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         inline bool AddChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DelUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { | ||||
|             std::string FakeUUID{ Prefix + ":" + ChildUUID}; | ||||
|             return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { | ||||
|             std::string FakeUUID{ Prefix + ":" + ChildUUID}; | ||||
|             return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool DeleteLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, false); | ||||
|         } | ||||
|  | ||||
|         inline bool AddLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||
|             return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, true); | ||||
|         } | ||||
|  | ||||
|         inline bool GetInUse(const char *FieldName, std::string & UUID, std::vector<std::string> & UUIDs ) { | ||||
|             RecordType  R; | ||||
|             if(GetRecord(FieldName,UUID,R)) { | ||||
|                 UUIDs = R.inUse; | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||
|             if(From<1) From=1; | ||||
|             switch(Type) { | ||||
|                 case OpenWifi::DBType::sqlite: | ||||
|                     return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) +  " "; | ||||
|                 case OpenWifi::DBType::pgsql: | ||||
|                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|                 case OpenWifi::DBType::mysql: | ||||
|                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|                 default: | ||||
|                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Poco::Logger & Logger() { return Logger_; } | ||||
|  | ||||
|     private: | ||||
|         OpenWifi::DBType            Type; | ||||
|         std::string                 DBName; | ||||
|         std::string                 CreateFields_; | ||||
|         std::string                 SelectFields_; | ||||
|         std::string                 SelectList_; | ||||
|         std::string                 UpdateFields_; | ||||
|         std::string                 IndexCreation; | ||||
|         std::map<std::string,int>   FieldNames_; | ||||
|         Poco::Data::SessionPool     &Pool_; | ||||
|         Poco::Logger                &Logger_; | ||||
|         std::string                 Prefix_; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #endif | ||||
| @@ -105,7 +105,7 @@ namespace OpenWifi::uCentralProtocol { | ||||
| 			ET_TELEMETRY | ||||
| 		}; | ||||
| 
 | ||||
| 	static EVENT_MSG EventFromString(const std::string & Method) { | ||||
| 	inline static EVENT_MSG EventFromString(const std::string & Method) { | ||||
| 		if (!Poco::icompare(Method, CONNECT)) { | ||||
| 			return ET_CONNECT; | ||||
| 		} else if (!Poco::icompare(Method, STATE)) { | ||||
							
								
								
									
										13
									
								
								src/ow_version.h.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/ow_version.h.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-12-06. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace OW_VERSION { | ||||
|     inline static const std::string VERSION{"@CMAKE_PROJECT_VERSION@"}; | ||||
|     inline static const std::string BUILD{"@BUILD_NUM@"}; | ||||
|     inline static const std::string HASH{"@GIT_HASH@"}; | ||||
| } | ||||
| @@ -4,7 +4,6 @@ | ||||
| 
 | ||||
| #include "storage_deviceInfo.h" | ||||
| #include "StorageService.h" | ||||
| #include "Utils.h" | ||||
| #include "Poco/Data/RecordSet.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
| 
 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user