mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralfms.git
				synced 2025-10-30 02:12:22 +00:00 
			
		
		
		
	Compare commits
	
		
			79 Commits
		
	
	
		
			v2.7.0
			...
			v2.8.0-RC2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 7760230f02 | ||
|   | 6223ade0f6 | ||
|   | 64e6a6f70a | ||
|   | c67367ec4d | ||
|   | fe3c5d7c4a | ||
|   | da52cf5823 | ||
|   | 3c8d8697e0 | ||
|   | 7b0c61ff5e | ||
|   | e9a9416332 | ||
|   | 2255988b4c | ||
|   | 106c9353fc | ||
|   | 6c7fc8e310 | ||
|   | e151b45d51 | ||
|   | 1584ff1ebe | ||
|   | 82ccb08d6b | ||
|   | 82b184a79f | ||
|   | 71c37edde7 | ||
|   | 0c8fa44935 | ||
|   | 32714f95b6 | ||
|   | 13cbf8c603 | ||
|   | 48c95c3101 | ||
|   | 857055750f | ||
|   | 96b22ef09b | ||
|   | acff9afff2 | ||
|   | 1e250be2cd | ||
|   | 93a8624645 | ||
|   | b9bf57e1f2 | ||
|   | f203622299 | ||
|   | 698282658c | ||
|   | 5baacac9bf | ||
|   | 8ca4531c99 | ||
|   | a6e0cd453b | ||
|   | 5e39f63fd2 | ||
|   | 9b081c6924 | ||
|   | be2150d845 | ||
|   | 2d6f1879f5 | ||
|   | e870a381a8 | ||
|   | 0cc0d0204e | ||
|   | 498e55f933 | ||
|   | a9b3cb9821 | ||
|   | 29736da681 | ||
|   | 28f890002b | ||
|   | 0ec0a4fd95 | ||
|   | 6dce398ddc | ||
|   | eca0e8883a | ||
|   | 43cc667a59 | ||
|   | a084230cbd | ||
|   | 59c5ed51c0 | ||
|   | 1128fd3c23 | ||
|   | ee4f1fac93 | ||
|   | a1a600de77 | ||
|   | 9d64c2dabd | ||
|   | 4f6c9728ef | ||
|   | 167933e422 | ||
|   | f661bb579a | ||
|   | 1e14eb1611 | ||
|   | dae57a7492 | ||
|   | d30014372b | ||
|   | a9f37ae30d | ||
|   | 23bc11f26e | ||
|   | caa25f0a1e | ||
|   | b86e80be6d | ||
|   | 880a14b651 | ||
|   | fa5d1b982a | ||
|   | e803885d24 | ||
|   | 17b7e1de59 | ||
|   | 15d7ae635c | ||
|   | 244423132e | ||
|   | ad149a8e40 | ||
|   | 59020f8809 | ||
|   | 4c5dbea4fb | ||
|   | 8fa42ab75d | ||
|   | ad1d7301a1 | ||
|   | 6ad2f0ba1b | ||
|   | aad7cf752a | ||
|   | ff8fa5e625 | ||
|   | 6e38083912 | ||
|   | 98509fdab7 | ||
|   | 2bbaf44e88 | 
							
								
								
									
										10
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -27,7 +27,7 @@ jobs: | ||||
|       DOCKER_REGISTRY_USERNAME: ucentral | ||||
|     steps: | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
| @@ -58,11 +58,11 @@ jobs: | ||||
|     - name: Get base branch name and set as output | ||||
|       id: get_base_branch | ||||
|       run: | | ||||
|         echo ::set-output name=branch::$(echo ${GITHUB_BASE_REF##*/}) | ||||
|         echo ::set-output name=owgw_branch::$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g') | ||||
|         echo "branch=$(echo ${GITHUB_BASE_REF##*/})" >> $GITHUB_OUTPUT | ||||
|         echo "owgw_branch=$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g')" >> $GITHUB_OUTPUT | ||||
|  | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
| @@ -87,7 +87,7 @@ jobs: | ||||
|       - docker | ||||
|     steps: | ||||
|     - name: Checkout actions repo | ||||
|       uses: actions/checkout@v2 | ||||
|       uses: actions/checkout@v3 | ||||
|       with: | ||||
|         repository: Telecominfraproject/.github | ||||
|         path: github | ||||
|   | ||||
							
								
								
									
										41
									
								
								.github/workflows/openapi-pages.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								.github/workflows/openapi-pages.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| name: Update OpenAPI docs on GitHub Pages | ||||
|  | ||||
| on: | ||||
|   push: | ||||
|     paths: | ||||
|       - 'openapi/**' | ||||
|     branches: | ||||
|       - main | ||||
|   workflow_dispatch: | ||||
|  | ||||
| defaults: | ||||
|   run: | ||||
|     shell: bash | ||||
|  | ||||
| jobs: | ||||
|   docsgen: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|       - uses: actions/checkout@v3 | ||||
|  | ||||
|       - name: Generate static HTML page with docs from OpenAPI definition | ||||
|         run: | | ||||
|           docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v6.2.1 generate -i https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralfms/main/openapi/owfms.yaml -g html2 --skip-validate-spec -o /local/ | ||||
|  | ||||
|       - name: Update OpenAPI docs | ||||
|         run: | | ||||
|           mkdir tmp-docs | ||||
|           mv index.html tmp-docs/index.html | ||||
|           mkdir -p ~/.ssh | ||||
|           ssh-keyscan -H github.com >> ~/.ssh/known_hosts | ||||
|           echo https://tip-automation:${{ secrets.GIT_PUSH_PAT }}@github.com > ~/.git-credentials | ||||
|           git config --global credential.helper store | ||||
|           git config --global user.email "tip-automation@telecominfraproject.com" | ||||
|           git config --global user.name "TIP Automation User" | ||||
|           git pull | ||||
|           git checkout gh-pages || git checkout -b gh-pages | ||||
|           rm -rf docs | ||||
|           mv tmp-docs docs | ||||
|           git add docs | ||||
|           git commit -m'Update OpenAPI docs for GitHub pages' | ||||
|           git push --set-upstream origin gh-pages | ||||
| @@ -1,5 +1,5 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(owfms VERSION 2.7.0) | ||||
| project(owfms VERSION 2.8.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
|  | ||||
| @@ -27,12 +27,12 @@ endif() | ||||
|  | ||||
| find_package(Git QUIET) | ||||
| if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") | ||||
|     execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags | ||||
|     execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD | ||||
|             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}") | ||||
|         message(FATAL_ERROR "git rev-parse --short HEAD failed with ${GIT_RESULT}") | ||||
|     endif() | ||||
|     string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}") | ||||
| endif() | ||||
| @@ -42,8 +42,10 @@ endif() | ||||
|  | ||||
| find_package(OpenSSL    REQUIRED) | ||||
| find_package(fmt        REQUIRED) | ||||
| find_package(ZLIB       REQUIRED) | ||||
| find_package(Poco       REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite) | ||||
| find_package(AWSSDK     REQUIRED COMPONENTS s3) | ||||
| find_package(ZLIB       REQUIRED) | ||||
|  | ||||
| if(SMALL_BUILD) | ||||
|     find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite) | ||||
| @@ -75,14 +77,54 @@ add_executable( owfms | ||||
|         src/framework/OpenWifiTypes.h | ||||
|         src/framework/orm.h | ||||
|         src/framework/StorageClass.h | ||||
|         src/framework/ow_constants.h | ||||
|         src/framework/MicroServiceErrorHandler.h | ||||
|         src/framework/WebSocketClientNotifications.h | ||||
|         src/framework/MicroServiceErrorHandler.h | ||||
|         src/framework/UI_WebSocketClientServer.cpp | ||||
|         src/framework/UI_WebSocketClientServer.h | ||||
|         src/framework/UI_WebSocketClientNotifications.cpp | ||||
|         src/framework/UI_WebSocketClientNotifications.h | ||||
|         src/framework/utils.h | ||||
|         src/framework/utils.cpp | ||||
|         src/framework/AppServiceRegistry.h | ||||
|         src/framework/SubSystemServer.cpp | ||||
|         src/framework/SubSystemServer.h | ||||
|         src/framework/RESTAPI_utils.h | ||||
|         src/framework/AuthClient.cpp | ||||
|         src/framework/AuthClient.h | ||||
|         src/framework/MicroServiceNames.h | ||||
|         src/framework/MicroServiceFuncs.h | ||||
|         src/framework/OpenAPIRequests.cpp | ||||
|         src/framework/OpenAPIRequests.h | ||||
|         src/framework/MicroServiceFuncs.cpp | ||||
|         src/framework/ALBserver.cpp | ||||
|         src/framework/ALBserver.h | ||||
|         src/framework/KafkaManager.cpp | ||||
|         src/framework/KafkaManager.h | ||||
|         src/framework/RESTAPI_RateLimiter.h | ||||
|         src/framework/WebSocketLogger.h | ||||
|         src/framework/RESTAPI_GenericServerAccounting.h | ||||
|         src/framework/RESTAPI_SystemConfiguration.h | ||||
|         src/framework/CIDR.h | ||||
|         src/framework/RESTAPI_Handler.cpp | ||||
|         src/framework/RESTAPI_Handler.h | ||||
|         src/framework/RESTAPI_ExtServer.h | ||||
|         src/framework/RESTAPI_ExtServer.cpp | ||||
|         src/framework/RESTAPI_IntServer.cpp | ||||
|         src/framework/RESTAPI_IntServer.h | ||||
|         src/framework/RESTAPI_SystemCommand.h | ||||
|         src/framework/RESTAPI_WebSocketServer.h | ||||
|         src/framework/EventBusManager.cpp | ||||
|         src/framework/EventBusManager.h | ||||
|         src/framework/RESTAPI_PartHandler.h | ||||
|         src/framework/MicroService.cpp | ||||
|         src/framework/MicroServiceExtra.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/RESTObjects/RESTAPI_CertObjects.cpp src/RESTObjects/RESTAPI_CertObjects.h | ||||
|         src/RESTObjects/RESTAPI_OWLSobjects.cpp src/RESTObjects/RESTAPI_OWLSobjects.h | ||||
|         src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h | ||||
|         src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h | ||||
|         src/RESTObjects/RESTAPI_SubObjects.cpp src/RESTObjects/RESTAPI_SubObjects.h | ||||
|         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 | ||||
| @@ -95,7 +137,6 @@ add_executable( owfms | ||||
|         src/Daemon.cpp src/Daemon.h | ||||
|         src/StorageService.cpp src/StorageService.h | ||||
|         src/ManifestCreator.cpp src/ManifestCreator.h | ||||
|         src/framework/MicroService.h | ||||
|         src/NewConnectionHandler.cpp src/NewConnectionHandler.h | ||||
|         src/LatestFirmwareCache.cpp src/LatestFirmwareCache.h | ||||
|         src/DeviceCache.cpp src/DeviceCache.h | ||||
| @@ -105,7 +146,9 @@ add_executable( owfms | ||||
|         src/NewCommandHandler.cpp src/NewCommandHandler.h | ||||
|         src/storage/orm_history.cpp src/storage/orm_history.h | ||||
|         src/storage/orm_firmwares.cpp src/storage/orm_firmwares.h | ||||
|         src/storage/orm_deviceInfo.cpp src/storage/orm_deviceInfo.h src/RESTAPI/RESTAPI_deviceInformation_handler.cpp src/RESTAPI/RESTAPI_deviceInformation_handler.h) | ||||
|         src/storage/orm_deviceInfo.cpp src/storage/orm_deviceInfo.h | ||||
|         src/RESTAPI/RESTAPI_deviceInformation_handler.cpp | ||||
|         src/RESTAPI/RESTAPI_deviceInformation_handler.h) | ||||
|  | ||||
| target_link_libraries( owfms PUBLIC | ||||
|         ${Poco_LIBRARIES} | ||||
|   | ||||
							
								
								
									
										85
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										85
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,17 +1,14 @@ | ||||
| ARG DEBIAN_VERSION=11.4-slim | ||||
| ARG POCO_VERSION=poco-tip-v1 | ||||
| ARG FMTLIB_VERSION=9.0.0 | ||||
| ARG DEBIAN_VERSION=11.5-slim | ||||
| ARG POCO_VERSION=poco-tip-v2 | ||||
| ARG CPPKAFKA_VERSION=tip-v1 | ||||
| ARG JSON_VALIDATOR_VERSION=2.1.0 | ||||
| ARG AWS_SDK_VERSION=1.9.315 | ||||
|  | ||||
| FROM debian:$DEBIAN_VERSION AS build-base | ||||
|  | ||||
| RUN apt-get update && apt-get install --no-install-recommends -y \ | ||||
|     make cmake g++ git \ | ||||
|     make cmake g++ git curl zip unzip pkg-config \ | ||||
|     libpq-dev libmariadb-dev libmariadbclient-dev-compat \ | ||||
|     librdkafka-dev libboost-all-dev libssl-dev \ | ||||
|     zlib1g-dev nlohmann-json3-dev ca-certificates libcurl4-openssl-dev | ||||
|     zlib1g-dev ca-certificates libcurl4-openssl-dev libfmt-dev | ||||
|  | ||||
| FROM build-base AS poco-build | ||||
|  | ||||
| @@ -27,20 +24,6 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS fmtlib-build | ||||
|  | ||||
| ARG FMTLIB_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/fmtlib/fmt/git/refs/tags/${FMTLIB_VERSION} version.json | ||||
| RUN git clone https://github.com/fmtlib/fmt --branch ${FMTLIB_VERSION} /fmtlib | ||||
|  | ||||
| WORKDIR /fmtlib | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| FROM build-base AS cppkafka-build | ||||
|  | ||||
| ARG CPPKAFKA_VERSION | ||||
| @@ -55,62 +38,30 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS json-schema-validator-build | ||||
|  | ||||
| ARG JSON_VALIDATOR_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json | ||||
| RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator | ||||
|  | ||||
| WORKDIR /json-schema-validator | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| FROM build-base AS aws-sdk-cpp-build | ||||
|  | ||||
| ARG AWS_SDK_VERSION | ||||
|  | ||||
| ADD https://api.github.com/repos/aws/aws-sdk-cpp/git/refs/tags/${AWS_SDK_VERSION} version.json | ||||
| RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp --branch ${AWS_SDK_VERSION} /aws-sdk-cpp | ||||
|  | ||||
| WORKDIR /aws-sdk-cpp | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. -DBUILD_ONLY="sns;s3" \ | ||||
|              -DCMAKE_BUILD_TYPE=Release \ | ||||
|              -DUSE_OPENSSL=ON \ | ||||
|              -DCPP_STANDARD=17 \ | ||||
|              -DBUILD_SHARED_LIBS=ON \ | ||||
|              -DCMAKE_CXX_FLAGS="-Wno-error=stringop-overflow -Wno-error=uninitialized" \ | ||||
|              -DAUTORUN_UNIT_TESTS=OFF | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| FROM build-base AS owfms-build | ||||
|  | ||||
| ADD CMakeLists.txt build /owfms/ | ||||
| ADD overlays /owfms/overlays | ||||
| ADD cmake /owfms/cmake | ||||
| ADD src /owfms/src | ||||
| ADD .git /owfms/.git | ||||
| ARG VCPKG_VERSION=2022.11.14 | ||||
| RUN git clone --depth 1 --branch ${VCPKG_VERSION} https://github.com/microsoft/vcpkg && \ | ||||
|     ./vcpkg/bootstrap-vcpkg.sh && \ | ||||
|     mkdir /vcpkg/custom-triplets && \ | ||||
|     cp /vcpkg/triplets/x64-linux.cmake /vcpkg/custom-triplets/x64-linux.cmake && \ | ||||
|     sed -i 's/set(VCPKG_LIBRARY.*/set(VCPKG_LIBRARY_LINKAGE dynamic)/g' /vcpkg/custom-triplets/x64-linux.cmake && \ | ||||
|     ./vcpkg/vcpkg install aws-sdk-cpp[s3]:x64-linux json-schema-validator:x64-linux --overlay-triplets=/vcpkg/custom-triplets --overlay-ports=/owfms/overlays | ||||
|  | ||||
| COPY --from=poco-build /usr/local/include /usr/local/include | ||||
| COPY --from=poco-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=cppkafka-build /usr/local/include /usr/local/include | ||||
| COPY --from=cppkafka-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=json-schema-validator-build /usr/local/include /usr/local/include | ||||
| COPY --from=json-schema-validator-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /usr/local/include /usr/local/include | ||||
| COPY --from=aws-sdk-cpp-build /usr/local/lib /usr/local/lib | ||||
| COPY --from=fmtlib-build /usr/local/include /usr/local/include | ||||
| COPY --from=fmtlib-build /usr/local/lib /usr/local/lib | ||||
|  | ||||
| WORKDIR /owfms | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR /owfms/cmake-build | ||||
| RUN cmake .. | ||||
| RUN cmake -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
|  | ||||
| FROM debian:$DEBIAN_VERSION | ||||
| @@ -127,7 +78,7 @@ RUN mkdir -p "$OWFMS_ROOT" "$OWFMS_CONFIG" && \ | ||||
|  | ||||
| RUN apt-get update && apt-get install --no-install-recommends -y \ | ||||
|     librdkafka++1 gosu gettext ca-certificates bash jq curl wget \ | ||||
|     libmariadb-dev-compat libpq5 unixodbc postgresql-client | ||||
|     libmariadb-dev-compat libpq5 postgresql-client libfmt7 sqlite3 | ||||
|  | ||||
| COPY readiness_check /readiness_check | ||||
| COPY test_scripts/curl/cli /cli | ||||
| @@ -139,11 +90,9 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr | ||||
|     -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt | ||||
|  | ||||
| COPY --from=owfms-build /owfms/cmake-build/owfms /openwifi/owfms | ||||
| COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/* /usr/local/lib | ||||
| COPY --from=poco-build /poco/cmake-build/lib/* /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-core/libaws-cpp-sdk-core.so /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-s3/libaws-cpp-sdk-s3.so /usr/local/lib | ||||
| COPY --from=aws-sdk-cpp-build /aws-sdk-cpp/cmake-build/aws-cpp-sdk-sns/libaws-cpp-sdk-sns.so /usr/local/lib | ||||
| COPY --from=owfms-build /vcpkg/installed/x64-linux/lib/ /usr/local/lib/ | ||||
| COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/ /usr/local/lib/ | ||||
| COPY --from=poco-build /poco/cmake-build/lib/ /usr/local/lib/ | ||||
|  | ||||
| RUN ldconfig | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,11 @@ The uCentralFMS is a micro-service part of the OpenWiFi ecosystem. uCentralFMS i | ||||
| to facilitate the task of upgrade and maintaining the proper firmware for all the devices  | ||||
| used in your OpenWiFi solution. You may either [build it](#building) or use the [Docker version](#docker). | ||||
|  | ||||
| ## OpenAPI | ||||
| You may get static page with OpenAPI docs generated from the definition on [GitHub Page](https://telecominfraproject.github.io/wlan-cloud-ucentralfms/). | ||||
|  | ||||
| Also you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentralfms/main/openapi/owfms.yaml)) to get interactive docs page. | ||||
|  | ||||
| ## Building | ||||
| In order to build the uCentralFMS, you will need to install its dependencies, which includes the following: | ||||
| - cmake | ||||
| @@ -216,4 +221,4 @@ s3.key =  ******************************************* | ||||
| s3.retry = 60 | ||||
| s3.bucket.uri = ucentral-ap-firmware.s3.amazonaws.com | ||||
|  | ||||
| ``` | ||||
| ``` | ||||
|   | ||||
| @@ -23,6 +23,8 @@ if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then | ||||
|   SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16004"} \ | ||||
|   SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \ | ||||
|   SECURITY_RESTAPI_DISABLE=${SECURITY_RESTAPI_DISABLE:-"false"} \ | ||||
|   FIRMWAREDB_REFRESH=${FIRMWAREDB_REFRESH:-"86400"} \ | ||||
|   FIRMWAREDB_MAXAGE=${FIRMWAREDB_MAXAGE:-"90"} \ | ||||
|   S3_BUCKETNAME=${S3_BUCKETNAME:-"ucentral-ap-firmware"} \ | ||||
|   S3_REGION=${S3_REGION:-"us-east-1"} \ | ||||
|   S3_SECRET=${S3_SECRET:-"*******************************************"} \ | ||||
|   | ||||
| @@ -9,7 +9,7 @@ fullnameOverride: "" | ||||
| images: | ||||
|   owfms: | ||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owfms | ||||
|     tag: main | ||||
|     tag: v2.8.0-RC2 | ||||
|     pullPolicy: Always | ||||
| #    regcred: | ||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||
| @@ -147,7 +147,7 @@ configProperties: | ||||
|   s3.region: us-east-1 | ||||
|   s3.retry: 60 | ||||
|   s3.bucket.uri: ucentral-ap-firmware.s3.amazonaws.com | ||||
|   firmwaredb.refresh: 1800 | ||||
|   firmwaredb.refresh: 86400 | ||||
|   # ALB | ||||
|   alb.enable: "true" | ||||
|   alb.port: 16104 | ||||
|   | ||||
							
								
								
									
										1
									
								
								overlays/curl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								overlays/curl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/curl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								overlays/curl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "curl", | ||||
|     "version-string": "7.74.0-1.3+deb11u3" | ||||
| } | ||||
							
								
								
									
										1
									
								
								overlays/openssl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								overlays/openssl/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/openssl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								overlays/openssl/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "openssl", | ||||
|     "version-string": "1.1.1n-0+deb11u3" | ||||
| } | ||||
							
								
								
									
										1
									
								
								overlays/zlib/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								overlays/zlib/portfile.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| set(VCPKG_POLICY_EMPTY_PACKAGE enabled) | ||||
							
								
								
									
										4
									
								
								overlays/zlib/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								overlays/zlib/vcpkg.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| { | ||||
|     "name": "zlib", | ||||
|     "version-string": "1:1.2.11.dfsg-2+deb11u2" | ||||
| } | ||||
| @@ -38,8 +38,8 @@ openwifi.system.commandchannel = /tmp/app.ucentralfms | ||||
| openwifi.system.uri.ui = ${SYSTEM_URI_UI} | ||||
| openwifi.security.restapi.disable = ${SECURITY_RESTAPI_DISABLE} | ||||
|  | ||||
| firmwaredb.refresh = 1800 | ||||
| firmwaredb.maxage = 90 | ||||
| firmwaredb.refresh = ${FIRMWAREDB_REFRESH} | ||||
| firmwaredb.maxage = ${FIRMWAREDB_MAXAGE} | ||||
|  | ||||
| # | ||||
| # Firmware Microservice Specific Section | ||||
|   | ||||
| @@ -8,14 +8,19 @@ | ||||
| #include "LatestFirmwareCache.h" | ||||
| #include "StorageService.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     int AutoUpdater::Start() { | ||||
|         poco_information(Logger(),"Starting..."); | ||||
|         AutoUpdaterEnabled_ = MicroService::instance().ConfigGetBool("autoupdater.enabled", false); | ||||
|         AutoUpdaterEnabled_ = MicroServiceConfigGetBool("autoupdater.enabled", false); | ||||
|         if(AutoUpdaterEnabled_) { | ||||
|             Running_ = false; | ||||
|             AutoUpdaterFrequency_ = MicroService::instance().ConfigGetInt("autoupdater.frequency",600); | ||||
|             AutoUpdaterFrequency_ = MicroServiceConfigGetInt("autoupdater.frequency",600); | ||||
|             AutoUpdaterCallBack_ = std::make_unique<Poco::TimerCallback<AutoUpdater>>(*this, &AutoUpdater::onTimer); | ||||
|             Timer_.setStartInterval(5 * 60 * 1000);  // first run in 5 minutes | ||||
|             Timer_.setPeriodicInterval(AutoUpdaterFrequency_ * 1000); | ||||
| @@ -50,7 +55,7 @@ namespace OpenWifi { | ||||
|             try { | ||||
|                 poco_debug(Logger(),fmt::format("Preparing to upgrade {}",Entry.first)); | ||||
|                 auto CacheEntry = Cache_.find(Entry.first); | ||||
|                 uint64_t now = OpenWifi::Now(); | ||||
|                 uint64_t now = Utils::Now(); | ||||
|                 std::string firmwareUpgrade; | ||||
|                 if(CacheEntry == Cache_.end() || (CacheEntry->second.LastCheck-now)>300) { | ||||
|                     //  get the firmware settings for that device. | ||||
|   | ||||
| @@ -2,11 +2,11 @@ | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_AUTOUPDATER_H | ||||
| #define OWFMS_AUTOUPDATER_H | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include <deque> | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "Poco/Util/Application.h" | ||||
| #include "Poco/Timer.h" | ||||
|  | ||||
| @@ -55,4 +55,3 @@ namespace OpenWifi { | ||||
|     inline auto AutoUpdater() { return AutoUpdater::instance(); } | ||||
| } | ||||
|  | ||||
| #endif //OWFMS_AUTOUPDATER_H | ||||
|   | ||||
| @@ -19,6 +19,8 @@ | ||||
| #include "AutoUpdater.h" | ||||
| #include "NewCommandHandler.h" | ||||
|  | ||||
| #include "framework/UI_WebSocketClientServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class Daemon *Daemon::instance_ = nullptr; | ||||
|  | ||||
| @@ -37,7 +39,8 @@ namespace OpenWifi { | ||||
|                                             NewConnectionHandler(), | ||||
|                                             ManifestCreator(), | ||||
|                                             AutoUpdater(), | ||||
|                                             NewCommandHandler() | ||||
|                                             NewCommandHandler(), | ||||
|                                             UI_WebSocketClientServer() | ||||
|                                    }); | ||||
|         } | ||||
|         return instance_; | ||||
| @@ -45,6 +48,11 @@ namespace OpenWifi { | ||||
|  | ||||
|     void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) { | ||||
|     } | ||||
|  | ||||
|     void DaemonPostInitialization(Poco::Util::Application &self) { | ||||
|         Daemon()->PostInitialization(self); | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
| int main(int argc, char **argv) { | ||||
|   | ||||
| @@ -7,6 +7,8 @@ | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "framework/MicroServiceNames.h" | ||||
|  | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "Dashboard.h" | ||||
|  | ||||
| @@ -30,9 +32,7 @@ namespace OpenWifi { | ||||
|  | ||||
|         void PostInitialization(Poco::Util::Application &self); | ||||
|         static Daemon *instance(); | ||||
|         inline void ResetDashboard() { DB_.Reset(); } | ||||
|         inline void CreateDashboard() { DB_.Create(); } | ||||
|         inline const FMSObjects::DeviceReport & GetDashboard() { return DB_.Report(); } | ||||
|         inline DeviceDashboard & GetDashboard() { return DB_; } | ||||
|  | ||||
|     private: | ||||
|         static Daemon 				*instance_; | ||||
| @@ -40,9 +40,6 @@ namespace OpenWifi { | ||||
|     }; | ||||
|  | ||||
|     inline Daemon * Daemon() { return Daemon::instance(); } | ||||
|     inline void DaemonPostInitialization(Poco::Util::Application &self) { | ||||
|         Daemon()->PostInitialization(self); | ||||
|     } | ||||
| } | ||||
|  | ||||
| #endif //UCENTRALFWS_DAEMON_H | ||||
|   | ||||
| @@ -4,15 +4,48 @@ | ||||
|  | ||||
| #include "Dashboard.h" | ||||
| #include "StorageService.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	void DeviceDashboard::Create() { | ||||
| 		uint64_t Now = OpenWifi::Now(); | ||||
|  | ||||
| 		if(LastRun_==0 || (Now-LastRun_)>120) { | ||||
| 			DB_.reset(); | ||||
| 			StorageService()->DevicesDB().GenerateDeviceReport(DB_); | ||||
| 			LastRun_ = Now; | ||||
| 		} | ||||
| 	} | ||||
|     bool DeviceDashboard::Get(FMSObjects::DeviceReport &D, Poco::Logger & Logger) { | ||||
|         uint64_t Now = Utils::Now(); | ||||
|         if(!ValidDashboard_ || LastRun_==0 || (Now-LastRun_)>120) { | ||||
|             Generate(D, Logger); | ||||
|         } else { | ||||
|             std::lock_guard	G(DataMutex_); | ||||
|             D = DB_; | ||||
|         } | ||||
|         return ValidDashboard_; | ||||
|     }; | ||||
|  | ||||
|     void DeviceDashboard::Generate(FMSObjects::DeviceReport &D, Poco::Logger & Logger ) { | ||||
|         if (GeneratingDashboard_.load()) { | ||||
|             // std::cout << "Trying to generate dashboard but already being generated" << std::endl; | ||||
|             while(GeneratingDashboard_.load()) { | ||||
|                 Poco::Thread::trySleep(100); | ||||
|             } | ||||
|             std::lock_guard	G(DataMutex_); | ||||
|             D = DB_; | ||||
|         } else { | ||||
|             GeneratingDashboard_ = true; | ||||
|             ValidDashboard_ = false; | ||||
|             try { | ||||
|                 // std::cout << "Generating dashboard." << std::endl; | ||||
|                 poco_information(Logger, "DASHBOARD: Generating a new dashboard."); | ||||
|                 FMSObjects::DeviceReport	NewData; | ||||
|                 StorageService()->DevicesDB().GenerateDeviceReport(NewData); | ||||
|                 LastRun_ = Utils::Now(); | ||||
|                 NewData.snapshot = LastRun_; | ||||
|                 D = NewData; | ||||
|                 std::lock_guard	G(DataMutex_); | ||||
|                 DB_ = NewData; | ||||
|                 ValidDashboard_=true; | ||||
|             } catch(...) { | ||||
|  | ||||
|             } | ||||
|             GeneratingDashboard_ = false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -2,22 +2,26 @@ | ||||
| // Created by stephane bourque on 2021-07-21. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALGW_DASHBOARD_H | ||||
| #define UCENTRALGW_DASHBOARD_H | ||||
| #pragma once | ||||
|  | ||||
| #include <mutex> | ||||
|  | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "Poco/Logger.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class DeviceDashboard { | ||||
| 	  public: | ||||
| 			void Create(); | ||||
| 			const FMSObjects::DeviceReport & Report() const { return DB_;} | ||||
| 			inline void Reset() { LastRun_=0; DB_.reset(); } | ||||
| 	  private: | ||||
|             FMSObjects::DeviceReport  	DB_; | ||||
| 			uint64_t 				LastRun_=0; | ||||
| 	}; | ||||
| } | ||||
|     class DeviceDashboard { | ||||
|     public: | ||||
|         bool Get(FMSObjects::DeviceReport &D, Poco::Logger &Logger); | ||||
|  | ||||
| #endif // UCENTRALGW_DASHBOARD_H | ||||
|     private: | ||||
|         std::mutex DataMutex_; | ||||
|         volatile std::atomic_bool GeneratingDashboard_ = false; | ||||
|         volatile bool ValidDashboard_ = false; | ||||
|         FMSObjects::DeviceReport DB_; | ||||
|         uint64_t LastRun_ = 0; | ||||
|  | ||||
|         void Generate(FMSObjects::DeviceReport &D, Poco::Logger &Logger); | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -2,11 +2,10 @@ | ||||
| // Created by stephane bourque on 2021-07-13. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFMS_DEVICECACHE_H | ||||
| #define UCENTRALFMS_DEVICECACHE_H | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -43,6 +42,3 @@ namespace OpenWifi { | ||||
|     inline auto DeviceCache() { return DeviceCache::instance(); } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| #endif //UCENTRALFMS_DEVICECACHE_H | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
| // Created by stephane bourque on 2021-07-26. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFMS_FIRMWARECACHE_H | ||||
| #define UCENTRALFMS_FIRMWARECACHE_H | ||||
| #pragma once | ||||
|  | ||||
| #include <map> | ||||
| #include <memory> | ||||
|  | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -40,6 +39,3 @@ namespace OpenWifi { | ||||
|     inline auto FirmwareCache() { return FirmwareCache::instance(); } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //UCENTRALFMS_FIRMWARECACHE_H | ||||
|   | ||||
| @@ -2,16 +2,17 @@ | ||||
| // Created by stephane bourque on 2021-07-13. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFMS_LATESTFIRMWARECACHE_H | ||||
| #define UCENTRALFMS_LATESTFIRMWARECACHE_H | ||||
| #pragma once | ||||
|  | ||||
| #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 "Poco/StringTokenizer.h" | ||||
|  | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -42,7 +43,7 @@ namespace OpenWifi { | ||||
|             auto Tokens = Poco::StringTokenizer(Revision,"/", Poco::StringTokenizer::TOK_TRIM); | ||||
|             if(Tokens.count()!=2) | ||||
|                 return false; | ||||
|             return (Tokens[1].substr(0,5) == "IP-v"); | ||||
|             return (Tokens[1].substr(0,5) == "TIP-v"); | ||||
|         } | ||||
|  | ||||
|         void DumpCache(); | ||||
| @@ -64,6 +65,3 @@ namespace OpenWifi { | ||||
|  | ||||
|     inline auto LatestFirmwareCache() { return LatestFirmwareCache::instance(); } | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //UCENTRALFMS_LATESTFIRMWARECACHE_H | ||||
|   | ||||
| @@ -14,6 +14,9 @@ | ||||
| #include "StorageService.h" | ||||
| #include "LatestFirmwareCache.h" | ||||
|  | ||||
| #include "framework/utils.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void ManifestCreator::onTimer([[maybe_unused]] Poco::Timer &timer) { | ||||
| @@ -29,7 +32,7 @@ namespace OpenWifi { | ||||
|  | ||||
|     bool ManifestCreator::ComputeManifest(S3BucketContent &BucketContent) { | ||||
|  | ||||
|         uint64_t Limit = OpenWifi::Now() - MaxAge_, Rejected=0, Accepted=0, BadFormat=0, MissingJson=0; | ||||
|         uint64_t Limit = Utils::Now() - MaxAge_, Rejected=0, Accepted=0, BadFormat=0, MissingJson=0; | ||||
|         for(auto &[Name,Entry]:BucketContent) { | ||||
|             std::string C = Entry.S3ContentManifest; | ||||
|  | ||||
| @@ -89,10 +92,10 @@ namespace OpenWifi { | ||||
|                 continue; | ||||
|  | ||||
|             if(BucketEntry.Valid && !StorageService()->FirmwaresDB().GetFirmwareByName(R,BucketEntry.Compatible,F)) { | ||||
|                 F.id = MicroService::instance().CreateUUID(); | ||||
|                 F.id = MicroServiceCreateUUID(); | ||||
|                 F.release = Release; | ||||
|                 F.size = BucketEntry.S3Size; | ||||
|                 F.created = OpenWifi::Now(); | ||||
|                 F.created = Utils::Now(); | ||||
|                 F.imageDate = BucketEntry.S3TimeStamp; | ||||
|                 F.image = BucketEntry.Image; | ||||
|                 F.uri = BucketEntry.URI; | ||||
| @@ -109,14 +112,14 @@ namespace OpenWifi { | ||||
|  | ||||
|     int ManifestCreator::Start() { | ||||
|         Running_ = true; | ||||
|         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); | ||||
|         S3BucketName_ = MicroServiceConfigGetString("s3.bucketname",""); | ||||
|         S3Region_ = MicroServiceConfigGetString("s3.region",""); | ||||
|         S3Secret_ = MicroServiceConfigGetString("s3.secret",""); | ||||
|         S3Key_ = MicroServiceConfigGetString("s3.key",""); | ||||
|         S3Retry_ = MicroServiceConfigGetInt("s3.retry",60); | ||||
|  | ||||
|         DBRefresh_ = MicroService::instance().ConfigGetInt("firmwaredb.refresh",30*60); | ||||
|         MaxAge_ = MicroService::instance().ConfigGetInt("firmwaredb.maxage",90) * 24 * 60 * 60; | ||||
|         DBRefresh_ = MicroServiceConfigGetInt("firmwaredb.refresh",30*60); | ||||
|         MaxAge_ = MicroServiceConfigGetInt("firmwaredb.maxage",90) * 24 * 60 * 60; | ||||
|  | ||||
|         AwsConfig_.enableTcpKeepAlive = true; | ||||
|         AwsConfig_.enableEndpointDiscovery = true; | ||||
| @@ -169,7 +172,7 @@ namespace OpenWifi { | ||||
|         static const std::string UPGRADE("-upgrade.bin"); | ||||
|  | ||||
|         std::string     URIBase = "https://"; | ||||
|         URIBase += MicroService::instance().ConfigGetString("s3.bucket.uri"); | ||||
|         URIBase += MicroServiceConfigGetString("s3.bucket.uri",""); | ||||
|  | ||||
|         Bucket.clear(); | ||||
|  | ||||
|   | ||||
| @@ -2,14 +2,13 @@ | ||||
| // Created by stephane bourque on 2021-06-02. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFWS_MANIFESTCREATOR_H | ||||
| #define UCENTRALFWS_MANIFESTCREATOR_H | ||||
| #pragma once | ||||
|  | ||||
| #include <aws/core/Aws.h> | ||||
| #include <aws/s3/S3Client.h> | ||||
| #include <aws/core/auth/AWSCredentials.h> | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "Poco/Timer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -71,5 +70,3 @@ namespace OpenWifi { | ||||
|     inline auto ManifestCreator() { return ManifestCreator::instance(); }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif //UCENTRALFWS_MANIFESTCREATOR_H | ||||
|   | ||||
| @@ -5,6 +5,10 @@ | ||||
| #include "NewCommandHandler.h" | ||||
| #include "StorageService.h" | ||||
|  | ||||
| #include "framework/KafkaManager.h" | ||||
| #include "fmt/format.h" | ||||
| #include "nlohmann/json.hpp" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void NewCommandHandler::run() { | ||||
|   | ||||
| @@ -2,10 +2,9 @@ | ||||
| // Created by stephane bourque on 2021-11-21. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_NEWCOMMANDHANDLER_H | ||||
| #define OWFMS_NEWCOMMANDHANDLER_H | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -37,5 +36,3 @@ namespace OpenWifi { | ||||
|     inline auto NewCommandHandler() { return NewCommandHandler::instance(); }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif //OWFMS_NEWCOMMANDHANDLER_H | ||||
|   | ||||
| @@ -13,6 +13,9 @@ | ||||
| #include "DeviceCache.h" | ||||
| #include "AutoUpdater.h" | ||||
|  | ||||
| #include "framework/KafkaManager.h" | ||||
| #include "fmt/format.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}} | ||||
|   | ||||
| @@ -2,11 +2,9 @@ | ||||
| // Created by stephane bourque on 2021-07-13. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALFMS_NEWCONNECTIONHANDLER_H | ||||
| #define UCENTRALFMS_NEWCONNECTIONHANDLER_H | ||||
| #pragma once | ||||
|  | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -40,4 +38,3 @@ namespace OpenWifi { | ||||
|     inline auto NewConnectionHandler() { return NewConnectionHandler::instance(); }; | ||||
| } | ||||
|  | ||||
| #endif //UCENTRALFMS_NEWCONNECTIONHANDLER_H | ||||
|   | ||||
| @@ -2,8 +2,7 @@ | ||||
| // Created by stephane bourque on 2021-10-23. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwareHandler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwaresHandler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwareAgeHandler.h" | ||||
| @@ -12,11 +11,13 @@ | ||||
| #include "RESTAPI/RESTAPI_historyHandler.h" | ||||
| #include "RESTAPI/RESTAPI_deviceReportHandler.h" | ||||
| #include "RESTAPI/RESTAPI_deviceInformation_handler.h" | ||||
| #include "framework/RESTAPI_SystemCommand.h" | ||||
| #include "framework/RESTAPI_WebSocketServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, | ||||
|                                                             Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t TransactionId) { | ||||
|                                                             Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) { | ||||
|         return  RESTAPI_Router< | ||||
|                 RESTAPI_firmwaresHandler, | ||||
|                 RESTAPI_firmwareHandler, | ||||
| @@ -26,12 +27,13 @@ namespace OpenWifi { | ||||
|                 RESTAPI_connectedDeviceHandler, | ||||
|                 RESTAPI_historyHandler, | ||||
|                 RESTAPI_deviceReportHandler, | ||||
|                 RESTAPI_deviceInformation_handler | ||||
|                 RESTAPI_deviceInformation_handler, | ||||
|                 RESTAPI_webSocketServer | ||||
|             >(Path,Bindings,L, S, TransactionId); | ||||
|     } | ||||
|  | ||||
|     Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, | ||||
|                                                             Poco::Logger & L, RESTAPI_GenericServer & S, uint64_t TransactionId) { | ||||
|                                                             Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t TransactionId) { | ||||
|         return RESTAPI_Router_I< | ||||
|                 RESTAPI_firmwaresHandler, | ||||
|                 RESTAPI_firmwareHandler, | ||||
|   | ||||
| @@ -5,12 +5,12 @@ | ||||
| #ifndef UCENTRALFMS_RESTAPI_CONNECTEDDEVICEHANDLER_H | ||||
| #define UCENTRALFMS_RESTAPI_CONNECTEDDEVICEHANDLER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_connectedDeviceHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_connectedDeviceHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_connectedDeviceHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -6,12 +6,12 @@ | ||||
| #define UCENTRALFMS_RESTAPI_CONNECTEDDEVICESHANDLER_H | ||||
|  | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_connectedDevicesHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_connectedDevicesHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_connectedDevicesHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -4,12 +4,12 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_deviceInformation_handler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_deviceInformation_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_deviceInformation_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -9,9 +9,13 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     void RESTAPI_deviceReportHandler::DoGet() { | ||||
|         Daemon()->CreateDashboard(); | ||||
|         Poco::JSON::Object  O; | ||||
|         Daemon()->GetDashboard().to_json(O); | ||||
|         ReturnObject(O); | ||||
|         poco_information(Logger(),fmt::format("GET-DASHBOARD: {}", Requester())); | ||||
|         FMSObjects::DeviceReport	Data; | ||||
|         if(Daemon()->GetDashboard().Get(Data, Logger())) { | ||||
|             Poco::JSON::Object Answer; | ||||
|             Data.to_json(Answer); | ||||
|             return ReturnObject(Answer); | ||||
|         } | ||||
|         return BadRequest(RESTAPI::Errors::InternalError); | ||||
|     } | ||||
| } | ||||
| @@ -4,12 +4,12 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_deviceReportHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_deviceReportHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_deviceReportHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -5,12 +5,12 @@ | ||||
| #ifndef UCENTRALFMS_RESTAPI_FIRMWAREAGEHANDLER_H | ||||
| #define UCENTRALFMS_RESTAPI_FIRMWAREAGEHANDLER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwareAgeHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_firmwareAgeHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_firmwareAgeHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -4,9 +4,11 @@ | ||||
|  | ||||
| #include "Poco/JSON/Parser.h" | ||||
|  | ||||
| #include "RESTAPI_firmwareHandler.h" | ||||
| #include "RESTAPI/RESTAPI_firmwareHandler.h" | ||||
| #include "StorageService.h" | ||||
| #include "framework/ow_constants.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     void | ||||
| @@ -16,7 +18,7 @@ namespace OpenWifi { | ||||
|         if (!F.from_json(Obj)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||
|         } | ||||
|         F.id = MicroService::instance().CreateUUID(); | ||||
|         F.id = MicroServiceCreateUUID(); | ||||
|         if(StorageService()->FirmwaresDB().AddFirmware(F)) { | ||||
|             Poco::JSON::Object  Answer; | ||||
|             F.to_json(Answer); | ||||
| @@ -78,7 +80,7 @@ namespace OpenWifi { | ||||
|             SecurityObjects::NoteInfoVec NIV; | ||||
|             NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get(RESTAPI::Protocol::NOTES).toString()); | ||||
|             for(auto const &i:NIV) { | ||||
|                 SecurityObjects::NoteInfo   ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UserInfo_.userinfo.email, .note=i.note}; | ||||
|                 SecurityObjects::NoteInfo   ii{.created=(uint64_t)Utils::Now(), .createdBy=UserInfo_.userinfo.email, .note=i.note}; | ||||
|                 F.notes.push_back(ii); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -5,12 +5,12 @@ | ||||
| #ifndef UCENTRALFWS_RESTAPI_FIRMWAREHANDLER_H | ||||
| #define UCENTRALFWS_RESTAPI_FIRMWAREHANDLER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwareHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_firmwareHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_firmwareHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -5,12 +5,12 @@ | ||||
| #ifndef UCENTRALFWS_RESTAPI_FIRMWARESHANDLER_H | ||||
| #define UCENTRALFWS_RESTAPI_FIRMWARESHANDLER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_firmwaresHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_firmwaresHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_firmwaresHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -4,12 +4,12 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class RESTAPI_historyHandler : public RESTAPIHandler { | ||||
|     public: | ||||
|         RESTAPI_historyHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal) | ||||
|         RESTAPI_historyHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
|                 : RESTAPIHandler(bindings, L, | ||||
|                                  std::vector<std::string> | ||||
|                                          {Poco::Net::HTTPRequest::HTTP_GET, | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
|  | ||||
| #include "RESTAPI_AnalyticsObjects.h" | ||||
| #include "RESTAPI_ProvObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
|   | ||||
| @@ -5,6 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "RESTAPI_ProvObjects.h" | ||||
| #include "framework/utils.h" | ||||
| #include <vector> | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -375,7 +376,7 @@ namespace OpenWifi { | ||||
|         }; | ||||
|  | ||||
|         struct WifiClientHistory { | ||||
|             uint64_t        timestamp=OpenWifi::Now(); | ||||
|             uint64_t        timestamp=Utils::Now(); | ||||
|             std::string     station_id; | ||||
|             std::string     bssid; | ||||
|             std::string     ssid; | ||||
|   | ||||
| @@ -3,7 +3,7 @@ | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_CertObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -154,6 +154,7 @@ namespace OpenWifi::CertObjects { | ||||
|         field_to_json(Obj,"submitted", submitted); | ||||
|         field_to_json(Obj,"started", started); | ||||
|         field_to_json(Obj,"completed", completed); | ||||
|         field_to_json(Obj,"requesterUsername", requesterUsername); | ||||
|     } | ||||
|  | ||||
|     bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| @@ -171,6 +172,7 @@ namespace OpenWifi::CertObjects { | ||||
|             field_from_json(Obj,"submitted", submitted); | ||||
|             field_from_json(Obj,"started", started); | ||||
|             field_from_json(Obj,"completed", completed); | ||||
|             field_from_json(Obj,"requesterUsername", requesterUsername); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|         } | ||||
|   | ||||
| @@ -91,6 +91,7 @@ namespace OpenWifi::CertObjects { | ||||
|         uint64_t                        submitted=0; | ||||
|         uint64_t                        started=0; | ||||
|         uint64_t                        completed=0; | ||||
|         std::string                     requesterUsername; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|   | ||||
| @@ -3,7 +3,8 @@ | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_FMSObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -233,7 +234,7 @@ namespace OpenWifi::FMSObjects { | ||||
|         UnknownFirmwares_.clear(); | ||||
|         totalSecondsOld_.clear(); | ||||
|         numberOfDevices = 0 ; | ||||
|         snapshot = OpenWifi::Now(); | ||||
|         snapshot = Utils::Now(); | ||||
|     } | ||||
|  | ||||
|     bool DeviceReport::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) { | ||||
|   | ||||
| @@ -11,12 +11,13 @@ | ||||
|  | ||||
| #include "Daemon.h" | ||||
| #ifdef	TIP_GATEWAY_SERVICE | ||||
| #include "DeviceRegistry.h" | ||||
| #include "AP_WS_Server.h" | ||||
| #include "CapabilitiesCache.h" | ||||
| #endif | ||||
|  | ||||
| #include "RESTAPI_GWobjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -49,6 +50,10 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"entity", entity); | ||||
| 		field_to_json(Obj,"modified", modified); | ||||
| 		field_to_json(Obj,"locale", locale); | ||||
| 		field_to_json(Obj,"restrictedDevice", restrictedDevice); | ||||
| 		field_to_json(Obj,"pendingConfiguration", pendingConfiguration); | ||||
| 		field_to_json(Obj,"pendingConfigurationCmd", pendingConfigurationCmd); | ||||
| 		field_to_json(Obj,"restrictionDetails", restrictionDetails); | ||||
| 	} | ||||
|  | ||||
| 	void Device::to_json_with_status(Poco::JSON::Object &Obj) const { | ||||
| @@ -57,7 +62,7 @@ namespace OpenWifi::GWObjects { | ||||
| #ifdef TIP_GATEWAY_SERVICE | ||||
| 		ConnectionState ConState; | ||||
|  | ||||
| 		if (DeviceRegistry()->GetState(SerialNumber, ConState)) { | ||||
| 		if (AP_WS_Server()->GetState(SerialNumber, ConState)) { | ||||
| 			ConState.to_json(Obj); | ||||
| 		} else { | ||||
| 			field_to_json(Obj,"ipAddress", ""); | ||||
| @@ -69,6 +74,7 @@ namespace OpenWifi::GWObjects { | ||||
| 			field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); | ||||
| 			field_to_json(Obj,"associations_2G", (uint64_t) 0); | ||||
| 			field_to_json(Obj,"associations_5G", (uint64_t) 0); | ||||
| 			field_to_json(Obj,"associations_6G", (uint64_t) 0); | ||||
| 		} | ||||
| #endif | ||||
| 	} | ||||
| @@ -88,6 +94,10 @@ namespace OpenWifi::GWObjects { | ||||
| 			field_from_json(Obj,"subscriber", subscriber); | ||||
| 			field_from_json(Obj,"entity", entity); | ||||
| 			field_from_json(Obj,"locale", locale); | ||||
| 			field_from_json(Obj,"restrictedDevice", restrictedDevice); | ||||
| 			field_from_json(Obj,"pendingConfiguration", pendingConfiguration); | ||||
| 			field_from_json(Obj,"pendingConfigurationCmd", pendingConfigurationCmd); | ||||
| 			field_from_json(Obj,"restrictionDetails", restrictionDetails); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| @@ -198,6 +208,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"lastContact", LastContact); | ||||
| 		field_to_json(Obj,"associations_2G", Associations_2G); | ||||
| 		field_to_json(Obj,"associations_5G", Associations_5G); | ||||
| 		field_to_json(Obj,"associations_6G", Associations_6G); | ||||
| 		field_to_json(Obj,"webSocketClients", webSocketClients); | ||||
| 		field_to_json(Obj,"websocketPackets", websocketPackets); | ||||
| 		field_to_json(Obj,"kafkaClients", kafkaClients); | ||||
| @@ -206,7 +217,8 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"started", started); | ||||
| 		field_to_json(Obj,"sessionId", sessionId); | ||||
| 		field_to_json(Obj,"connectionCompletionTime", connectionCompletionTime); | ||||
| 		field_to_json(Obj,"totalConnectionTime", OpenWifi::Now() - started); | ||||
| 		field_to_json(Obj,"totalConnectionTime", Utils::Now() - started); | ||||
| 		field_to_json(Obj,"certificateExpiryDate", certificateExpiryDate); | ||||
|  | ||||
| 		switch(VerifiedCertificate) { | ||||
| 			case NO_CERTIFICATE: | ||||
| @@ -225,12 +237,14 @@ namespace OpenWifi::GWObjects { | ||||
| 	void DeviceConnectionStatistics::to_json(Poco::JSON::Object &Obj) const { | ||||
| 		field_to_json(Obj,"averageConnectionTime", averageConnectionTime); | ||||
| 		field_to_json(Obj,"connectedDevices", connectedDevices ); | ||||
| 		field_to_json(Obj,"connectingDevices", connectingDevices ); | ||||
| 	} | ||||
|  | ||||
| 	bool DeviceConnectionStatistics::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"averageConnectionTime", averageConnectionTime); | ||||
| 			field_from_json(Obj,"connectedDevices", connectedDevices ); | ||||
| 			field_from_json(Obj,"connectingDevices", connectingDevices ); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| @@ -283,7 +297,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		lastContact.clear(); | ||||
| 		associations.clear(); | ||||
| 		numberOfDevices = 0 ; | ||||
| 		snapshot = OpenWifi::Now(); | ||||
| 		snapshot = Utils::Now(); | ||||
| 	} | ||||
|  | ||||
| 	void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{ | ||||
| @@ -295,9 +309,12 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"serialNumber",serialNumber); | ||||
| 		field_to_json(Obj,"timeout",timeout); | ||||
| 		field_to_json(Obj,"type",type); | ||||
| 		field_to_json(Obj,"script",script); | ||||
| 		field_to_json(Obj,"scriptId",scriptId); | ||||
| 		field_to_json(Obj,"script",script); | ||||
| 		field_to_json(Obj,"when",when); | ||||
| 		field_to_json(Obj,"signature", signature); | ||||
| 		field_to_json(Obj,"deferred", deferred); | ||||
| 		field_to_json(Obj,"uri", uri); | ||||
| 	} | ||||
|  | ||||
| 	bool ScriptRequest::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| @@ -308,6 +325,9 @@ namespace OpenWifi::GWObjects { | ||||
| 			field_from_json(Obj,"script",script); | ||||
| 			field_from_json(Obj,"scriptId",scriptId); | ||||
| 			field_from_json(Obj,"when",when); | ||||
| 			field_from_json(Obj,"signature", signature); | ||||
| 			field_from_json(Obj,"deferred", deferred); | ||||
| 			field_from_json(Obj,"uri", uri); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| @@ -379,6 +399,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"secret",secret); | ||||
| 		field_to_json(Obj,"certificate",certificate); | ||||
| 		field_to_json(Obj,"radsec",radsec); | ||||
| 		field_to_json(Obj,"allowSelfSigned",allowSelfSigned); | ||||
| 		field_to_json(Obj,"radsecPort",radsecPort); | ||||
| 		field_to_json(Obj,"radsecSecret",radsecSecret); | ||||
| 		field_to_json(Obj,"radsecCacerts",radsecCacerts); | ||||
| @@ -397,6 +418,7 @@ namespace OpenWifi::GWObjects { | ||||
| 			field_from_json(Obj,"secret",secret); | ||||
| 			field_from_json(Obj,"certificate",certificate); | ||||
| 			field_from_json(Obj,"radsec",radsec); | ||||
| 			field_from_json(Obj,"allowSelfSigned",allowSelfSigned); | ||||
| 			field_from_json(Obj,"radsecSecret",radsecSecret); | ||||
| 			field_from_json(Obj,"radsecPort",radsecPort); | ||||
| 			field_from_json(Obj,"radsecCacerts",radsecCacerts); | ||||
| @@ -409,5 +431,117 @@ namespace OpenWifi::GWObjects { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	void ScriptEntry::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,"uri", uri); | ||||
| 		field_to_json(Obj,"content", content); | ||||
| 		field_to_json(Obj,"version", version); | ||||
| 		field_to_json(Obj,"type", type); | ||||
| 		field_to_json(Obj,"created", created); | ||||
| 		field_to_json(Obj,"modified", modified); | ||||
| 		field_to_json(Obj,"author", author); | ||||
| 		field_to_json(Obj,"restricted", restricted); | ||||
| 		field_to_json(Obj,"deferred", deferred); | ||||
| 		field_to_json(Obj,"timeout", timeout); | ||||
| 		field_to_json(Obj,"defaultUploadURI", defaultUploadURI); | ||||
| 	} | ||||
|  | ||||
| 	bool ScriptEntry::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,"uri", uri); | ||||
| 			field_from_json(Obj,"content", content); | ||||
| 			field_from_json(Obj,"version", version); | ||||
| 			field_from_json(Obj,"type", type); | ||||
| 			field_from_json(Obj,"created", created); | ||||
| 			field_from_json(Obj,"modified", modified); | ||||
| 			field_from_json(Obj,"author", author); | ||||
| 			field_from_json(Obj,"restricted", restricted); | ||||
| 			field_from_json(Obj,"deferred", deferred); | ||||
| 			field_from_json(Obj,"timeout", timeout); | ||||
| 			field_from_json(Obj,"defaultUploadURI", defaultUploadURI); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	void ScriptEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||
| 		field_to_json(Obj,"scripts",scripts); | ||||
| 	} | ||||
|  | ||||
| 	bool ScriptEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"scripts",scripts); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	void DeviceRestrictionsKeyInfo::to_json(Poco::JSON::Object &Obj) const { | ||||
| 		field_to_json(Obj,"vendor", vendor); | ||||
| 		field_to_json(Obj,"algo", algo); | ||||
| 	} | ||||
|  | ||||
| 	bool DeviceRestrictionsKeyInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"vendor", vendor); | ||||
| 			field_from_json(Obj,"algo", algo); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| 		return false; | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	void DeviceRestrictions::to_json(Poco::JSON::Object &Obj) const { | ||||
| 		field_to_json(Obj,"dfs", dfs); | ||||
| 		field_to_json(Obj,"ssh", ssh); | ||||
| 		field_to_json(Obj,"rtty", rtty); | ||||
| 		field_to_json(Obj,"tty", tty); | ||||
| 		field_to_json(Obj,"developer", developer); | ||||
| 		field_to_json(Obj,"upgrade", upgrade); | ||||
| 		field_to_json(Obj,"commands", commands); | ||||
| 		field_to_json(Obj,"country", country); | ||||
| 		field_to_json(Obj,"key_info", key_info); | ||||
| 	} | ||||
|  | ||||
| 	bool DeviceRestrictions::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"dfs", dfs); | ||||
| 			field_from_json(Obj,"ssh", ssh); | ||||
| 			field_from_json(Obj,"rtty", rtty); | ||||
| 			field_from_json(Obj,"tty", tty); | ||||
| 			field_from_json(Obj,"developer", developer); | ||||
| 			field_from_json(Obj,"upgrade", upgrade); | ||||
| 			field_from_json(Obj,"commands", commands); | ||||
| 			field_from_json(Obj,"country", country); | ||||
| 			field_from_json(Obj,"key_info", key_info); | ||||
| 			return true; | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	bool DeviceRestrictionsKeyInfo::operator!=(const OpenWifi::GWObjects::DeviceRestrictionsKeyInfo &T) const { | ||||
| 		return (T.algo!=algo) || (T.vendor!=vendor); | ||||
| 	} | ||||
|  | ||||
| 	bool DeviceRestrictions::operator!=(const OpenWifi::GWObjects::DeviceRestrictions &T) const { | ||||
| 		return (	(T.dfs!=dfs)					|| | ||||
| 					(T.rtty!=rtty)					|| | ||||
| 					(T.upgrade!=upgrade)		|| | ||||
| 					(T.commands != commands)		|| | ||||
| 					(T.developer != developer)		|| | ||||
| 					(T.ssh !=ssh) 					|| | ||||
| 					(T.key_info != key_info)		|| | ||||
| 					(T.country != country) ); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,23 +28,52 @@ namespace OpenWifi::GWObjects { | ||||
| 		uint64_t TX = 0, RX = 0; | ||||
| 		uint64_t Associations_2G=0; | ||||
| 		uint64_t Associations_5G=0; | ||||
| 		uint64_t Associations_6G=0; | ||||
| 		bool Connected = false; | ||||
| 		uint64_t LastContact=0; | ||||
| 		std::string Firmware; | ||||
| 		CertificateValidation VerifiedCertificate = NO_CERTIFICATE; | ||||
| 		std::string Compatible; | ||||
| 		uint64_t 	kafkaClients=0; | ||||
| 		uint64_t 	webSocketClients=0; | ||||
| 		uint64_t 	kafkaPackets=0; | ||||
| 		uint64_t 	websocketPackets=0; | ||||
| 		std::string locale; | ||||
| 		uint64_t 	started=0; | ||||
| 		uint64_t 	sessionId=0; | ||||
| 		double      connectionCompletionTime=0.0; | ||||
| 		std::string 	Compatible; | ||||
| 		uint64_t 		kafkaClients=0; | ||||
| 		uint64_t 		webSocketClients=0; | ||||
| 		uint64_t 		kafkaPackets=0; | ||||
| 		uint64_t 		websocketPackets=0; | ||||
| 		std::string 	locale; | ||||
| 		uint64_t 		started=0; | ||||
| 		uint64_t 		sessionId=0; | ||||
| 		double      	connectionCompletionTime=0.0; | ||||
| 		std::uint64_t	certificateExpiryDate=0; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	}; | ||||
|  | ||||
| 	struct DeviceRestrictionsKeyInfo { | ||||
| 		std::string 	vendor; | ||||
| 		std::string 	algo; | ||||
|  | ||||
| 		bool operator !=(const DeviceRestrictionsKeyInfo &b) const; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct DeviceRestrictions { | ||||
| 		bool    					dfs = false; | ||||
| 		bool    					ssh = false; | ||||
| 		bool    					rtty = false; | ||||
| 		bool    					tty = false; | ||||
| 		bool    					developer = false; | ||||
| 		bool    					upgrade = false; | ||||
| 		bool    					commands = false; | ||||
| 		std::vector<std::string>   	country; | ||||
| 		DeviceRestrictionsKeyInfo	key_info; | ||||
|  | ||||
| 		bool operator !=(const DeviceRestrictions &D) const; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct Device { | ||||
| 		std::string SerialNumber; | ||||
| 		std::string DeviceType; | ||||
| @@ -68,6 +97,10 @@ namespace OpenWifi::GWObjects { | ||||
| 		std::string entity; | ||||
| 		uint64_t 	modified=0; | ||||
| 		std::string locale; | ||||
| 		bool 		restrictedDevice=false; | ||||
| 		std::string pendingConfiguration; | ||||
| 		std::string pendingConfigurationCmd; | ||||
| 		DeviceRestrictions	restrictionDetails; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		void to_json_with_status(Poco::JSON::Object &Obj) const; | ||||
| @@ -78,6 +111,8 @@ namespace OpenWifi::GWObjects { | ||||
| 	struct DeviceConnectionStatistics { | ||||
| 		std::uint64_t connectedDevices = 0; | ||||
| 		std::uint64_t averageConnectionTime = 0; | ||||
| 		std::uint64_t connectingDevices = 0; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| @@ -211,13 +246,44 @@ namespace OpenWifi::GWObjects { | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	}; | ||||
|  | ||||
| 	struct ScriptEntry { | ||||
| 		std::string 		id; | ||||
| 		std::string 		name; | ||||
| 		std::string 		description; | ||||
| 		std::string 		uri; | ||||
| 		std::string 		content; | ||||
| 		std::string 		version; | ||||
| 		std::string 		type; | ||||
| 		std::uint64_t 		created; | ||||
| 		std::uint64_t 		modified; | ||||
| 		std::string 		author; | ||||
| 		Types::StringVec 	restricted; | ||||
| 		bool				deferred=false; | ||||
| 		std::uint64_t 		timeout=30; | ||||
| 		std::string 		defaultUploadURI; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct ScriptEntryList { | ||||
| 		std::vector<ScriptEntry>	scripts; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct ScriptRequest { | ||||
| 		uint64_t 	timeout=30; | ||||
| 		std::string serialNumber; | ||||
| 		uint64_t 	timeout=30; | ||||
| 		std::string type; | ||||
| 		std::string script; | ||||
| 		std::string scriptId; | ||||
| 		uint64_t 	when=0; | ||||
| 		std::uint64_t when; | ||||
| 		std::string signature; | ||||
| 		bool deferred; | ||||
| 		std::string uri; | ||||
|  | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| @@ -230,6 +296,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		std::string secret; | ||||
| 		std::string certificate; | ||||
| 		bool 		radsec=false; | ||||
| 		bool 		allowSelfSigned=false; | ||||
| 		uint16_t 	radsecPort=2083; | ||||
| 		std::string radsecSecret; | ||||
| 		std::string radsecKey; | ||||
| @@ -271,4 +338,5 @@ namespace OpenWifi::GWObjects { | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										110
									
								
								src/RESTObjects/RESTAPI_OWLSobjects.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/RESTObjects/RESTAPI_OWLSobjects.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-08-31. | ||||
| // | ||||
|  | ||||
| #include "framework/RESTAPI_utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| using OpenWifi::RESTAPI_utils::EmbedDocument; | ||||
|  | ||||
| #include "RESTAPI_OWLSobjects.h" | ||||
|  | ||||
| // SIM -> 0x53/0x073, 0x49/0x69, 0x4d/0x6d | ||||
|  | ||||
| namespace OpenWifi::OWLSObjects { | ||||
|  | ||||
|     void SimulationDetails::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"id", id); | ||||
|         field_to_json(Obj,"name", name); | ||||
|         field_to_json(Obj,"gateway", gateway); | ||||
|         field_to_json(Obj,"certificate", certificate); | ||||
|         field_to_json(Obj,"key", key); | ||||
|         field_to_json(Obj,"macPrefix", macPrefix); | ||||
|         field_to_json(Obj,"deviceType", deviceType); | ||||
|         field_to_json(Obj,"devices", devices); | ||||
|         field_to_json(Obj,"healthCheckInterval", healthCheckInterval); | ||||
|         field_to_json(Obj,"stateInterval", stateInterval); | ||||
|         field_to_json(Obj,"minAssociations", minAssociations); | ||||
|         field_to_json(Obj,"maxAssociations", maxAssociations); | ||||
|         field_to_json(Obj,"minClients", minClients); | ||||
|         field_to_json(Obj,"maxClients", maxClients); | ||||
|         field_to_json(Obj,"simulationLength", simulationLength); | ||||
|         field_to_json(Obj,"threads", threads); | ||||
|         field_to_json(Obj,"clientInterval", clientInterval); | ||||
|         field_to_json(Obj,"keepAlive", keepAlive); | ||||
|         field_to_json(Obj,"reconnectInterval", reconnectInterval); | ||||
|         field_to_json(Obj,"concurrentDevices", concurrentDevices); | ||||
|     } | ||||
|  | ||||
|     bool SimulationDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"id", id); | ||||
|             field_from_json(Obj,"name", name); | ||||
|             field_from_json(Obj,"gateway", gateway); | ||||
|             field_from_json(Obj,"certificate", certificate); | ||||
|             field_from_json(Obj,"key", key); | ||||
|             field_from_json(Obj,"macPrefix", macPrefix); | ||||
|             field_from_json(Obj,"deviceType", deviceType); | ||||
|             field_from_json(Obj,"devices", devices); | ||||
|             field_from_json(Obj,"healthCheckInterval", healthCheckInterval); | ||||
|             field_from_json(Obj,"stateInterval", stateInterval); | ||||
|             field_from_json(Obj,"minAssociations", minAssociations); | ||||
|             field_from_json(Obj,"maxAssociations", maxAssociations); | ||||
|             field_from_json(Obj,"minClients", minClients); | ||||
|             field_from_json(Obj,"maxClients", maxClients); | ||||
|             field_from_json(Obj,"simulationLength", simulationLength); | ||||
|             field_from_json(Obj,"threads", threads); | ||||
|             field_from_json(Obj,"clientInterval", clientInterval); | ||||
|             field_from_json(Obj,"keepAlive", keepAlive); | ||||
|             field_from_json(Obj,"reconnectInterval", reconnectInterval); | ||||
|             field_from_json(Obj,"concurrentDevices", concurrentDevices); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void SimulationDetailsList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"list", list); | ||||
|     } | ||||
|  | ||||
|     bool SimulationDetailsList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"list", list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void SimulationStatus::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"id", id); | ||||
|         field_to_json(Obj,"simulationId", simulationId); | ||||
|         field_to_json(Obj,"state", state); | ||||
|         field_to_json(Obj,"tx", tx); | ||||
|         field_to_json(Obj,"rx", rx); | ||||
|         field_to_json(Obj,"msgsTx", msgsTx); | ||||
|         field_to_json(Obj,"msgsRx", msgsRx); | ||||
|         field_to_json(Obj,"liveDevices", liveDevices); | ||||
|         field_to_json(Obj,"timeToFullDevices", timeToFullDevices); | ||||
|         field_to_json(Obj,"startTime", startTime); | ||||
|         field_to_json(Obj,"endTime", endTime); | ||||
|         field_to_json(Obj,"errorDevices", errorDevices); | ||||
|         field_to_json(Obj,"owner", owner); | ||||
|     } | ||||
|  | ||||
|     void Dashboard::to_json([[maybe_unused]] Poco::JSON::Object &Obj) const { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     bool Dashboard::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void Dashboard::reset() { | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/RESTObjects/RESTAPI_OWLSobjects.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/RESTObjects/RESTAPI_OWLSobjects.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-08-31. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRALSIM_RESTAPI_OWLSOBJECTS_H | ||||
| #define UCENTRALSIM_RESTAPI_OWLSOBJECTS_H | ||||
|  | ||||
| #include <vector> | ||||
| #include "Poco/JSON/Object.h" | ||||
|  | ||||
| namespace OpenWifi::OWLSObjects { | ||||
|  | ||||
|     struct SimulationDetails { | ||||
|         std::string     id; | ||||
|         std::string     name; | ||||
|         std::string     gateway; | ||||
|         std::string     certificate; | ||||
|         std::string     key; | ||||
|         std::string     macPrefix; | ||||
|         std::string     deviceType; | ||||
|         uint64_t        devices = 5; | ||||
|         uint64_t        healthCheckInterval = 60; | ||||
|         uint64_t        stateInterval = 60 ; | ||||
|         uint64_t        minAssociations = 1; | ||||
|         uint64_t        maxAssociations = 3; | ||||
|         uint64_t        minClients = 1 ; | ||||
|         uint64_t        maxClients = 3; | ||||
|         uint64_t        simulationLength = 60 * 60; | ||||
|         uint64_t        threads = 16; | ||||
|         uint64_t        clientInterval = 1; | ||||
|         uint64_t        keepAlive = 300; | ||||
|         uint64_t        reconnectInterval = 30 ; | ||||
|         uint64_t        concurrentDevices = 5; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct SimulationDetailsList { | ||||
|         std::vector<SimulationDetails>  list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct SimulationStatus { | ||||
|         std::string     id; | ||||
|         std::string     simulationId; | ||||
|         std::string     state; | ||||
|         uint64_t        tx; | ||||
|         uint64_t        rx; | ||||
|         uint64_t        msgsTx; | ||||
|         uint64_t        msgsRx; | ||||
|         uint64_t        liveDevices; | ||||
|         uint64_t        timeToFullDevices; | ||||
|         uint64_t        startTime; | ||||
|         uint64_t        endTime; | ||||
|         uint64_t        errorDevices; | ||||
|         std::string     owner; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|     }; | ||||
|  | ||||
|  | ||||
|     struct Dashboard { | ||||
|         int O; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|         void reset(); | ||||
|  | ||||
|     }; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //UCENTRALSIM_RESTAPI_OWLSOBJECTS_H | ||||
| @@ -8,7 +8,9 @@ | ||||
|  | ||||
|  | ||||
| #include "RESTAPI_ProvObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
| @@ -600,6 +602,7 @@ namespace OpenWifi::ProvObjects { | ||||
|         field_to_json( Obj, "devClass",devClass); | ||||
|         field_to_json( Obj, "locale",locale); | ||||
|         field_to_json( Obj, "realMacAddress",realMacAddress); | ||||
|         field_to_json( Obj, "doNotAllowOverrides",doNotAllowOverrides); | ||||
|     } | ||||
|  | ||||
|     bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
| @@ -621,6 +624,7 @@ namespace OpenWifi::ProvObjects { | ||||
|             field_from_json( Obj,"devClass",devClass); | ||||
|             field_from_json( Obj,"locale",locale); | ||||
|             field_from_json( Obj,"realMacAddress",realMacAddress); | ||||
|             field_from_json( Obj, "doNotAllowOverrides",doNotAllowOverrides); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -1091,7 +1095,7 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = OpenWifi::Now(); | ||||
|         uint64_t Now = Utils::Now(); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|  | ||||
| @@ -1112,7 +1116,7 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = OpenWifi::Now(); | ||||
|         uint64_t Now = Utils::Now(); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|  | ||||
| @@ -1130,14 +1134,14 @@ namespace OpenWifi::ProvObjects { | ||||
|         } | ||||
|         I.notes = N; | ||||
|         I.modified = I.created = Now; | ||||
|         I.id = MicroService::CreateUUID(); | ||||
|         I.id = MicroServiceCreateUUID(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool CreateObjectInfo([[maybe_unused]] const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         I.modified = I.created = OpenWifi::Now(); | ||||
|         I.id = MicroService::CreateUUID(); | ||||
|         I.modified = I.created = Utils::Now(); | ||||
|         I.id = MicroServiceCreateUUID(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| @@ -1159,5 +1163,82 @@ namespace OpenWifi::ProvObjects { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void RRMAlgorithmDetails::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"name",name); | ||||
|         field_to_json(Obj,"parameters",parameters); | ||||
|     } | ||||
|  | ||||
|     bool RRMAlgorithmDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"name",name); | ||||
|             field_from_json(Obj,"parameters",parameters); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void RRMDetails::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"vendor",vendor); | ||||
|         field_to_json(Obj,"schedule",schedule); | ||||
|         field_to_json(Obj,"algorithms",algorithms); | ||||
|     } | ||||
|  | ||||
|     bool RRMDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"vendor",vendor); | ||||
|             field_from_json(Obj,"schedule",schedule); | ||||
|             field_from_json(Obj,"algorithms",algorithms); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ConfigurationOverride::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"source",source); | ||||
|         field_to_json(Obj,"reason",reason); | ||||
|         field_to_json(Obj,"parameterName",parameterName); | ||||
|         field_to_json(Obj,"parameterType",parameterType); | ||||
|         field_to_json(Obj,"parameterValue",parameterValue); | ||||
|         field_to_json(Obj,"modified",modified); | ||||
|     } | ||||
|  | ||||
|     bool ConfigurationOverride::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"source",source); | ||||
|             field_from_json(Obj,"reason",reason); | ||||
|             field_from_json(Obj,"parameterName",parameterName); | ||||
|             field_from_json(Obj,"parameterType",parameterType); | ||||
|             field_from_json(Obj,"parameterValue",parameterValue); | ||||
|             field_from_json(Obj,"modified",modified); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ConfigurationOverrideList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj,"serialNumber",serialNumber); | ||||
|         field_to_json(Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json(Obj,"overrides",overrides); | ||||
|     } | ||||
|  | ||||
|     bool ConfigurationOverrideList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"serialNumber",serialNumber); | ||||
|             field_from_json(Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json(Obj,"overrides",overrides); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,8 +8,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include "RESTAPI_SecurityObjects.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| namespace OpenWifi::ProvObjects { | ||||
|  | ||||
| @@ -62,6 +61,21 @@ namespace OpenWifi::ProvObjects { | ||||
|     }; | ||||
|     typedef std::vector<ManagementPolicy>      ManagementPolicyVec; | ||||
|  | ||||
|     struct RRMAlgorithmDetails { | ||||
|         std::string     name; | ||||
|         std::string     parameters; | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct RRMDetails { | ||||
|         std::string     vendor; | ||||
|         std::string     schedule; | ||||
|         std::vector<RRMAlgorithmDetails>    algorithms; | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct DeviceRules { | ||||
|         std::string     rcOnly{"inherit"}; | ||||
|         std::string     rrm{"inherit"}; | ||||
| @@ -414,6 +428,7 @@ namespace OpenWifi::ProvObjects { | ||||
|         std::string     devClass; | ||||
|         std::string     locale; | ||||
|         std::string     realMacAddress; | ||||
|         bool            doNotAllowOverrides=false; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| @@ -679,6 +694,27 @@ namespace OpenWifi::ProvObjects { | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ConfigurationOverride { | ||||
|         std::string     source; | ||||
|         std::string     reason; | ||||
|         std::string     parameterName; | ||||
|         std::string     parameterType; | ||||
|         std::string     parameterValue; | ||||
|         std::uint64_t   modified; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ConfigurationOverrideList { | ||||
|         std::string     serialNumber; | ||||
|         Types::UUID_t   managementPolicy; | ||||
|         std::vector<ConfigurationOverride>  overrides; | ||||
|  | ||||
|         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); | ||||
|     bool CreateObjectInfo(const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/JSON/Stringifier.h" | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
| #include "RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| @@ -433,7 +433,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	            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)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note}; | ||||
| 	                SecurityObjects::NoteInfo   ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note}; | ||||
| 	                Notes.push_back(ii); | ||||
| 	            } | ||||
| 	        } | ||||
| @@ -446,7 +446,7 @@ namespace OpenWifi::SecurityObjects { | ||||
|  | ||||
| 	bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes) { | ||||
| 	    for(auto const &i:NewNotes) { | ||||
| 	        SecurityObjects::NoteInfo   ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note}; | ||||
| 	        SecurityObjects::NoteInfo   ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note}; | ||||
| 	        ExistingNotes.push_back(ii); | ||||
| 	    } | ||||
|         return true; | ||||
| @@ -619,5 +619,80 @@ namespace OpenWifi::SecurityObjects { | ||||
|         field_to_json(Obj,"login",login); | ||||
|         field_to_json(Obj,"logout",logout); | ||||
|     } | ||||
|  | ||||
|     void ApiKeyAccessRight::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "service", service); | ||||
|         field_to_json(Obj, "access", access); | ||||
|     } | ||||
|  | ||||
|     bool ApiKeyAccessRight::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "service", service); | ||||
|             field_from_json(Obj, "access", access); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|             std::cout << "Cannot parse: Token" << std::endl; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ApiKeyAccessRightList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "acls", acls); | ||||
|     } | ||||
|  | ||||
|     bool ApiKeyAccessRightList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "acls", acls); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|             std::cout << "Cannot parse: Token" << std::endl; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ApiKeyEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "id", id); | ||||
|         field_to_json(Obj, "userUuid", userUuid); | ||||
|         field_to_json(Obj, "name", name); | ||||
|         field_to_json(Obj, "apiKey", apiKey); | ||||
|         field_to_json(Obj, "salt", salt); | ||||
|         field_to_json(Obj, "description", description); | ||||
|         field_to_json(Obj, "expiresOn", expiresOn); | ||||
|         field_to_json(Obj, "rights", rights); | ||||
|         field_to_json(Obj, "lastUse", lastUse); | ||||
|     } | ||||
|  | ||||
|     bool ApiKeyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "id", id); | ||||
|             field_from_json(Obj, "userUuid", userUuid); | ||||
|             field_from_json(Obj, "name", name); | ||||
|             field_from_json(Obj, "apiKey", apiKey); | ||||
|             field_from_json(Obj, "salt", salt); | ||||
|             field_from_json(Obj, "description", description); | ||||
|             field_from_json(Obj, "expiresOn", expiresOn); | ||||
|             field_from_json(Obj, "rights", rights); | ||||
|             field_from_json(Obj, "lastUse", lastUse); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|             std::cout << "Cannot parse: Token" << std::endl; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ApiKeyEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "apiKeys", apiKeys); | ||||
|     } | ||||
|  | ||||
|     bool ApiKeyEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "apiKeys", apiKeys); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|             std::cout << "Cannot parse: Token" << std::endl; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/Data/LOB.h" | ||||
| #include "Poco/Data/LOBStream.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     uint64_t Now(); | ||||
| @@ -62,7 +63,7 @@ namespace OpenWifi { | ||||
|         std::string UserTypeToString(USER_ROLE U); | ||||
|  | ||||
|         struct NoteInfo { | ||||
|             uint64_t    created=0; // = OpenWifi::Now(); | ||||
|             uint64_t    created=0; // = Utils::Now(); | ||||
|             std::string createdBy; | ||||
|             std::string note; | ||||
|  | ||||
| @@ -101,7 +102,7 @@ namespace OpenWifi { | ||||
|             std::string uuid; | ||||
|             std::string question; | ||||
|             std::string method; | ||||
|             uint64_t    created = OpenWifi::Now(); | ||||
|             uint64_t    created = Utils::Now(); | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|             bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
| @@ -251,7 +252,8 @@ namespace OpenWifi { | ||||
|             VERIFY_EMAIL, | ||||
|             SUB_FORGOT_PASSWORD, | ||||
|             SUB_VERIFY_EMAIL, | ||||
|             SUB_SIGNUP | ||||
|             SUB_SIGNUP, | ||||
|             EMAIL_INVITATION | ||||
|         }; | ||||
|  | ||||
|         struct ActionLink { | ||||
| @@ -263,7 +265,7 @@ namespace OpenWifi { | ||||
|             std::string         locale; | ||||
|             std::string         message; | ||||
|             uint64_t            sent=0; | ||||
|             uint64_t            created=OpenWifi::Now(); | ||||
|             uint64_t            created=Utils::Now(); | ||||
|             uint64_t            expires=0; | ||||
|             uint64_t            completed=0; | ||||
|             uint64_t            canceled=0; | ||||
| @@ -323,5 +325,44 @@ namespace OpenWifi { | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|         }; | ||||
|  | ||||
|         struct ApiKeyAccessRight { | ||||
|             std::string     service; | ||||
|             std::string     access; | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|             bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|         }; | ||||
|  | ||||
|         struct ApiKeyAccessRightList { | ||||
|             std::vector<ApiKeyAccessRight>      acls; | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|             bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|         }; | ||||
|  | ||||
|         struct ApiKeyEntry { | ||||
|             Types::UUID_t           id; | ||||
|             Types::UUID_t           userUuid; | ||||
|             std::string             name; | ||||
|             std::string             description; | ||||
|             std::string             apiKey; | ||||
|             std::string             salt; | ||||
|             std::uint64_t           created; | ||||
|             std::uint64_t           expiresOn=0; | ||||
|             ApiKeyAccessRightList   rights; | ||||
|             std::uint64_t           lastUse=0; | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|             bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|         }; | ||||
|  | ||||
|         struct ApiKeyEntryList { | ||||
|             std::vector<ApiKeyEntry>    apiKeys; | ||||
|  | ||||
|             void to_json(Poco::JSON::Object &Obj) const; | ||||
|             bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|         }; | ||||
|  | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,12 +3,11 @@ | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_SubObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
|  | ||||
|  | ||||
| namespace OpenWifi::SubObjects { | ||||
|  | ||||
|     void HomeDeviceMode::to_json(Poco::JSON::Object &Obj) const { | ||||
|   | ||||
| @@ -3,9 +3,11 @@ | ||||
| // | ||||
|  | ||||
| #include "GW_SDK.h" | ||||
| #include "Daemon.h" | ||||
| #include "Poco/Net/HTTPResponse.h" | ||||
|  | ||||
| #include "framework/MicroServiceNames.h" | ||||
| #include "framework/OpenAPIRequests.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::GW { | ||||
|  | ||||
|     bool SendFirmwareUpgradeCommand( const std::string & serialNumber, const std::string & URI, [[maybe_unused]] uint64_t When  ) { | ||||
|   | ||||
| @@ -2,11 +2,9 @@ | ||||
| // Created by stephane bourque on 2021-10-05. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_GW_SDK_H | ||||
| #define OWFMS_GW_SDK_H | ||||
| #pragma once | ||||
|  | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include <string> | ||||
|  | ||||
| namespace OpenWifi::SDK::GW { | ||||
|  | ||||
| @@ -14,4 +12,3 @@ namespace OpenWifi::SDK::GW { | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif //OWFMS_GW_SDK_H | ||||
|   | ||||
| @@ -2,7 +2,8 @@ | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/OpenAPIRequests.h" | ||||
| #include "framework/MicroServiceNames.h" | ||||
|  | ||||
| namespace OpenWifi::SDK::Prov { | ||||
|     bool GetFirmwareOptions( const std::string & serialNumber, std::string &firmwareUpgrade, | ||||
|   | ||||
| @@ -2,10 +2,9 @@ | ||||
| // Created by stephane bourque on 2021-10-04. | ||||
| // | ||||
|  | ||||
| #ifndef OWFMS_PROV_SDK_H | ||||
| #define OWFMS_PROV_SDK_H | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include <string> | ||||
|  | ||||
| namespace OpenWifi::SDK::Prov { | ||||
|  | ||||
| @@ -14,5 +13,3 @@ namespace OpenWifi::SDK::Prov { | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
| #endif //OWFMS_PROV_SDK_H | ||||
|   | ||||
| @@ -6,10 +6,8 @@ | ||||
| //	Arilia Wireless Inc. | ||||
| // | ||||
|  | ||||
| #ifndef UCENTRAL_USTORAGESERVICE_H | ||||
| #define UCENTRAL_USTORAGESERVICE_H | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/StorageClass.h" | ||||
|  | ||||
| #include "RESTObjects/RESTAPI_FMSObjects.h" | ||||
| @@ -53,4 +51,3 @@ namespace OpenWifi { | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| #endif //UCENTRAL_USTORAGESERVICE_H | ||||
|   | ||||
							
								
								
									
										77
									
								
								src/framework/ALBserver.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/framework/ALBserver.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "ALBserver.h" | ||||
|  | ||||
| #include "framework/utils.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	void ALBRequestHandler::handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) { | ||||
| 		Utils::SetThreadName("alb-request"); | ||||
| 		try { | ||||
| 			if((id_ % 100) == 0) { | ||||
| 				Logger_.debug(fmt::format("ALB-REQUEST({}): ALB Request {}.", Request.clientAddress().toString(), id_)); | ||||
| 			} | ||||
| 			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 << "process Alive and kicking!"; | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	ALBRequestHandlerFactory::ALBRequestHandlerFactory(Poco::Logger & L): | ||||
| 		Logger_(L) { | ||||
| 	} | ||||
|  | ||||
| 	ALBRequestHandler* ALBRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) { | ||||
| 		if (request.getURI() == "/") | ||||
| 			return new ALBRequestHandler(Logger_, req_id_++); | ||||
| 		else | ||||
| 			return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	ALBHealthCheckServer::ALBHealthCheckServer() : | ||||
| 		  SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") | ||||
| 	{ | ||||
| 	} | ||||
|  | ||||
| 	int ALBHealthCheckServer::Start() { | ||||
| 		if(MicroServiceConfigGetBool("alb.enable",false)) { | ||||
|             poco_information(Logger(),"Starting..."); | ||||
| 			Running_=true; | ||||
| 			Port_ = (int)MicroServiceConfigGetInt("alb.port",15015); | ||||
| 			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_); | ||||
| 			Poco::Net::ServerSocket ClientSocket(SockAddr, 64); | ||||
|  | ||||
| 			Socket_ = std::make_unique<Poco::Net::ServerSocket>(SockAddr, Port_); | ||||
| 			auto Params = new Poco::Net::HTTPServerParams; | ||||
| 			Params->setName("ws:alb"); | ||||
| 			Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger()), *Socket_, Params); | ||||
| 			Server_->start(); | ||||
| 		} | ||||
|  | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	void ALBHealthCheckServer::Stop() { | ||||
| 		poco_information(Logger(),"Stopping..."); | ||||
| 		if(Running_) | ||||
| 			Server_->stopAll(true); | ||||
| 		poco_information(Logger(),"Stopped..."); | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										63
									
								
								src/framework/ALBserver.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/framework/ALBserver.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,63 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| #include "Poco/Net/HTTPRequestHandler.h" | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
| #include "Poco/Net/HTTPRequestHandlerFactory.h" | ||||
| #include "Poco/Net/HTTPServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class ALBRequestHandler: public Poco::Net::HTTPRequestHandler { | ||||
| 	  public: | ||||
| 		explicit ALBRequestHandler(Poco::Logger & L, uint64_t id) | ||||
|                 : Logger_(L), id_(id) { | ||||
|         } | ||||
|  | ||||
| 		void handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override; | ||||
|  | ||||
| 	  private: | ||||
| 		Poco::Logger 	& Logger_; | ||||
| 		uint64_t 		id_; | ||||
| 	}; | ||||
|  | ||||
| 	class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory | ||||
| 	{ | ||||
| 	  public: | ||||
| 		explicit ALBRequestHandlerFactory(Poco::Logger & L); | ||||
| 		ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override; | ||||
|  | ||||
| 	  private: | ||||
| 		Poco::Logger	                            &Logger_; | ||||
| 		inline static std::atomic_uint64_t 			req_id_=1; | ||||
| 	}; | ||||
|  | ||||
| 	class ALBHealthCheckServer : public SubSystemServer { | ||||
| 	  public: | ||||
| 		ALBHealthCheckServer(); | ||||
|  | ||||
| 		static auto instance() { | ||||
| 			static auto instance = new ALBHealthCheckServer; | ||||
| 			return instance; | ||||
| 		} | ||||
|  | ||||
| 		int Start() override; | ||||
| 		void Stop() override; | ||||
|  | ||||
| 	  private: | ||||
| 		std::unique_ptr<Poco::Net::HTTPServer>   	Server_; | ||||
| 		std::unique_ptr<Poco::Net::ServerSocket> 	Socket_; | ||||
| 		int                                     	Port_ = 0; | ||||
| 		mutable std::atomic_bool                    Running_=false; | ||||
| 	}; | ||||
|  | ||||
| 	inline auto ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|  | ||||
| @@ -4,8 +4,14 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
| #include "Poco/Net/HTTPSClientSession.h" | ||||
| #include "Poco/URI.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     inline void API_Proxy( Poco::Logger &Logger, | ||||
| @@ -15,7 +21,7 @@ namespace OpenWifi { | ||||
|                     const char * PathRewrite, | ||||
|                     uint64_t msTimeout_ = 10000 ) { | ||||
|         try { | ||||
|             auto Services = MicroService::instance().GetServices(ServiceType); | ||||
|             auto Services = MicroServiceGetServices(ServiceType); | ||||
|             for(auto const &Svc:Services) { | ||||
|                 Poco::URI   SourceURI(Request->getURI()); | ||||
|                 Poco::URI	DestinationURI(Svc.PrivateEndPoint); | ||||
| @@ -35,7 +41,7 @@ namespace OpenWifi { | ||||
|                     ProxyRequest.add("Authorization", Request->get("Authorization")); | ||||
|                 } else { | ||||
|                     ProxyRequest.add("X-API-KEY", Svc.AccessKey); | ||||
|                     ProxyRequest.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint()); | ||||
|                     ProxyRequest.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); | ||||
|                 } | ||||
|  | ||||
|                 if(Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) { | ||||
|   | ||||
							
								
								
									
										102
									
								
								src/framework/AppServiceRegistry.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/framework/AppServiceRegistry.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <fstream> | ||||
| #include <iomanip> | ||||
| #include <iostream> | ||||
|  | ||||
| #include "Poco/StreamCopier.h" | ||||
| #include "Poco/File.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| #include "nlohmann/json.hpp" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|  | ||||
| 	class AppServiceRegistry { | ||||
| 	  public: | ||||
| 		AppServiceRegistry() { | ||||
| 			FileName = MicroServiceDataDirectory() + "/registry.json"; | ||||
| 			Poco::File F(FileName); | ||||
|  | ||||
| 			try { | ||||
| 				if(F.exists()) { | ||||
| 					std::ostringstream  OS; | ||||
| 					std::ifstream       IF(FileName); | ||||
| 					Poco::StreamCopier::copyStream(IF, OS); | ||||
| 					Registry_ = nlohmann::json::parse(OS.str()); | ||||
| 				} | ||||
| 			} catch (...) { | ||||
| 				Registry_ = nlohmann::json::parse("{}"); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		static AppServiceRegistry & instance() { | ||||
| 			static auto instance_= new AppServiceRegistry; | ||||
| 			return *instance_; | ||||
| 		} | ||||
|  | ||||
| 		inline ~AppServiceRegistry() { | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Save() { | ||||
| 			std::istringstream  IS( to_string(Registry_)); | ||||
| 			std::ofstream       OF; | ||||
| 			OF.open(FileName,std::ios::binary | std::ios::trunc); | ||||
| 			Poco::StreamCopier::copyStream(IS, OF); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, uint64_t Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, const std::string &Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, bool Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, bool & Value ) { | ||||
| 			if(Registry_[Key].is_boolean()) { | ||||
| 				Value = Registry_[Key].get<bool>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, uint64_t & Value ) { | ||||
| 			if(Registry_[Key].is_number_unsigned()) { | ||||
| 				Value = Registry_[Key].get<uint64_t>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, std::string & Value ) { | ||||
| 			if(Registry_[Key].is_string()) { | ||||
| 				Value = Registry_[Key].get<std::string>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 	  private: | ||||
| 		std::string         FileName; | ||||
| 		nlohmann::json      Registry_; | ||||
| 	}; | ||||
|  | ||||
| 	inline auto AppServiceRegistry() { return AppServiceRegistry::instance(); } | ||||
|  | ||||
| } | ||||
							
								
								
									
										129
									
								
								src/framework/AuthClient.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/framework/AuthClient.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,129 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
|  | ||||
| #include "framework/AuthClient.h" | ||||
| #include "framework/MicroServiceNames.h" | ||||
| #include "framework/OpenAPIRequests.h" | ||||
| #include "framework/utils.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	bool AuthClient::RetrieveTokenInformation(const std::string & SessionToken, | ||||
| 										 SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
| 										 std::uint64_t TID, | ||||
| 										 bool & Expired, bool & Contacted, bool Sub) { | ||||
| 		try { | ||||
| 			Types::StringPairVec QueryData; | ||||
| 			QueryData.push_back(std::make_pair("token",SessionToken)); | ||||
|             std::string     AlternateURIForLogging = fmt::format("{}?token={}",  Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", Utils::SanitizeToken(SessionToken)); | ||||
| 			OpenAPIRequestGet	Req(    uSERVICE_SECURITY, | ||||
| 								  Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", | ||||
| 								  QueryData, | ||||
| 								  10000, | ||||
| 								  AlternateURIForLogging | ||||
|                                   ); | ||||
| 			Poco::JSON::Object::Ptr Response; | ||||
|  | ||||
| 			auto StatusCode = Req.Do(Response); | ||||
| 			if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) { | ||||
| 				Contacted = false; | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			Contacted = true; | ||||
| 			if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) { | ||||
| 				if(Response->has("tokenInfo") && Response->has("userInfo")) { | ||||
| 					UInfo.from_json(Response); | ||||
| 					if(IsTokenExpired(UInfo.webtoken)) { | ||||
| 						Expired = true; | ||||
| 						return false; | ||||
| 					} | ||||
| 					Expired = false; | ||||
| 					Cache_.update(SessionToken, UInfo); | ||||
| 					return true; | ||||
| 				} else { | ||||
| 					return false; | ||||
| 				} | ||||
| 			} | ||||
| 		} catch (...) { | ||||
| 			poco_error(Logger(),fmt::format("Failed to retrieve token={} for TID={}", Utils::SanitizeToken(SessionToken), TID)); | ||||
| 		} | ||||
| 		Expired = false; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	bool AuthClient::IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
| 							 std::uint64_t TID, | ||||
| 							 bool & Expired, bool & Contacted, bool Sub) { | ||||
| 		auto User = Cache_.get(SessionToken); | ||||
| 		if(!User.isNull()) { | ||||
| 			if(IsTokenExpired(User->webtoken)) { | ||||
| 				Expired = true; | ||||
| 				Cache_.remove(SessionToken); | ||||
| 				return false; | ||||
| 			} | ||||
| 			Expired = false; | ||||
| 			UInfo = *User; | ||||
| 			return true; | ||||
| 		} | ||||
| 		return RetrieveTokenInformation(SessionToken, UInfo, TID, Expired, Contacted, Sub); | ||||
| 	} | ||||
|  | ||||
|     bool AuthClient::RetrieveApiKeyInformation(const std::string & SessionToken, | ||||
|                                               SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
|                                               std::uint64_t TID, | ||||
|                                               bool & Expired, bool & Contacted) { | ||||
|         try { | ||||
|             Types::StringPairVec QueryData; | ||||
|             QueryData.push_back(std::make_pair("apikey",SessionToken)); | ||||
|             std::string     AlternateURIForLogging = fmt::format("/api/v1/validateApiKey?apiKey={}", Utils::SanitizeToken(SessionToken)); | ||||
|             OpenAPIRequestGet	Req(    uSERVICE_SECURITY, | ||||
|                                          "/api/v1/validateApiKey" , | ||||
|                                          QueryData, | ||||
|                                          10000, | ||||
|                                          AlternateURIForLogging); | ||||
|             Poco::JSON::Object::Ptr Response; | ||||
|  | ||||
|             auto StatusCode = Req.Do(Response); | ||||
|             if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) { | ||||
|                 Contacted = false; | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             Contacted = true; | ||||
|             if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) { | ||||
|                 if(Response->has("tokenInfo") && Response->has("userInfo") && Response->has("expiresOn")) { | ||||
|                     UInfo.from_json(Response); | ||||
|                     Expired = false; | ||||
|                     ApiKeyCache_.update(SessionToken, ApiKeyCacheEntry{ .UserInfo = UInfo, .ExpiresOn = Response->get("expiresOn")}); | ||||
|                     return true; | ||||
|                 } else { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } catch (...) { | ||||
|             poco_error(Logger(),fmt::format("Failed to retrieve api key={} for TID={}", Utils::SanitizeToken(SessionToken), TID)); | ||||
|         } | ||||
|         Expired = false; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool AuthClient::IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy &UInfo, | ||||
|                                    std::uint64_t TID, bool &Expired, bool &Contacted) { | ||||
|         auto User = ApiKeyCache_.get(SessionToken); | ||||
|         if (!User.isNull()) { | ||||
|             if(User->ExpiresOn < Utils::Now()) { | ||||
|                 Expired = false; | ||||
|                 UInfo = User->UserInfo; | ||||
|                 return true; | ||||
|             } | ||||
| 			ApiKeyCache_.remove(SessionToken); | ||||
|         } | ||||
|         return RetrieveApiKeyInformation(SessionToken, UInfo, TID, Expired, Contacted); | ||||
|     } | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										79
									
								
								src/framework/AuthClient.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/framework/AuthClient.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "Poco/ExpireLRUCache.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class AuthClient : public SubSystemServer { | ||||
|  | ||||
| 	  public: | ||||
| 		explicit AuthClient() noexcept: | ||||
| 			 SubSystemServer("Authentication", "AUTH-CLNT", "authentication") | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new AuthClient; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
|         struct ApiKeyCacheEntry { | ||||
|             OpenWifi::SecurityObjects::UserInfoAndPolicy    UserInfo; | ||||
|             std::uint64_t                                   ExpiresOn; | ||||
|         }; | ||||
|  | ||||
|         inline int Start() override { | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		inline void Stop() override { | ||||
| 			poco_information(Logger(),"Stopping..."); | ||||
| 			std::lock_guard	G(Mutex_); | ||||
| 			Cache_.clear(); | ||||
| 			poco_information(Logger(),"Stopped..."); | ||||
| 		} | ||||
|  | ||||
| 		inline void RemovedCachedToken(const std::string &Token) { | ||||
| 			Cache_.remove(Token); | ||||
|             ApiKeyCache_.remove(Token); | ||||
| 		} | ||||
|  | ||||
| 		inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { | ||||
| 			return ((T.expires_in_+T.created_) < Utils::Now()); | ||||
| 		} | ||||
|  | ||||
| 		bool RetrieveTokenInformation(const std::string & SessionToken, | ||||
| 			SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
| 			std::uint64_t TID, | ||||
| 		bool & Expired, bool & Contacted, bool Sub=false); | ||||
|  | ||||
|         bool RetrieveApiKeyInformation(const std::string & SessionToken, | ||||
|                                       SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
|                                       std::uint64_t TID, | ||||
|                                       bool & Expired, bool & Contacted); | ||||
|  | ||||
| 		bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
| 								 std::uint64_t TID, | ||||
| 								 bool & Expired, bool & Contacted, bool Sub = false); | ||||
|  | ||||
|         bool IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, | ||||
|                           std::uint64_t TID, | ||||
|                           bool & Expired, bool & Contacted); | ||||
|  | ||||
| 	  private: | ||||
|  | ||||
| 		Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy>      Cache_{512,1200000 }; | ||||
|         Poco::ExpireLRUCache<std::string,ApiKeyCacheEntry>                                  ApiKeyCache_{512,1200000 }; | ||||
| 	}; | ||||
|  | ||||
| 	inline auto AuthClient() { return AuthClient::instance(); } | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|  | ||||
							
								
								
									
										155
									
								
								src/framework/CIDR.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/framework/CIDR.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "Poco/Net/IPAddress.h" | ||||
| #include "Poco/StringTokenizer.h" | ||||
|  | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi::CIDR { | ||||
|  | ||||
| 	static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits) { | ||||
| 		if (bits == 0) { | ||||
| 			return true; | ||||
| 		} | ||||
| 		return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits))); | ||||
| 	} | ||||
|  | ||||
| 	static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits) { | ||||
| 	#ifdef __linux__ | ||||
| 		const uint32_t *a = address.s6_addr32; | ||||
| 		const uint32_t *n = network.s6_addr32; | ||||
| 	#else | ||||
| 		const uint32_t *a = address.__u6_addr.__u6_addr32; | ||||
| 		const uint32_t *n = network.__u6_addr.__u6_addr32; | ||||
| 	#endif | ||||
| 		int bits_whole, bits_incomplete; | ||||
| 		bits_whole = bits >> 5;		   // number of whole u32 | ||||
| 		bits_incomplete = bits & 0x1F; // number of bits in incomplete u32 | ||||
| 		if (bits_whole) { | ||||
| 			if (memcmp(a, n, bits_whole << 2) != 0) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 		if (bits_incomplete) { | ||||
| 			uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete)); | ||||
| 			if ((a[bits_whole] ^ n[bits_whole]) & mask) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	static bool ConvertStringToLong(const char *S, unsigned long &L) { | ||||
| 		char *end; | ||||
| 		L = std::strtol(S, &end, 10); | ||||
| 		return end != S; | ||||
| 	} | ||||
|  | ||||
| 	static bool CidrIPinRange(const Poco::Net::IPAddress &IP, const std::string &Range) { | ||||
| 		Poco::StringTokenizer TimeTokens(Range, "/", Poco::StringTokenizer::TOK_TRIM); | ||||
|  | ||||
| 		Poco::Net::IPAddress RangeIP; | ||||
| 		if (Poco::Net::IPAddress::tryParse(TimeTokens[0], RangeIP)) { | ||||
| 			if (TimeTokens.count() == 2) { | ||||
| 				if (RangeIP.family() == Poco::Net::IPAddress::IPv4) { | ||||
| 					unsigned long MaskLength; | ||||
| 					if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { | ||||
| 						return cidr_match(*static_cast<const in_addr *>(RangeIP.addr()), | ||||
| 										  *static_cast<const in_addr *>(IP.addr()), MaskLength); | ||||
| 					} | ||||
| 				} else if (RangeIP.family() == Poco::Net::IPAddress::IPv6) { | ||||
| 					unsigned long MaskLength; | ||||
| 					if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) { | ||||
| 						return cidr6_match(*static_cast<const in6_addr *>(RangeIP.addr()), | ||||
| 										   *static_cast<const in6_addr *>(IP.addr()), MaskLength); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	// | ||||
| 	//  Ranges can be a single IP, of IP1-IP2, of A set of IPs: IP1,IP2,IP3, or a cidr IP/24 | ||||
| 	//  These can work for IPv6 too... | ||||
| 	// | ||||
| 	static bool ValidateRange(const std::string &R) { | ||||
|  | ||||
| 		auto Tokens = Poco::StringTokenizer(R, "-"); | ||||
| 		if (Tokens.count() == 2) { | ||||
| 			Poco::Net::IPAddress a, b; | ||||
| 			if (!Poco::Net::IPAddress::tryParse(Tokens[0], a) && | ||||
| 				Poco::Net::IPAddress::tryParse(Tokens[1], b)) | ||||
| 				return false; | ||||
| 			return a.family() == b.family(); | ||||
| 		} | ||||
|  | ||||
| 		Tokens = Poco::StringTokenizer(R, ","); | ||||
| 		if (Tokens.count() > 1) { | ||||
| 			return std::all_of(Tokens.begin(), Tokens.end(), [](const std::string &A) { | ||||
| 				Poco::Net::IPAddress a; | ||||
| 				return Poco::Net::IPAddress::tryParse(A, a); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		Tokens = Poco::StringTokenizer(R, "/"); | ||||
| 		if (Tokens.count() == 2) { | ||||
| 			Poco::Net::IPAddress a; | ||||
| 			if (!Poco::Net::IPAddress::tryParse(Tokens[0], a)) | ||||
| 				return false; | ||||
| 			if (std::atoi(Tokens[1].c_str()) == 0) | ||||
| 				return false; | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		Poco::Net::IPAddress a; | ||||
| 		return Poco::Net::IPAddress::tryParse(R, a); | ||||
| 	} | ||||
|  | ||||
| 	static bool IpInRange(const Poco::Net::IPAddress &target, const std::string &R) { | ||||
|  | ||||
| 		auto Tokens = Poco::StringTokenizer(R, "-"); | ||||
| 		if (Tokens.count() == 2) { | ||||
| 			auto a = Poco::Net::IPAddress::parse(Tokens[0]); | ||||
| 			auto b = Poco::Net::IPAddress::parse(Tokens[1]); | ||||
| 			if (target.family() != a.family()) | ||||
| 				return false; | ||||
| 			return (a <= target && b >= target); | ||||
| 		} | ||||
|  | ||||
| 		Tokens = Poco::StringTokenizer(R, ","); | ||||
| 		if (Tokens.count() > 1) { | ||||
| 			return std::any_of(Tokens.begin(), Tokens.end(), [target](const std::string &Element) { | ||||
| 				return Poco::Net::IPAddress::parse(Element) == target; | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		Tokens = Poco::StringTokenizer(R, "/"); | ||||
| 		if (Tokens.count() == 2) { | ||||
| 			return CidrIPinRange(target, R); | ||||
| 		} | ||||
|  | ||||
| 		return Poco::Net::IPAddress::parse(R) == target; | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] inline bool IpInRanges(const std::string &IP, const Types::StringVec &R) { | ||||
| 		Poco::Net::IPAddress Target; | ||||
|  | ||||
| 		if (!Poco::Net::IPAddress::tryParse(IP, Target)) | ||||
| 			return false; | ||||
|  | ||||
| 		return std::any_of(cbegin(R), cend(R), | ||||
| 						   [Target](const std::string &i) { return IpInRange(Target, i); }); | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] inline bool ValidateIpRanges(const Types::StringVec &Ranges) { | ||||
| 		return std::all_of(cbegin(Ranges), cend(Ranges), ValidateRange); | ||||
| 	} | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -5,7 +5,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <nlohmann/json-schema.hpp> | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| using nlohmann::json; | ||||
| using nlohmann::json_schema::json_validator; | ||||
| @@ -32,7 +32,7 @@ namespace OpenWifi { | ||||
|         nlohmann::json  RootSchema_; | ||||
|  | ||||
|         ConfigurationValidator(): | ||||
|             SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") { | ||||
|             SubSystemServer("ConfigValidator", "CFG-VALIDATOR", "config.validator") { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										49
									
								
								src/framework/EventBusManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/framework/EventBusManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #include "framework/EventBusManager.h" | ||||
| #include "framework/KafkaManager.h" | ||||
| #include "framework/utils.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	EventBusManager::EventBusManager(Poco::Logger &L) : | ||||
| 		Logger_(L) { | ||||
| 	} | ||||
|  | ||||
| 	void EventBusManager::run() { | ||||
| 		Running_ = true; | ||||
| 		Utils::SetThreadName("fmwk:EventMgr"); | ||||
| 		auto Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); | ||||
| 		KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); | ||||
| 		while(Running_) { | ||||
| 			Poco::Thread::trySleep((unsigned long)MicroServiceDaemonBusTimer()); | ||||
| 			if(!Running_) | ||||
| 				break; | ||||
| 			Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE); | ||||
| 			KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); | ||||
| 		} | ||||
| 		Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE); | ||||
| 		KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false); | ||||
| 	}; | ||||
|  | ||||
| 	void EventBusManager::Start() { | ||||
|         poco_information(Logger(),"Starting..."); | ||||
| 		if(KafkaManager()->Enabled()) { | ||||
| 			Thread_.start(*this); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void EventBusManager::Stop() { | ||||
| 		if(KafkaManager()->Enabled()) { | ||||
| 			poco_information(Logger(),"Stopping..."); | ||||
| 			Running_ = false; | ||||
| 			Thread_.wakeUp(); | ||||
| 			Thread_.join(); | ||||
| 			poco_information(Logger(),"Stopped..."); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										28
									
								
								src/framework/EventBusManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/framework/EventBusManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Runnable.h" | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/Thread.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class EventBusManager : public Poco::Runnable { | ||||
| 	  public: | ||||
| 		explicit EventBusManager(Poco::Logger &L); | ||||
| 		void run() final; | ||||
| 		void Start(); | ||||
| 		void Stop(); | ||||
| 		inline Poco::Logger & Logger() { return Logger_; } | ||||
|  | ||||
| 	  private: | ||||
| 		mutable std::atomic_bool 	Running_ = false; | ||||
| 		Poco::Thread		Thread_; | ||||
| 		Poco::Logger		&Logger_; | ||||
| 	}; | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|  | ||||
							
								
								
									
										365
									
								
								src/framework/KafkaManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										365
									
								
								src/framework/KafkaManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,365 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "KafkaManager.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	void KafkaLoggerFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string &message) { | ||||
| 		switch ((cppkafka::LogLevel) level) { | ||||
| 		case cppkafka::LogLevel::LogNotice: { | ||||
| 			poco_notice(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		case cppkafka::LogLevel::LogDebug: { | ||||
| 			poco_debug(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		case cppkafka::LogLevel::LogInfo: { | ||||
| 			poco_information(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		case cppkafka::LogLevel::LogWarning: { | ||||
| 			poco_warning(KafkaManager()->Logger(), fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		case cppkafka::LogLevel::LogAlert: | ||||
| 		case cppkafka::LogLevel::LogCrit: { | ||||
| 			poco_critical(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		case cppkafka::LogLevel::LogErr: | ||||
| 		case cppkafka::LogLevel::LogEmerg: | ||||
| 		default: { | ||||
| 			poco_error(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message)); | ||||
| 		} | ||||
| 		break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline void KafkaErrorFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int error, const std::string &reason) { | ||||
| 		poco_error(KafkaManager()->Logger(),fmt::format("kafka-error: {}, reason: {}", error, reason)); | ||||
| 	} | ||||
|  | ||||
| 	inline void AddKafkaSecurity(cppkafka::Configuration & Config) { | ||||
| 		auto CA = MicroServiceConfigGetString("openwifi.kafka.ssl.ca.location",""); | ||||
| 		auto Certificate = MicroServiceConfigGetString("openwifi.kafka.ssl.certificate.location",""); | ||||
| 		auto Key = MicroServiceConfigGetString("openwifi.kafka.ssl.key.location",""); | ||||
| 		auto Password = MicroServiceConfigGetString("openwifi.kafka.ssl.key.password",""); | ||||
|  | ||||
| 		if(CA.empty() || Certificate.empty() || Key.empty()) | ||||
| 			return; | ||||
|  | ||||
| 		Config.set("ssl.ca.location", CA); | ||||
| 		Config.set("ssl.certificate.location", Certificate); | ||||
| 		Config.set("ssl.key.location", Key); | ||||
| 		if(!Password.empty()) | ||||
| 			Config.set("ssl.key.password", Password); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	void KafkaManager::initialize(Poco::Util::Application & self) { | ||||
| 		SubSystemServer::initialize(self); | ||||
| 		KafkaEnabled_ = MicroServiceConfigGetBool("openwifi.kafka.enable",false); | ||||
| 	} | ||||
|  | ||||
| 	inline void KafkaProducer::run() { | ||||
|         Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-PRODUCER", KafkaManager()->Logger().getChannel()); | ||||
|         poco_information(Logger_,"Starting..."); | ||||
|  | ||||
| 		Utils::SetThreadName("Kafka:Prod"); | ||||
| 		cppkafka::Configuration Config({ | ||||
| 			{ "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id", "") }, | ||||
| 			{ "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist", "") } | ||||
| 		}); | ||||
|  | ||||
| 		AddKafkaSecurity(Config); | ||||
|  | ||||
| 		Config.set_log_callback(KafkaLoggerFun); | ||||
| 		Config.set_error_callback(KafkaErrorFun); | ||||
|  | ||||
| 		KafkaManager()->SystemInfoWrapper_ = 	R"lit({ "system" : { "id" : )lit" + | ||||
| 											 std::to_string(MicroServiceID()) + | ||||
| 											 R"lit( , "host" : ")lit" + MicroServicePrivateEndPoint() + | ||||
| 											 R"lit(" } , "payload" : )lit" ; | ||||
|  | ||||
| 		cppkafka::Producer	Producer(Config); | ||||
| 		Running_ = true; | ||||
|  | ||||
| 		Poco::AutoPtr<Poco::Notification>	Note(Queue_.waitDequeueNotification()); | ||||
| 		while(Note && Running_) { | ||||
| 			try { | ||||
| 				auto Msg = dynamic_cast<KafkaMessage *>(Note.get()); | ||||
| 				if (Msg != nullptr) { | ||||
| 					Producer.produce( | ||||
| 						cppkafka::MessageBuilder(Msg->Topic()).key(Msg->Key()).payload(Msg->Payload())); | ||||
| 				} | ||||
| 			} catch (const cppkafka::HandleException &E) { | ||||
| 				poco_warning(Logger_,fmt::format("Caught a Kafka exception (producer): {}", E.what())); | ||||
| 			} catch( const Poco::Exception &E) { | ||||
| 				Logger_.log(E); | ||||
| 			} catch (...) { | ||||
| 				poco_error(Logger_,"std::exception"); | ||||
| 			} | ||||
| 			Note = Queue_.waitDequeueNotification(); | ||||
| 		} | ||||
|         poco_information(Logger_,"Stopped..."); | ||||
| 	} | ||||
|  | ||||
| 	inline void KafkaConsumer::run() { | ||||
| 		Utils::SetThreadName("Kafka:Cons"); | ||||
|  | ||||
|         Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-CONSUMER", KafkaManager()->Logger().getChannel()); | ||||
|  | ||||
|         poco_information(Logger_,"Starting..."); | ||||
|  | ||||
| 		cppkafka::Configuration Config({ | ||||
| 			{ "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id","") }, | ||||
| 			{ "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist","") }, | ||||
| 			{ "group.id", MicroServiceConfigGetString("openwifi.kafka.group.id","") }, | ||||
| 			{ "enable.auto.commit", MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false) }, | ||||
| 			{ "auto.offset.reset", "latest" } , | ||||
| 			{ "enable.partition.eof", false } | ||||
| 		}); | ||||
|  | ||||
| 		AddKafkaSecurity(Config); | ||||
|  | ||||
| 		Config.set_log_callback(KafkaLoggerFun); | ||||
| 		Config.set_error_callback(KafkaErrorFun); | ||||
|  | ||||
| 		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([&](cppkafka::TopicPartitionList& partitions) { | ||||
| 			if(!partitions.empty()) { | ||||
| 				poco_information(Logger_,fmt::format("Partition assigned: {}...", | ||||
| 																 partitions.front().get_partition())); | ||||
| 			} | ||||
| 		}); | ||||
| 		Consumer.set_revocation_callback([&](const cppkafka::TopicPartitionList& partitions) { | ||||
| 			if(!partitions.empty()) { | ||||
|                 poco_information(Logger_,fmt::format("Partition revocation: {}...", | ||||
| 																 partitions.front().get_partition())); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		bool AutoCommit = MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false); | ||||
| 		auto BatchSize = MicroServiceConfigGetInt("openwifi.kafka.consumer.batchsize",20); | ||||
|  | ||||
| 		Types::StringVec    Topics; | ||||
| 		KafkaManager()->Topics(Topics); | ||||
| 		Consumer.subscribe(Topics); | ||||
|  | ||||
| 		Running_ = true; | ||||
| 		while(Running_) { | ||||
| 			try { | ||||
| 				std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(100)); | ||||
| 				for(auto const &Msg:MsgVec) { | ||||
| 					if (!Msg) | ||||
| 						continue; | ||||
| 					if (Msg.get_error()) { | ||||
| 						if (!Msg.is_eof()) { | ||||
| 							poco_error(Logger_,fmt::format("Error: {}", Msg.get_error().to_string())); | ||||
| 						} | ||||
| 						if(!AutoCommit) | ||||
| 							Consumer.async_commit(Msg); | ||||
| 						continue; | ||||
| 					} | ||||
| 					KafkaManager()->Dispatch(Msg.get_topic(), Msg.get_key(),Msg.get_payload() ); | ||||
| 					if (!AutoCommit) | ||||
| 						Consumer.async_commit(Msg); | ||||
| 				} | ||||
| 			} catch (const cppkafka::HandleException &E) { | ||||
| 				poco_warning(Logger_,fmt::format("Caught a Kafka exception (consumer): {}", E.what())); | ||||
| 			} catch (const Poco::Exception &E) { | ||||
| 				Logger_.log(E); | ||||
| 			} catch (...) { | ||||
| 				poco_error(Logger_,"std::exception"); | ||||
| 			} | ||||
| 		} | ||||
| 		Consumer.unsubscribe(); | ||||
|         poco_information(Logger_,"Stopped..."); | ||||
| 	} | ||||
|  | ||||
| 	void KafkaProducer::Start() { | ||||
| 		if(!Running_) { | ||||
| 			Running_=true; | ||||
| 			Worker_.start(*this); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaProducer::Stop() { | ||||
| 		if(Running_) { | ||||
| 			Running_=false; | ||||
| 			Queue_.wakeUpAll(); | ||||
| 			Worker_.join(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaProducer::Produce(const std::string &Topic, const std::string &Key, const std::string &Payload) { | ||||
| 		std::lock_guard	G(Mutex_); | ||||
| 		Queue_.enqueueNotification( new KafkaMessage(Topic,Key,Payload)); | ||||
| 	} | ||||
|  | ||||
| 	void KafkaConsumer::Start() { | ||||
| 		if(!Running_) { | ||||
| 			Running_=true; | ||||
| 			Worker_.start(*this); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaConsumer::Stop() { | ||||
| 		if(Running_) { | ||||
| 			Running_=false; | ||||
| 			Worker_.wakeUp(); | ||||
| 			Worker_.join(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::Start() { | ||||
| 		if(!Running_) { | ||||
| 			Running_=true; | ||||
| 			Worker_.start(*this); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::Stop() { | ||||
| 		if(Running_) { | ||||
| 			Running_=false; | ||||
| 			Queue_.wakeUpAll(); | ||||
| 			Worker_.join(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	auto KafkaDispatcher::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { | ||||
| 		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_++; | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::UnregisterTopicWatcher(const std::string &Topic, int Id) { | ||||
| 		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; | ||||
| 				} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload) { | ||||
| 		std::lock_guard	G(Mutex_); | ||||
| 		auto It = Notifiers_.find(Topic); | ||||
| 		if(It!=Notifiers_.end()) { | ||||
| 			Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::run() { | ||||
|         Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-DISPATCHER", KafkaManager()->Logger().getChannel()); | ||||
|         poco_information(Logger_,"Starting..."); | ||||
| 		Poco::AutoPtr<Poco::Notification>	Note(Queue_.waitDequeueNotification()); | ||||
| 		Utils::SetThreadName("kafka:dispatch"); | ||||
| 		while(Note && Running_) { | ||||
| 			auto Msg = dynamic_cast<KafkaMessage*>(Note.get()); | ||||
| 			if(Msg!= nullptr) { | ||||
| 				auto It = Notifiers_.find(Msg->Topic()); | ||||
| 				if (It != Notifiers_.end()) { | ||||
| 					const auto & FL = It->second; | ||||
| 					for(const auto &[CallbackFunc,_]:FL) { | ||||
| 						CallbackFunc(Msg->Key(), Msg->Payload()); | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			Note = Queue_.waitDequeueNotification(); | ||||
| 		} | ||||
|         poco_information(Logger_,"Stopped..."); | ||||
| 	} | ||||
|  | ||||
| 	void KafkaDispatcher::Topics(std::vector<std::string> &T) { | ||||
| 		T.clear(); | ||||
| 		for(const auto &[TopicName,_]:Notifiers_) | ||||
| 			T.push_back(TopicName); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	int KafkaManager::Start() { | ||||
| 		if(!KafkaEnabled_) | ||||
| 			return 0; | ||||
|         ConsumerThr_.Start(); | ||||
| 		ProducerThr_.Start(); | ||||
| 		Dispatcher_.Start(); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::Stop()  { | ||||
| 		if(KafkaEnabled_) { | ||||
| 			poco_information(Logger(),"Stopping..."); | ||||
| 			Dispatcher_.Stop(); | ||||
| 			ProducerThr_.Stop(); | ||||
| 			ConsumerThr_.Stop(); | ||||
| 			poco_information(Logger(),"Stopped..."); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) { | ||||
| 		if(KafkaEnabled_) { | ||||
| 			ProducerThr_.Produce(topic,key,WrapMessage ? WrapSystemId(PayLoad) : PayLoad); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload) { | ||||
| 		Dispatcher_.Dispatch(Topic, Key, Payload); | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string KafkaManager::WrapSystemId(const std::string & PayLoad) { | ||||
| 		return SystemInfoWrapper_ + PayLoad + "}"; | ||||
| 	} | ||||
|  | ||||
| 	uint64_t KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { | ||||
| 		if(KafkaEnabled_) { | ||||
| 			return Dispatcher_.RegisterTopicWatcher(Topic,F); | ||||
| 		} else { | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) { | ||||
| 		if(KafkaEnabled_) { | ||||
| 			Dispatcher_.UnregisterTopicWatcher(Topic, Id); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::Topics(std::vector<std::string> &T) { | ||||
| 		Dispatcher_.Topics(T); | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { | ||||
| 		poco_information(Logger(),fmt::format("Partition assigned: {}...", partitions.front().get_partition())); | ||||
| 	} | ||||
|  | ||||
| 	void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { | ||||
| 		poco_information(Logger(),fmt::format("Partition revocation: {}...",partitions.front().get_partition())); | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										122
									
								
								src/framework/KafkaManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/framework/KafkaManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Notification.h" | ||||
| #include "Poco/NotificationQueue.h" | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "framework/utils.h" | ||||
| #include "framework/KafkaTopics.h" | ||||
|  | ||||
| #include "cppkafka/cppkafka.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class KafkaMessage: public Poco::Notification { | ||||
| 	  public: | ||||
| 		KafkaMessage( const std::string &Topic, const std::string &Key, const std::string & Payload) : | ||||
| 			Topic_(Topic), Key_(Key), Payload_(Payload) { | ||||
| 		} | ||||
|  | ||||
| 		inline const std::string & Topic() { return Topic_; } | ||||
| 		inline const std::string & Key() { return Key_; } | ||||
| 		inline const std::string & Payload() { return Payload_; } | ||||
|  | ||||
| 	  private: | ||||
| 		std::string	Topic_; | ||||
| 		std::string	Key_; | ||||
| 		std::string	Payload_; | ||||
| 	}; | ||||
|  | ||||
| 	class KafkaProducer : public Poco::Runnable { | ||||
|         public: | ||||
|             void run () override; | ||||
|             void Start(); | ||||
|             void Stop(); | ||||
|             void Produce(const std::string &Topic, const std::string &Key, const std::string &Payload); | ||||
|  | ||||
| 	  private: | ||||
|             std::recursive_mutex  		Mutex_; | ||||
|             Poco::Thread        		Worker_; | ||||
|             mutable std::atomic_bool    Running_=false; | ||||
|             Poco::NotificationQueue		Queue_; | ||||
|     }; | ||||
|  | ||||
| 	class KafkaConsumer : public Poco::Runnable { | ||||
| 	  public: | ||||
| 		void run() override; | ||||
| 		void Start(); | ||||
| 		void Stop(); | ||||
|  | ||||
| 	  private: | ||||
| 		std::recursive_mutex  	    Mutex_; | ||||
| 		Poco::Thread        	    Worker_; | ||||
| 		mutable std::atomic_bool    Running_=false; | ||||
| 	}; | ||||
|  | ||||
| 	class KafkaDispatcher : public Poco::Runnable { | ||||
| 	  public: | ||||
| 		void Start(); | ||||
| 		void Stop(); | ||||
| 		auto RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F); | ||||
| 		void UnregisterTopicWatcher(const std::string &Topic, int Id); | ||||
| 		void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload); | ||||
| 		void run() override; | ||||
| 		void Topics(std::vector<std::string> &T); | ||||
|  | ||||
| 	  private: | ||||
| 		std::recursive_mutex  		Mutex_; | ||||
| 		Types::NotifyTable      	Notifiers_; | ||||
| 		Poco::Thread        		Worker_; | ||||
| 		mutable std::atomic_bool    Running_=false; | ||||
| 		uint64_t          			FunctionId_=1; | ||||
| 		Poco::NotificationQueue		Queue_; | ||||
| 	}; | ||||
|  | ||||
| 	class KafkaManager : public SubSystemServer { | ||||
| 	  public: | ||||
|  | ||||
| 		friend class KafkaConsumer; | ||||
| 		friend class KafkaProducer; | ||||
|  | ||||
| 		inline void initialize(Poco::Util::Application & self) override; | ||||
|  | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new KafkaManager; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
| 		int Start() override; | ||||
| 		void Stop() override; | ||||
|  | ||||
| 		void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true  ); | ||||
| 		void Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload); | ||||
| 		[[nodiscard]] std::string WrapSystemId(const std::string & PayLoad); | ||||
| 		[[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; } | ||||
| 		uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F); | ||||
| 		void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id); | ||||
| 		void Topics(std::vector<std::string> &T); | ||||
|  | ||||
| 	  private: | ||||
| 		bool 				KafkaEnabled_ = false; | ||||
| 		std::string 		SystemInfoWrapper_; | ||||
| 		KafkaProducer       ProducerThr_; | ||||
|         KafkaConsumer       ConsumerThr_; | ||||
| 		KafkaDispatcher     Dispatcher_; | ||||
|  | ||||
| 		void PartitionAssignment(const cppkafka::TopicPartitionList& partitions); | ||||
| 		void PartitionRevocation(const cppkafka::TopicPartitionList& partitions); | ||||
|  | ||||
| 		KafkaManager() noexcept: | ||||
| 			SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") { | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	inline auto KafkaManager() { return KafkaManager::instance(); } | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|  | ||||
| @@ -8,6 +8,7 @@ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| namespace OpenWifi::KafkaTopics { | ||||
| 	static const std::string HEALTHCHECK{"healthcheck"}; | ||||
| 	static const std::string STATE{"state"}; | ||||
|   | ||||
							
								
								
									
										712
									
								
								src/framework/MicroService.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										712
									
								
								src/framework/MicroService.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,712 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #include "Poco/FileChannel.h" | ||||
| #include "Poco/ConsoleChannel.h" | ||||
| #include "Poco/PatternFormatter.h" | ||||
| #include "Poco/FormattingChannel.h" | ||||
| #include "Poco/AsyncChannel.h" | ||||
| #include "Poco/NullChannel.h" | ||||
| #include "Poco/SplitterChannel.h" | ||||
| #include "Poco/Net/HTTPStreamFactory.h" | ||||
| #include "Poco/Net/HTTPSStreamFactory.h" | ||||
| #include "Poco/Net/FTPSStreamFactory.h" | ||||
| #include "Poco/Net/FTPStreamFactory.h" | ||||
| #include "Poco/Net/SSLManager.h" | ||||
| #include "Poco/JSON/JSONException.h" | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/MicroServiceErrorHandler.h" | ||||
| #include "framework/UI_WebSocketClientServer.h" | ||||
| #include "framework/MicroServiceNames.h" | ||||
| #include "framework/AuthClient.h" | ||||
| #include "framework/ALBserver.h" | ||||
| #include "framework/KafkaManager.h" | ||||
| #include "framework/RESTAPI_GenericServerAccounting.h" | ||||
| #include "framework/RESTAPI_ExtServer.h" | ||||
| #include "framework/RESTAPI_IntServer.h" | ||||
| #include "framework/utils.h" | ||||
| #include "framework/WebSocketLogger.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	void MicroService::Exit(int Reason) { | ||||
| 		std::exit(Reason); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::BusMessageReceived([[maybe_unused]] const std::string &Key, const std::string & Payload) { | ||||
| 		std::lock_guard G(InfraMutex_); | ||||
| 		try { | ||||
| 			Poco::JSON::Parser P; | ||||
| 			auto Object = P.parse(Payload).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)) { | ||||
| 							auto PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(); | ||||
| 							if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(PrivateEndPoint) != Services_.end()) { | ||||
| 								Services_[PrivateEndPoint].LastUpdate = Utils::Now(); | ||||
| 							} else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { | ||||
| 								Services_.erase(PrivateEndPoint); | ||||
| 								poco_debug(logger(),fmt::format("Service {} ID={} leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); | ||||
| 							} else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { | ||||
| 								poco_debug(logger(),fmt::format("Service {} ID={} joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); | ||||
| 								Services_[PrivateEndPoint] = Types::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 = Utils::Now() }; | ||||
|  | ||||
| 								std::string SvcList; | ||||
| 								for (const auto &Svc: Services_) { | ||||
| 									if(SvcList.empty()) | ||||
| 										SvcList = Svc.second.Type; | ||||
| 									else | ||||
| 										SvcList += ", " + Svc.second.Type; | ||||
| 								} | ||||
| 								poco_information(logger(),fmt::format("Current list of microservices: {}", SvcList)); | ||||
| 							} | ||||
| 						} else { | ||||
| 							poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', 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 { | ||||
| 							poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing token",Event)); | ||||
| 						} | ||||
| 					} else { | ||||
| 						poco_error(logger(),fmt::format("Unknown Event: {} Source: {}", Event, ID)); | ||||
| 					} | ||||
| 				} | ||||
| 			} else { | ||||
| 				poco_error(logger(),"Bad bus message."); | ||||
| 			} | ||||
|  | ||||
| 			auto i=Services_.begin(); | ||||
| 			auto now = Utils::Now(); | ||||
| 			for(;i!=Services_.end();) { | ||||
| 				if((now - i->second.LastUpdate)>60) { | ||||
| 					i = Services_.erase(i); | ||||
| 				} else | ||||
| 					++i; | ||||
| 			} | ||||
|  | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 			logger().log(E); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	Types::MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { | ||||
| 		std::lock_guard G(InfraMutex_); | ||||
|  | ||||
| 		auto T = Poco::toLower(Type); | ||||
| 		Types::MicroServiceMetaVec	Res; | ||||
| 		for(const auto &[_,ServiceRec]:Services_) { | ||||
| 			if(ServiceRec.Type==T) | ||||
| 				Res.push_back(ServiceRec); | ||||
| 		} | ||||
| 		return Res; | ||||
| 	} | ||||
|  | ||||
| 	Types::MicroServiceMetaVec MicroService::GetServices() { | ||||
| 		std::lock_guard G(InfraMutex_); | ||||
|  | ||||
| 		Types::MicroServiceMetaVec	Res; | ||||
| 		for(const auto &[_,ServiceRec]:Services_) { | ||||
| 			Res.push_back(ServiceRec); | ||||
| 		} | ||||
| 		return Res; | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::LoadConfigurationFile() { | ||||
| 		std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); | ||||
| 		ConfigFileName_ = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_; | ||||
| 		Poco::Path ConfigFile(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()); | ||||
| 		PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(ConfigFile.toString()); | ||||
| 		configPtr()->addWriteable(PropConfigurationFile_, PRIO_DEFAULT); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::Reload() { | ||||
| 		LoadConfigurationFile(); | ||||
| 		LoadMyConfig(); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::LoadMyConfig() { | ||||
| 		NoAPISecurity_ = ConfigGetBool("openwifi.security.restapi.disable",false); | ||||
| 		std::string KeyFile = ConfigPath("openwifi.service.key",""); | ||||
| 		if(!KeyFile.empty()) { | ||||
| 			std::string KeyFilePassword = ConfigPath("openwifi.service.key.password", ""); | ||||
| 			AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); | ||||
| 			Cipher_ = CipherFactory_.createCipher(*AppKey_); | ||||
| 			Signer_.setRSAKey(AppKey_); | ||||
| 			Signer_.addAllAlgorithms(); | ||||
| 			NoBuiltInCrypto_ = false; | ||||
| 		} else { | ||||
| 			NoBuiltInCrypto_ = true; | ||||
| 		} | ||||
|  | ||||
| 		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_ = Utils::ComputeHash(MyPublicEndPoint_); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::InitializeLoggingSystem() { | ||||
| 		static auto initialized = false; | ||||
|  | ||||
| 		if(!initialized) { | ||||
| 			initialized = true; | ||||
| 			LoadConfigurationFile(); | ||||
|  | ||||
| 			auto LoggingDestination = MicroService::instance().ConfigGetString("logging.type", "file"); | ||||
| 			auto LoggingFormat = MicroService::instance().ConfigGetString("logging.format", | ||||
| 																		  "%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t"); | ||||
| 			auto UseAsyncLogs_ = MicroService::instance().ConfigGetBool("logging.asynch", true); | ||||
| 			auto DisableWebSocketLogging = MicroService::instance().ConfigGetBool("logging.websocket",false); | ||||
|  | ||||
| 			if (LoggingDestination == "null") { | ||||
| 				Poco::AutoPtr<Poco::NullChannel> DevNull(new Poco::NullChannel); | ||||
| 				Poco::Logger::root().setChannel(DevNull); | ||||
| 			} else if (LoggingDestination == "console") { | ||||
|                 SetConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat); | ||||
| 			} else if (LoggingDestination == "colorconsole") { | ||||
|                 SetColorConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat); | ||||
| 			} else if (LoggingDestination == "sql") { | ||||
|                 SetSQLLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat); | ||||
| 			} else if (LoggingDestination == "syslog") { | ||||
|                 SetSyslogLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat); | ||||
| 			} else { | ||||
|                 SetFileLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat, DAEMON_ROOT_ENV_VAR); | ||||
|             } | ||||
|  | ||||
| 			auto Level = Poco::Logger::parseLevel(MicroService::instance().ConfigGetString("logging.level", "debug")); | ||||
| 			Poco::Logger::root().setLevel(Level); | ||||
| 			if(!DisableWebSocketLogging) { | ||||
| 				static const UI_WebSocketClientServer::NotificationTypeIdVec Notifications = { | ||||
| 					{1, "log"}}; | ||||
| 				UI_WebSocketClientServer()->RegisterNotifications(Notifications); | ||||
| 			} | ||||
| 		} | ||||
|     } | ||||
|  | ||||
|     void MicroService::SetConsoleLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern) { | ||||
|  | ||||
|         Poco::AutoPtr<Poco::ConsoleChannel> Console(new Poco::ConsoleChannel); | ||||
|         Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter); | ||||
|         Formatter->setProperty("pattern", FormatterPattern); | ||||
|         Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, Console)); | ||||
|  | ||||
|         if(DisableWebSocketLogging) { | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(FormattingChannel); | ||||
|             } | ||||
|         } else { | ||||
|             Poco::AutoPtr<WebSocketLogger>			WSLogger(new WebSocketLogger); | ||||
|             Poco::AutoPtr<Poco::SplitterChannel>	Splitter(new Poco::SplitterChannel); | ||||
|             Splitter->addChannel(WSLogger); | ||||
|             Splitter->addChannel(FormattingChannel); | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(Splitter); | ||||
|             } | ||||
|         } | ||||
| 		Poco::Logger::root().information(fmt::format("Enabled console logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging)); | ||||
|     } | ||||
|  | ||||
|     void MicroService::SetColorConsoleLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern) { | ||||
|  | ||||
|         Poco::AutoPtr<Poco::ColorConsoleChannel> Console(new Poco::ColorConsoleChannel); | ||||
|         Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter); | ||||
|         Formatter->setProperty("pattern", FormatterPattern); | ||||
|         Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, Console)); | ||||
|  | ||||
|         if(DisableWebSocketLogging) { | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(FormattingChannel); | ||||
|             } | ||||
|         } else { | ||||
|             Poco::AutoPtr<WebSocketLogger>			WSLogger(new WebSocketLogger); | ||||
|             Poco::AutoPtr<Poco::SplitterChannel>	Splitter(new Poco::SplitterChannel); | ||||
|             Splitter->addChannel(WSLogger); | ||||
|             Splitter->addChannel(FormattingChannel); | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(Splitter); | ||||
|             } | ||||
|         } | ||||
| 		Poco::Logger::root().information(fmt::format("Enabled color console logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging)); | ||||
|     } | ||||
|  | ||||
|     void MicroService::SetSQLLogs([[maybe_unused]] bool UseAsync,[[maybe_unused]]  bool DisableWebSocketLogging,[[maybe_unused]]  const std::string & FormatterPattern) { | ||||
|         //"CREATE TABLE T_POCO_LOG (Source VARCHAR, Name VARCHAR, ProcessId INTEGER, Thread VARCHAR, ThreadId INTEGER, Priority INTEGER, Text VARCHAR, DateTime DATE)" | ||||
|     } | ||||
|  | ||||
|     void MicroService::SetSyslogLogs([[maybe_unused]] bool UseAsync,[[maybe_unused]]  bool DisableWebSocketLogging,[[maybe_unused]]  const std::string & FormatterPattern) { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     void MicroService::SetFileLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern, const std::string & root_env_var) { | ||||
|         std::string DefaultLogPath = fmt::format("${}/logs",root_env_var); | ||||
|         auto LoggingLocationDir = MicroService::instance().ConfigPath("logging.path", DefaultLogPath); | ||||
|         Poco::File      LD(LoggingLocationDir); | ||||
|         try { | ||||
|             if(!LD.exists()) { | ||||
|                 LD.createDirectory(); | ||||
|             } | ||||
|         } catch(const Poco::Exception &E) { | ||||
|             std::cout << "Cannot create " << LD.path() << "  Error: " << E.message() << std::endl; | ||||
|         } | ||||
|         auto LoggingLocationDirFilePattern = LoggingLocationDir + "/log"; | ||||
|  | ||||
|         Poco::AutoPtr<Poco::FileChannel> FileChannel(new Poco::FileChannel); | ||||
|         FileChannel->setProperty("rotation", "10 M"); | ||||
|         FileChannel->setProperty("archive", "timestamp"); | ||||
|         FileChannel->setProperty("purgeCount", "10"); | ||||
|         FileChannel->setProperty("path", LoggingLocationDirFilePattern); | ||||
|  | ||||
|         Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter); | ||||
|         Formatter->setProperty("pattern", FormatterPattern); | ||||
|         Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, FileChannel)); | ||||
|  | ||||
|         if(DisableWebSocketLogging) { | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(FormattingChannel); | ||||
|             } | ||||
|         } else { | ||||
|             Poco::AutoPtr<WebSocketLogger>			WSLogger(new WebSocketLogger); | ||||
|             Poco::AutoPtr<Poco::SplitterChannel>	Splitter(new Poco::SplitterChannel); | ||||
|             Splitter->addChannel(WSLogger); | ||||
|             Splitter->addChannel(FormattingChannel); | ||||
|             if(UseAsync) { | ||||
|                 Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter)); | ||||
|                 Poco::Logger::root().setChannel(Async); | ||||
|             } else { | ||||
|                 Poco::Logger::root().setChannel(Splitter); | ||||
|             } | ||||
|         } | ||||
| 		Poco::Logger::root().information(fmt::format("Enabled file logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging)); | ||||
|     } | ||||
|  | ||||
| 	void DaemonPostInitialization(Poco::Util::Application &self); | ||||
|  | ||||
| 	void MicroService::initialize(Poco::Util::Application &self) { | ||||
| 		// add the default services | ||||
| 		LoadConfigurationFile(); | ||||
| 		InitializeLoggingSystem(); | ||||
|  | ||||
| 		SubSystems_.push_back(KafkaManager()); | ||||
| 		SubSystems_.push_back(ALBHealthCheckServer()); | ||||
| 		SubSystems_.push_back(RESTAPI_ExtServer()); | ||||
| 		SubSystems_.push_back(RESTAPI_IntServer()); | ||||
| 	#ifndef TIP_SECURITY_SERVICE | ||||
| 		SubSystems_.push_back(AuthClient()); | ||||
| 	#endif | ||||
| 		Poco::Net::initializeSSL(); | ||||
| 		Poco::Net::HTTPStreamFactory::registerFactory(); | ||||
| 		Poco::Net::HTTPSStreamFactory::registerFactory(); | ||||
| 		Poco::Net::FTPStreamFactory::registerFactory(); | ||||
| 		Poco::Net::FTPSStreamFactory::registerFactory(); | ||||
|  | ||||
| 		Poco::File	DataDir(ConfigPath("openwifi.system.data")); | ||||
| 		DataDir_ = DataDir.path(); | ||||
| 		if(!DataDir.exists()) { | ||||
| 			try { | ||||
| 				DataDir.createDirectory(); | ||||
| 			} catch (const Poco::Exception &E) { | ||||
| 				logger().log(E); | ||||
| 			} | ||||
| 		} | ||||
| 		WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets",""); | ||||
| 		if(WWWAssetsDir_.empty()) | ||||
| 			WWWAssetsDir_ = DataDir_; | ||||
|  | ||||
| 		LoadMyConfig(); | ||||
|  | ||||
| 		InitializeSubSystemServers(); | ||||
| 		ServerApplication::initialize(self); | ||||
| 		DaemonPostInitialization(self); | ||||
|  | ||||
| 		Types::TopicNotifyFunction F = [this](const std::string &Key,const std::string &Payload) { this->BusMessageReceived(Key, Payload); }; | ||||
| 		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([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { | ||||
| 		HelpRequested_ = true; | ||||
| 		displayHelp(); | ||||
| 		stopOptionsProcessing(); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::handleVersion([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) { | ||||
| 		HelpRequested_ = true; | ||||
| 		std::cout << Version() << std::endl; | ||||
| 		stopOptionsProcessing(); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::handleDebug([[maybe_unused]] const std::string &name, const std::string &value) { | ||||
| 		if(value == "true") | ||||
| 			DebugMode_ = true ; | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::handleLogs([[maybe_unused]] const std::string &name, const std::string &value) { | ||||
| 		LogDir_ = value; | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::handleConfig([[maybe_unused]] 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() { | ||||
| 		AddActivity("Starting"); | ||||
| 		for(auto i:SubSystems_) { | ||||
| 			i->Start(); | ||||
| 		} | ||||
| 		EventBusManager_ = std::make_unique<EventBusManager>(Poco::Logger::create("EventBusManager",Poco::Logger::root().getChannel(),Poco::Logger::root().getLevel())); | ||||
| 		EventBusManager_->Start(); | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::StopSubSystemServers() { | ||||
| 		AddActivity("Stopping"); | ||||
| 		EventBusManager_->Stop(); | ||||
| 		for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) { | ||||
| 			(*i)->Stop(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string MicroService::CreateUUID() { | ||||
| 		static std::random_device              rd; | ||||
| 		static std::mt19937_64                 gen(rd()); | ||||
| 		static std::uniform_int_distribution<> dis(0, 15); | ||||
| 		static std::uniform_int_distribution<> dis2(8, 11); | ||||
|  | ||||
| 		std::stringstream ss; | ||||
| 		int i; | ||||
| 		ss << std::hex; | ||||
| 		for (i = 0; i < 8; i++) { | ||||
| 			ss << dis(gen); | ||||
| 		} | ||||
| 		ss << "-"; | ||||
| 		for (i = 0; i < 4; i++) { | ||||
| 			ss << dis(gen); | ||||
| 		} | ||||
| 		ss << "-4"; | ||||
| 		for (i = 0; i < 3; i++) { | ||||
| 			ss << dis(gen); | ||||
| 		} | ||||
| 		ss << "-"; | ||||
| 		ss << dis2(gen); | ||||
| 		for (i = 0; i < 3; i++) { | ||||
| 			ss << dis(gen); | ||||
| 		} | ||||
| 		ss << "-"; | ||||
| 		for (i = 0; i < 12; i++) { | ||||
| 			ss << dis(gen); | ||||
| 		}; | ||||
| 		return ss.str(); | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 				for (auto i : SubSystems_) { | ||||
| 					if (Sub == Poco::toLower(i->Name())) { | ||||
| 						i->Logger().setLevel(P); | ||||
| 						return true; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} catch (const Poco::Exception & E) { | ||||
| 			std::cerr << "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) { | ||||
| 		if(NoBuiltInCrypto_) { | ||||
| 			return S; | ||||
| 		} | ||||
| 		return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; | ||||
| 	} | ||||
|  | ||||
| 	std::string MicroService::Decrypt(const std::string &S) { | ||||
| 		if(NoBuiltInCrypto_) { | ||||
| 			return S; | ||||
| 		} | ||||
| 		return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; | ||||
| 	} | ||||
|  | ||||
| 	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(); | ||||
| 	} | ||||
|  | ||||
| 	[[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(MicroService::instance().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([[maybe_unused]] const ArgVec &args) { | ||||
| 		MicroServiceErrorHandler	ErrorHandler(*this); | ||||
| 		Poco::ErrorHandler::set(&ErrorHandler); | ||||
|  | ||||
| 		if (!HelpRequested_) { | ||||
| 			SavePID(); | ||||
|  | ||||
| 			Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); | ||||
| 			logger.notice(fmt::format("Starting {} version {}.",DAEMON_APP_NAME, Version())); | ||||
|  | ||||
| 			if(Poco::Net::Socket::supportsIPv6()) | ||||
| 				poco_information(logger,"System supports IPv6."); | ||||
| 			else | ||||
| 				poco_information(logger,"System does NOT support IPv6."); | ||||
|  | ||||
| 			if (config().getBool("application.runAsDaemon", false)) { | ||||
| 				poco_information(logger,"Starting as a daemon."); | ||||
| 			} | ||||
|  | ||||
| 			poco_information(logger,fmt::format("System ID set to {}",ID_)); | ||||
| 			StartSubSystemServers(); | ||||
| 			waitForTerminationRequest(); | ||||
| 			StopSubSystemServers(); | ||||
| 			logger.notice(fmt::format("Stopped {}...",DAEMON_APP_NAME)); | ||||
| 		} | ||||
|  | ||||
| 		return Application::EXIT_OK; | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::AddActivity(const std::string &Activity) { | ||||
| 		if(!DataDir_.empty()) { | ||||
| 			std::string ActivityFile{ DataDir_ + "/activity.log"}; | ||||
| 			try { | ||||
| 				std::ofstream of(ActivityFile,std::ios_base::app | std::ios_base::out ); | ||||
| 				auto t = std::chrono::system_clock::now(); | ||||
| 				std::time_t now = std::chrono::system_clock::to_time_t(t); | ||||
| 				of << Activity << " at " << std::ctime(&now) ; | ||||
| 			} catch (...) { | ||||
|  | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] std::string MicroService::Sign(Poco::JWT::Token &T, const std::string &Algo) { | ||||
| 		if(NoBuiltInCrypto_) { | ||||
| 			return T.toString(); | ||||
| 		} else { | ||||
| 			return Signer_.sign(T,Algo); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void MicroService::DeleteOverrideConfiguration() { | ||||
| 		Poco::File	F(DataDir_ + ExtraConfigurationFilename); | ||||
|  | ||||
| 		try { | ||||
| 			if(F.exists()) | ||||
| 				F.remove(); | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -10,6 +10,7 @@ | ||||
| #include "Poco/Net/NetException.h" | ||||
| #include "Poco/Net/SSLException.h" | ||||
| #include "Poco/JSON/Template.h" | ||||
| #include "Poco/JSON/JSONException.h" | ||||
| #include "Poco/Thread.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| @@ -101,6 +102,48 @@ namespace OpenWifi { | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::TimeoutException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::TimeoutException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::NoThreadAvailableException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::NoThreadAvailableException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::OutOfMemoryException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::OutOfMemoryException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::BadCastException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::BadCastException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::DataException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::DataException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::PoolOverflowException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::PoolOverflowException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::SystemException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::SystemException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
| 													  E.displayText(), | ||||
| 													  E.message(), | ||||
| 													  E.what())); | ||||
| 			} catch (const Poco::RuntimeException &E) { | ||||
| 				poco_error(App_.logger(), fmt::format("Poco::RuntimeException thr_name={} thr_id={} code={} text={} msg={} what={}", | ||||
| 													  t_name, t_id, E.code(), | ||||
|   | ||||
							
								
								
									
										132
									
								
								src/framework/MicroServiceExtra.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										132
									
								
								src/framework/MicroServiceExtra.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,132 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <map> | ||||
|  | ||||
| #include "Poco/BasicEvent.h" | ||||
| #include "Poco/ExpireLRUCache.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class ConfigurationEntry { | ||||
| 	  public: | ||||
| 		template <typename T> explicit ConfigurationEntry(T def) : | ||||
| 											 Default_(def), | ||||
| 											 Current_(def){ | ||||
| 		} | ||||
|  | ||||
| 		template <typename T> explicit ConfigurationEntry(T def, T cur, const std::string  &Hint="") : | ||||
| 																				  Default_(def), | ||||
| 																				  Current_(cur), | ||||
| 																				  Hint_(Hint){ | ||||
| 		} | ||||
|  | ||||
| 		inline ConfigurationEntry()=default; | ||||
| 		inline ~ConfigurationEntry()=default; | ||||
|  | ||||
| 		template <typename T> explicit operator T () const { return std::get<T>(Current_); } | ||||
| 		inline ConfigurationEntry & operator=(const char *v) { Current_ = std::string(v); return *this;} | ||||
| 		template <typename T> ConfigurationEntry &  operator=(T v) { Current_ = (T) v; return *this;} | ||||
|  | ||||
| 		void reset() { | ||||
| 			Current_ = Default_; | ||||
| 		} | ||||
|  | ||||
| 	  private: | ||||
| 		std::variant<bool,uint64_t,std::string> Default_, Current_; | ||||
| 		std::string Hint_; | ||||
| 	}; | ||||
| 	inline std::string to_string(const ConfigurationEntry &v) { return (std::string) v; } | ||||
|  | ||||
| 	typedef std::map<std::string,ConfigurationEntry>    ConfigurationMap_t; | ||||
|  | ||||
| 	template <typename T> class FIFO { | ||||
| 	  public: | ||||
| 		explicit FIFO(uint32_t Size) : | ||||
| 									   Size_(Size) { | ||||
| 			Buffer_ = new T [Size_]; | ||||
| 		} | ||||
|  | ||||
| 		~FIFO() { | ||||
| 			delete [] Buffer_; | ||||
| 		} | ||||
|  | ||||
| 		mutable Poco::BasicEvent<bool> Writable_; | ||||
| 		mutable Poco::BasicEvent<bool> Readable_; | ||||
|  | ||||
| 		inline bool Read(T &t) { | ||||
| 			{ | ||||
| 				std::lock_guard M(Mutex_); | ||||
| 				if (Write_ == Read_) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				t = Buffer_[Read_++]; | ||||
| 				if (Read_ == Size_) { | ||||
| 					Read_ = 0; | ||||
| 				} | ||||
| 				Used_--; | ||||
| 			} | ||||
| 			bool flag = true; | ||||
| 			Writable_.notify(this, flag); | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		inline bool Write(const T &t) { | ||||
| 			{ | ||||
| 				std::lock_guard M(Mutex_); | ||||
|  | ||||
| 				Buffer_[Write_++] = t; | ||||
| 				if (Write_ == Size_) { | ||||
| 					Write_ = 0; | ||||
| 				} | ||||
| 				Used_++; | ||||
| 				MaxEverUsed_ = std::max(Used_,MaxEverUsed_); | ||||
| 			} | ||||
| 			bool flag = true; | ||||
| 			Readable_.notify(this, flag); | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline bool isFull() { | ||||
| 			std::lock_guard M(Mutex_); | ||||
| 			return Used_==Buffer_->capacity(); | ||||
| 		} | ||||
|  | ||||
| 		inline auto MaxEverUser() const { return MaxEverUsed_; } | ||||
|  | ||||
| 	  private: | ||||
| 		std::recursive_mutex    Mutex_; | ||||
| 		uint32_t 				Size_=0; | ||||
| 		uint32_t        		Read_=0; | ||||
| 		uint32_t        		Write_=0; | ||||
| 		uint32_t 				Used_=0; | ||||
| 		uint32_t 				MaxEverUsed_=0; | ||||
| 		T	  					* Buffer_ = nullptr; | ||||
| 	}; | ||||
|  | ||||
| 	template <class Record, typename KeyType = std::string, int Size=256, int Expiry=60000> class RecordCache { | ||||
| 	  public: | ||||
| 		explicit RecordCache( KeyType Record::* Q) : | ||||
| 												   MemberOffset(Q){ | ||||
| 												   }; | ||||
| 		inline auto update(const Record &R) { | ||||
| 			return Cache_.update(R.*MemberOffset, R); | ||||
| 		} | ||||
| 		inline auto get(const KeyType &K) { | ||||
| 			return Cache_.get(K); | ||||
| 		} | ||||
| 		inline auto remove(const KeyType &K) { | ||||
| 			return Cache_.remove(K); | ||||
| 		} | ||||
| 		inline auto remove(const Record &R) { | ||||
| 			return Cache_.remove(R.*MemberOffset); | ||||
| 		} | ||||
| 	  private: | ||||
| 		KeyType Record::* MemberOffset; | ||||
| 		Poco::ExpireLRUCache<KeyType,Record>  Cache_{Size,Expiry}; | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										121
									
								
								src/framework/MicroServiceFuncs.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								src/framework/MicroServiceFuncs.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,121 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     const std::string &MicroServiceDataDirectory() { return MicroService::instance().DataDir(); } | ||||
|  | ||||
|     Types::MicroServiceMetaVec MicroServiceGetServices(const std::string &Type) { | ||||
|         return MicroService::instance().GetServices(Type); | ||||
|     } | ||||
|  | ||||
|     Types::MicroServiceMetaVec MicroServiceGetServices() { | ||||
|         return MicroService::instance().GetServices(); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServicePublicEndPoint() { return MicroService::instance().PublicEndPoint(); } | ||||
|  | ||||
|     std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue) { | ||||
|         return MicroService::instance().ConfigGetString(Key, DefaultValue); | ||||
|     } | ||||
|  | ||||
|     bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue) { | ||||
|         return MicroService::instance().ConfigGetBool(Key, DefaultValue); | ||||
|     } | ||||
|  | ||||
|     std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue) { | ||||
|         return MicroService::instance().ConfigGetInt(Key, DefaultValue); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServicePrivateEndPoint() { return MicroService::instance().PrivateEndPoint(); } | ||||
|  | ||||
|     std::uint64_t MicroServiceID() { return MicroService::instance().ID(); } | ||||
|  | ||||
|     bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) { | ||||
|         return MicroService::instance().IsValidAPIKEY(Request); | ||||
|     } | ||||
|  | ||||
|     bool MicroServiceNoAPISecurity() { return MicroService::instance().NoAPISecurity(); } | ||||
|  | ||||
|     void MicroServiceLoadConfigurationFile() { MicroService::instance().LoadConfigurationFile(); } | ||||
|  | ||||
|     void MicroServiceReload() { MicroService::instance().Reload(); } | ||||
|  | ||||
|     void MicroServiceReload(const std::string &Type) { MicroService::instance().Reload(Type); } | ||||
|  | ||||
|     const Types::StringVec MicroServiceGetLogLevelNames() { | ||||
|         return MicroService::instance().GetLogLevelNames(); | ||||
|     } | ||||
|  | ||||
|     const Types::StringVec MicroServiceGetSubSystems() { | ||||
|         return MicroService::instance().GetSubSystems(); | ||||
|     } | ||||
|  | ||||
|     Types::StringPairVec MicroServiceGetLogLevels() { return MicroService::instance().GetLogLevels(); } | ||||
|  | ||||
|     bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { | ||||
|         return MicroService::instance().SetSubsystemLogLevel(SubSystem, Level); | ||||
|     } | ||||
|  | ||||
|     void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer) { | ||||
|         MicroService::instance().GetExtraConfiguration(Answer); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceVersion() { return MicroService::instance().Version(); } | ||||
|  | ||||
|     std::uint64_t MicroServiceUptimeTotalSeconds() { | ||||
|         return MicroService::instance().uptime().totalSeconds(); | ||||
|     } | ||||
|  | ||||
|     std::uint64_t MicroServiceStartTimeEpochTime() { | ||||
|         return MicroService::instance().startTime().epochTime(); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceGetUIURI() { return MicroService::instance().GetUIURI(); } | ||||
|  | ||||
|     const SubSystemVec MicroServiceGetFullSubSystems() { | ||||
|         return MicroService::instance().GetFullSubSystems(); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceCreateUUID() { return MicroService::CreateUUID(); } | ||||
|  | ||||
|     std::uint64_t MicroServiceDaemonBusTimer() { return MicroService::instance().DaemonBusTimer(); } | ||||
|  | ||||
|     std::string MicroServiceMakeSystemEventMessage(const std::string &Type) { | ||||
|         return MicroService::instance().MakeSystemEventMessage(Type); | ||||
|     } | ||||
|  | ||||
|     Poco::ThreadPool &MicroServiceTimerPool() { return MicroService::instance().TimerPool(); } | ||||
|  | ||||
|     std::string MicroServiceConfigPath(const std::string &Key, | ||||
|                                        const std::string &DefaultValue) { | ||||
|         return MicroService::instance().ConfigPath(Key, DefaultValue); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceWWWAssetsDir() { | ||||
|         return MicroService::instance().WWWAssetsDir(); | ||||
|     } | ||||
|  | ||||
|     std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End) { | ||||
|         return MicroService::instance().Random(Start, End); | ||||
|     } | ||||
|  | ||||
|     std::uint64_t MicroServiceRandom(std::uint64_t Range) { | ||||
|         return MicroService::instance().Random(Range); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo) { | ||||
|         return MicroService::instance().Sign(T, Algo); | ||||
|     } | ||||
|  | ||||
|     std::string MicroServiceGetPublicAPIEndPoint() { | ||||
|         return MicroService::instance().GetPublicAPIEndPoint(); | ||||
|     } | ||||
|  | ||||
| 	void MicroServiceDeleteOverrideConfiguration() { | ||||
| 		return MicroService::instance().DeleteOverrideConfiguration(); | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										56
									
								
								src/framework/MicroServiceFuncs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/framework/MicroServiceFuncs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/ThreadPool.h" | ||||
| #include "Poco/JWT/Token.h" | ||||
|  | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class SubSystemServer; | ||||
| 	using SubSystemVec=std::vector<SubSystemServer *>; | ||||
| 	const std::string & MicroServiceDataDirectory(); | ||||
| 	Types::MicroServiceMetaVec MicroServiceGetServices(const std::string & Type); | ||||
|     Types::MicroServiceMetaVec MicroServiceGetServices(); | ||||
| 	std::string MicroServicePublicEndPoint(); | ||||
| 	std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue); | ||||
| 	bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue); | ||||
| 	std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue); | ||||
| 	std::string MicroServicePrivateEndPoint(); | ||||
| 	std::uint64_t MicroServiceID(); | ||||
| 	bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | ||||
| 	bool MicroServiceNoAPISecurity(); | ||||
| 	void MicroServiceLoadConfigurationFile(); | ||||
| 	void MicroServiceReload(); | ||||
| 	void MicroServiceReload(const std::string &Type); | ||||
| 	const Types::StringVec MicroServiceGetLogLevelNames(); | ||||
| 	const Types::StringVec MicroServiceGetSubSystems(); | ||||
| 	Types::StringPairVec MicroServiceGetLogLevels(); | ||||
| 	bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); | ||||
| 	void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer); | ||||
| 	std::string MicroServiceVersion(); | ||||
| 	std::uint64_t MicroServiceUptimeTotalSeconds(); | ||||
| 	std::uint64_t MicroServiceStartTimeEpochTime(); | ||||
| 	std::string MicroServiceGetUIURI(); | ||||
| 	const SubSystemVec MicroServiceGetFullSubSystems(); | ||||
| 	std::string MicroServiceCreateUUID(); | ||||
| 	std::uint64_t MicroServiceDaemonBusTimer(); | ||||
| 	std::string MicroServiceMakeSystemEventMessage( const std::string & Type ); | ||||
| 	Poco::ThreadPool & MicroServiceTimerPool(); | ||||
| 	std::string MicroServiceConfigPath(const std::string &Key, | ||||
| 									   const std::string &DefaultValue); | ||||
|     std::string MicroServiceWWWAssetsDir(); | ||||
|     std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End); | ||||
|     std::uint64_t MicroServiceRandom(std::uint64_t Range); | ||||
|     std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo); | ||||
|     std::string MicroServiceGetPublicAPIEndPoint(); | ||||
| 	void MicroServiceDeleteOverrideConfiguration(); | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/framework/MicroServiceNames.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/framework/MicroServiceNames.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| 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"}; | ||||
| 	static const std::string uSERVICE_SUBCRIBER{ "owsub"}; | ||||
| 	static const std::string uSERVICE_INSTALLER{ "owinst"}; | ||||
| 	static const std::string uSERVICE_ANALYTICS{ "owanalytics"}; | ||||
| 	static const std::string uSERVICE_OWRRM{ "owrrm"}; | ||||
|  | ||||
| } | ||||
							
								
								
									
										289
									
								
								src/framework/OpenAPIRequests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								src/framework/OpenAPIRequests.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,289 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "OpenAPIRequests.h" | ||||
|  | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/URI.h" | ||||
| #include "Poco/Net/HTTPRequest.h" | ||||
| #include "Poco/Net/HTTPSClientSession.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
|  | ||||
| #include "fmt/format.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { | ||||
| 	try { | ||||
|  | ||||
| 		auto Services = MicroServiceGetServices(Type_); | ||||
| 		for(auto const &Svc:Services) { | ||||
| 			Poco::URI	URI(Svc.PrivateEndPoint); | ||||
|  | ||||
| 			auto Secure = (URI.getScheme() == "https"); | ||||
|  | ||||
| 			URI.setPath(EndPoint_); | ||||
| 			for (const auto &qp : QueryData_) | ||||
| 				URI.addQueryParameter(qp.first, qp.second); | ||||
|  | ||||
| 			std::string Path(URI.getPathAndQuery()); | ||||
| 			Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 										   Path, | ||||
| 										   Poco::Net::HTTPMessage::HTTP_1_1); | ||||
|  | ||||
| 			poco_debug(Poco::Logger::get("REST-CALLER-GET"), fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) ); | ||||
|  | ||||
| 			if(BearerToken.empty()) { | ||||
| 				Request.add("X-API-KEY", Svc.AccessKey); | ||||
| 				Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); | ||||
| 			} else { | ||||
| 				// Authorization: Bearer ${token} | ||||
| 				Request.add("Authorization", "Bearer " + BearerToken); | ||||
| 			} | ||||
|  | ||||
| 			if(Secure) { | ||||
| 				Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
|  | ||||
| 				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(); | ||||
| 			} else { | ||||
| 				Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
|  | ||||
| 				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) | ||||
| 	{ | ||||
| 		Poco::Logger::get("REST-CALLER-GET").log(E); | ||||
| 	} | ||||
| 	return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; | ||||
| } | ||||
|  | ||||
| 	Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { | ||||
| 	try { | ||||
| 		auto Services = MicroServiceGetServices(Type_); | ||||
| 		for(auto const &Svc:Services) { | ||||
| 			Poco::URI	URI(Svc.PrivateEndPoint); | ||||
|  | ||||
| 			auto Secure = (URI.getScheme() == "https"); | ||||
|  | ||||
| 			URI.setPath(EndPoint_); | ||||
| 			for (const auto &qp : QueryData_) | ||||
| 				URI.addQueryParameter(qp.first, qp.second); | ||||
|  | ||||
| 			poco_debug(Poco::Logger::get("REST-CALLER-PUT"), fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) ); | ||||
|  | ||||
| 			std::string Path(URI.getPathAndQuery()); | ||||
|  | ||||
| 			Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_PUT, | ||||
| 										   Path, | ||||
| 										   Poco::Net::HTTPMessage::HTTP_1_1); | ||||
| 			std::ostringstream obody; | ||||
| 			Poco::JSON::Stringifier::stringify(Body_,obody); | ||||
|  | ||||
| 			Request.setContentType("application/json"); | ||||
| 			Request.setContentLength(obody.str().size()); | ||||
|  | ||||
| 			if(BearerToken.empty()) { | ||||
| 				Request.add("X-API-KEY", Svc.AccessKey); | ||||
| 				Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); | ||||
| 			} else { | ||||
| 				// Authorization: Bearer ${token} | ||||
| 				Request.add("Authorization", "Bearer " + BearerToken); | ||||
| 			} | ||||
|  | ||||
| 			if(Secure) { | ||||
| 				Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
|  | ||||
| 				std::ostream &os = Session.sendRequest(Request); | ||||
| 				os << obody.str(); | ||||
|  | ||||
| 				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>(); | ||||
| 				} else { | ||||
| 					Poco::JSON::Parser P; | ||||
| 					ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
| 				} | ||||
| 				return Response.getStatus(); | ||||
| 			} else { | ||||
| 				Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
|  | ||||
| 				std::ostream &os = Session.sendRequest(Request); | ||||
| 				os << obody.str(); | ||||
|  | ||||
| 				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>(); | ||||
| 				} else { | ||||
| 					Poco::JSON::Parser P; | ||||
| 					ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
| 				} | ||||
| 				return Response.getStatus(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	catch (const Poco::Exception &E) | ||||
| 	{ | ||||
| 		Poco::Logger::get("REST-CALLER-PUT").log(E); | ||||
| 	} | ||||
| 	return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; | ||||
| } | ||||
|  | ||||
| 	Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) { | ||||
| 	try { | ||||
| 		auto Services = MicroServiceGetServices(Type_); | ||||
|  | ||||
| 		for(auto const &Svc:Services) { | ||||
| 			Poco::URI	URI(Svc.PrivateEndPoint); | ||||
|  | ||||
|  | ||||
| 			auto Secure = (URI.getScheme() == "https"); | ||||
|  | ||||
| 			URI.setPath(EndPoint_); | ||||
| 			for (const auto &qp : QueryData_) | ||||
| 				URI.addQueryParameter(qp.first, qp.second); | ||||
|  | ||||
| 			poco_debug(Poco::Logger::get("REST-CALLER-POST"),fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) ); | ||||
|  | ||||
| 			std::string Path(URI.getPathAndQuery()); | ||||
|  | ||||
| 			Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST, | ||||
| 										   Path, | ||||
| 										   Poco::Net::HTTPMessage::HTTP_1_1); | ||||
| 			std::ostringstream obody; | ||||
| 			Poco::JSON::Stringifier::stringify(Body_,obody); | ||||
|  | ||||
| 			Request.setContentType("application/json"); | ||||
| 			Request.setContentLength(obody.str().size()); | ||||
|  | ||||
| 			if(BearerToken.empty()) { | ||||
| 				Request.add("X-API-KEY", Svc.AccessKey); | ||||
| 				Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); | ||||
| 			} else { | ||||
| 				// Authorization: Bearer ${token} | ||||
| 				Request.add("Authorization", "Bearer " + BearerToken); | ||||
| 			} | ||||
|  | ||||
| 			if(Secure) { | ||||
| 				Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
| 				std::ostream &os = Session.sendRequest(Request); | ||||
| 				os << obody.str(); | ||||
|  | ||||
| 				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>(); | ||||
| 				} else { | ||||
| 					Poco::JSON::Parser P; | ||||
| 					ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
| 				} | ||||
| 				return Response.getStatus(); | ||||
| 			} else { | ||||
| 				Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
| 				std::ostream &os = Session.sendRequest(Request); | ||||
| 				os << obody.str(); | ||||
|  | ||||
| 				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>(); | ||||
| 				} else { | ||||
| 					Poco::JSON::Parser P; | ||||
| 					ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
| 				} | ||||
| 				return Response.getStatus(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	catch (const Poco::Exception &E) | ||||
| 	{ | ||||
| 		Poco::Logger::get("REST-CALLER-POST").log(E); | ||||
| 	} | ||||
| 	return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; | ||||
| } | ||||
|  | ||||
| 	Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestDelete::Do(const std::string & BearerToken) { | ||||
| 	try { | ||||
| 		auto Services = MicroServiceGetServices(Type_); | ||||
|  | ||||
| 		for(auto const &Svc:Services) { | ||||
| 			Poco::URI	URI(Svc.PrivateEndPoint); | ||||
|  | ||||
| 			auto Secure = (URI.getScheme() == "https"); | ||||
|  | ||||
| 			URI.setPath(EndPoint_); | ||||
| 			for (const auto &qp : QueryData_) | ||||
| 				URI.addQueryParameter(qp.first, qp.second); | ||||
|  | ||||
| 			poco_debug(Poco::Logger::get("REST-CALLER-DELETE"),fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) ); | ||||
|  | ||||
| 			std::string Path(URI.getPathAndQuery()); | ||||
|  | ||||
| 			Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_DELETE, | ||||
| 										   Path, | ||||
| 										   Poco::Net::HTTPMessage::HTTP_1_1); | ||||
| 			if(BearerToken.empty()) { | ||||
| 				Request.add("X-API-KEY", Svc.AccessKey); | ||||
| 				Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint()); | ||||
| 			} else { | ||||
| 				// Authorization: Bearer ${token} | ||||
| 				Request.add("Authorization", "Bearer " + BearerToken); | ||||
| 			} | ||||
|  | ||||
| 			if(Secure) { | ||||
| 				Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
| 				Session.sendRequest(Request); | ||||
| 				Poco::Net::HTTPResponse Response; | ||||
| 				Session.receiveResponse(Response); | ||||
| 				return Response.getStatus(); | ||||
| 			} else { | ||||
| 				Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort()); | ||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000)); | ||||
| 				Session.sendRequest(Request); | ||||
| 				Poco::Net::HTTPResponse Response; | ||||
| 				Session.receiveResponse(Response); | ||||
| 				return Response.getStatus(); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	catch (const Poco::Exception &E) | ||||
| 	{ | ||||
| 		Poco::Logger::get("REST-CALLER-DELETE").log(E); | ||||
| 	} | ||||
| 	return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT; | ||||
| } | ||||
|  | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										110
									
								
								src/framework/OpenAPIRequests.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/framework/OpenAPIRequests.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
|  | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class OpenAPIRequestGet { | ||||
| 	  public: | ||||
| 		explicit OpenAPIRequestGet( const std::string & Type, | ||||
| 								   const std::string & EndPoint, | ||||
| 								   const Types::StringPairVec & QueryData, | ||||
| 								   uint64_t msTimeout, | ||||
|                                    const std::string &LoggingStr=""): | ||||
| 														 Type_(Type), | ||||
| 														 EndPoint_(EndPoint), | ||||
| 														 QueryData_(QueryData), | ||||
| 														 msTimeout_(msTimeout), | ||||
|                                                          LoggingStr_(LoggingStr){}; | ||||
| 		Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); | ||||
| 	  private: | ||||
| 		std::string 			Type_; | ||||
| 		std::string 			EndPoint_; | ||||
| 		Types::StringPairVec 	QueryData_; | ||||
| 		uint64_t 				msTimeout_; | ||||
|         std::string             LoggingStr_; | ||||
| 	}; | ||||
|  | ||||
| 	class OpenAPIRequestPut { | ||||
| 	  public: | ||||
| 		explicit OpenAPIRequestPut( const std::string & Type, | ||||
| 								   const std::string & EndPoint, | ||||
| 								   const Types::StringPairVec & QueryData, | ||||
| 								   const Poco::JSON::Object & Body, | ||||
| 								   uint64_t msTimeout, | ||||
|                                    const std::string &LoggingStr=""): | ||||
| 														 Type_(Type), | ||||
| 														 EndPoint_(EndPoint), | ||||
| 														 QueryData_(QueryData), | ||||
| 														 msTimeout_(msTimeout), | ||||
| 														 Body_(Body), | ||||
|                                                          LoggingStr_(LoggingStr){}; | ||||
|  | ||||
| 		Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); | ||||
|  | ||||
| 	  private: | ||||
| 		std::string 			Type_; | ||||
| 		std::string 			EndPoint_; | ||||
| 		Types::StringPairVec 	QueryData_; | ||||
| 		uint64_t 				msTimeout_; | ||||
| 		Poco::JSON::Object      Body_; | ||||
|         std::string             LoggingStr_; | ||||
| 	}; | ||||
|  | ||||
| 	class OpenAPIRequestPost { | ||||
| 	  public: | ||||
| 		explicit OpenAPIRequestPost( const std::string & Type, | ||||
| 									const std::string & EndPoint, | ||||
| 									const Types::StringPairVec & QueryData, | ||||
| 									const Poco::JSON::Object & Body, | ||||
| 									uint64_t msTimeout, | ||||
|                                    const std::string &LoggingStr=""): | ||||
| 														  Type_(Type), | ||||
| 														  EndPoint_(EndPoint), | ||||
| 														  QueryData_(QueryData), | ||||
| 														  msTimeout_(msTimeout), | ||||
| 														  Body_(Body), | ||||
|                                                          LoggingStr_(LoggingStr){}; | ||||
| 		Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = ""); | ||||
| 	  private: | ||||
| 		std::string 			Type_; | ||||
| 		std::string 			EndPoint_; | ||||
| 		Types::StringPairVec 	QueryData_; | ||||
| 		uint64_t 				msTimeout_; | ||||
| 		Poco::JSON::Object      Body_; | ||||
|         std::string             LoggingStr_; | ||||
| 	}; | ||||
|  | ||||
| 	class OpenAPIRequestDelete { | ||||
| 	  public: | ||||
| 		explicit OpenAPIRequestDelete(  const std::string & Type, | ||||
| 									    const std::string & EndPoint, | ||||
| 									    const Types::StringPairVec & QueryData, | ||||
| 									    uint64_t msTimeout, | ||||
|                                         const std::string &LoggingStr=""): | ||||
|                                             Type_(Type), | ||||
|                                             EndPoint_(EndPoint), | ||||
|                                             QueryData_(QueryData), | ||||
|                                             msTimeout_(msTimeout), | ||||
|                                             LoggingStr_(LoggingStr){}; | ||||
| 		Poco::Net::HTTPServerResponse::HTTPStatus Do(const std::string & BearerToken = ""); | ||||
|  | ||||
| 	  private: | ||||
| 		std::string 			Type_; | ||||
| 		std::string 			EndPoint_; | ||||
| 		Types::StringPairVec 	QueryData_; | ||||
| 		uint64_t 				msTimeout_; | ||||
| 		Poco::JSON::Object      Body_; | ||||
|         std::string             LoggingStr_; | ||||
| 	}; | ||||
|  | ||||
| } // namespace OpenWifi | ||||
| @@ -28,6 +28,19 @@ namespace OpenWifi::Types { | ||||
|     typedef std::string                                         UUID_t; | ||||
|     typedef std::vector<UUID_t>                                 UUIDvec_t; | ||||
|     typedef std::map<std::string,std::map<uint32_t,uint64_t>>   Counted3DMapSII; | ||||
|  | ||||
| 	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<std::string, MicroServiceMeta>				MicroServiceMetaMap; | ||||
| 	typedef std::vector<MicroServiceMeta>						MicroServiceMetaVec; | ||||
| } | ||||
|  | ||||
| namespace OpenWifi { | ||||
|   | ||||
							
								
								
									
										27
									
								
								src/framework/RESTAPI_ExtServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/framework/RESTAPI_ExtServer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "framework/RESTAPI_ExtServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler *ExtRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) { | ||||
| 		try { | ||||
| 			Poco::URI uri(Request.getURI()); | ||||
| 			auto TID = NextTransactionId_++; | ||||
| 			Utils::SetThreadName(fmt::format("x-rest:{}",TID).c_str()); | ||||
| 			return RESTAPI_ExtServer()->CallServer(uri.getPath(), TID); | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler *RESTAPI_ExtServer::CallServer(const std::string &Path, uint64_t Id) { | ||||
| 		RESTAPIHandler::BindingMap Bindings; | ||||
| 		Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str()); | ||||
| 		return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id); | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										99
									
								
								src/framework/RESTAPI_ExtServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/framework/RESTAPI_ExtServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Net/HTTPServer.h" | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, | ||||
| 													 Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id); | ||||
|  | ||||
| 	class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	  public: | ||||
| 		ExtRequestHandlerFactory() = default; | ||||
| 		Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override; | ||||
| 	  private: | ||||
| 		static inline std::atomic_uint64_t  NextTransactionId_ = 1; | ||||
| 	}; | ||||
|  | ||||
| 	class RESTAPI_ExtServer : public SubSystemServer { | ||||
| 	  public: | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new RESTAPI_ExtServer; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
| 		inline int Start() override { | ||||
| 			poco_information(Logger(),"Starting."); | ||||
| 			Server_.InitLogging(); | ||||
|  | ||||
| 			for(const auto & Svr: ConfigServersList_) { | ||||
|  | ||||
| 				if(MicroServiceNoAPISecurity()) { | ||||
| 					poco_information(Logger(),fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); | ||||
| 				} else { | ||||
| 					poco_information(Logger(),fmt::format("Starting: {}:{} Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), | ||||
| 													 Svr.KeyFile(),Svr.CertFile())); | ||||
| 					Svr.LogCert(Logger()); | ||||
| 					if (!Svr.RootCA().empty()) | ||||
| 						Svr.LogCas(Logger()); | ||||
| 				} | ||||
|  | ||||
| 				Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; | ||||
| 				Params->setKeepAlive(true); | ||||
| 				Params->setName("ws:xrest"); | ||||
|  | ||||
| 				std::unique_ptr<Poco::Net::HTTPServer>  NewServer; | ||||
| 				if(MicroServiceNoAPISecurity()) { | ||||
| 					auto Sock{Svr.CreateSocket(Logger())}; | ||||
| 					NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory, Pool_, Sock, Params); | ||||
| 				} else { | ||||
| 					auto Sock{Svr.CreateSecureSocket(Logger())}; | ||||
| 					NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory, Pool_, Sock, Params); | ||||
| 				}; | ||||
| 				NewServer->start(); | ||||
| 				RESTServers_.push_back(std::move(NewServer)); | ||||
| 			} | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		inline void Stop() override { | ||||
| 			poco_information(Logger(),"Stopping..."); | ||||
| 			for( const auto & svr : RESTServers_ ) | ||||
| 				svr->stopAll(true); | ||||
| 			Pool_.stopAll(); | ||||
| 			Pool_.joinAll(); | ||||
| 			RESTServers_.clear(); | ||||
| 			poco_information(Logger(),"Stopped..."); | ||||
| 		} | ||||
|  | ||||
| 		inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { | ||||
| 			MicroServiceLoadConfigurationFile(); | ||||
| 			poco_information(Logger(),"Reinitializing."); | ||||
| 			Stop(); | ||||
| 			Start(); | ||||
| 		} | ||||
|  | ||||
| 		Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id); | ||||
| 		const Poco::ThreadPool & Pool() { return Pool_; } | ||||
|  | ||||
| 	  private: | ||||
| 		std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||
| 		Poco::ThreadPool	    Pool_{"x-rest",8,128}; | ||||
| 		RESTAPI_GenericServerAccounting   Server_; | ||||
|  | ||||
| 		RESTAPI_ExtServer() noexcept: | ||||
| 									   SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi") | ||||
| 		{ | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	inline auto RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); }; | ||||
|  | ||||
| } | ||||
							
								
								
									
										75
									
								
								src/framework/RESTAPI_GenericServerAccounting.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/framework/RESTAPI_GenericServerAccounting.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <array> | ||||
|  | ||||
| #include "Poco/StringTokenizer.h" | ||||
| #include "Poco/String.h" | ||||
| #include "Poco/Net/HTTPRequest.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class RESTAPI_GenericServerAccounting { | ||||
| 	  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 = MicroServiceConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); | ||||
| 			SetFlags(true, Public); | ||||
| 			std::string Private = MicroServiceConfigGetString("apilogging.private.methods","PUT,POST,DELETE"); | ||||
| 			SetFlags(false, Private); | ||||
|  | ||||
| 			std::string PublicBadTokens = MicroServiceConfigGetString("apilogging.public.badtokens.methods",""); | ||||
| 			LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0); | ||||
| 			std::string PrivateBadTokens = MicroServiceConfigGetString("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}; | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/framework/RESTAPI_Handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/framework/RESTAPI_Handler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										826
									
								
								src/framework/RESTAPI_Handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										826
									
								
								src/framework/RESTAPI_Handler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,826 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <map> | ||||
|  | ||||
| #include "Poco/Net/HTTPRequestHandler.h" | ||||
| #include "Poco/Logger.h" | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/Net/HTTPResponse.h" | ||||
| #include "Poco/Net/HTTPServerResponse.h" | ||||
| #include "Poco/DeflatingStream.h" | ||||
| #include "Poco/TemporaryFile.h" | ||||
| #include "Poco/Net/OAuth20Credentials.h" | ||||
|  | ||||
| #include "framework/ow_constants.h" | ||||
| #include "framework/RESTAPI_GenericServerAccounting.h" | ||||
| #include "framework/RESTAPI_RateLimiter.h" | ||||
| #include "framework/utils.h" | ||||
| #include "framework/RESTAPI_utils.h" | ||||
| #include "framework/AuthClient.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| #if defined(TIP_SECURITY_SERVICE) | ||||
| #include "AuthService.h" | ||||
| #endif | ||||
|  | ||||
| using namespace std::chrono_literals; | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	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; | ||||
| 			std::vector<std::string>    Select; | ||||
| 			bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false; | ||||
| 		}; | ||||
| 		typedef std::map<std::string, std::string> BindingMap; | ||||
|  | ||||
| 		struct RateLimit { | ||||
| 			int64_t     Interval=1000; | ||||
| 			int64_t     MaxCalls=10; | ||||
| 		}; | ||||
|  | ||||
| 		RESTAPIHandler( BindingMap map, | ||||
| 					   Poco::Logger &l, | ||||
| 					   std::vector<std::string> Methods, | ||||
| 					   RESTAPI_GenericServerAccounting & Server, | ||||
| 					   uint64_t TransactionId, | ||||
| 					   bool Internal, | ||||
| 					   bool AlwaysAuthorize=true, | ||||
| 					   bool RateLimited=false, | ||||
| 					   const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100}, | ||||
| 					   bool SubscriberOnly=false) | ||||
| 			:   Bindings_(std::move(map)), | ||||
| 			  Logger_(l), | ||||
| 			  Methods_(std::move(Methods)), | ||||
| 			  Internal_(Internal), | ||||
| 			  RateLimited_(RateLimited), | ||||
| 			  SubOnlyService_(SubscriberOnly), | ||||
| 			  AlwaysAuthorize_(AlwaysAuthorize), | ||||
| 			  Server_(Server), | ||||
| 			  MyRates_(Profile), | ||||
| 			  TransactionId_(TransactionId) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		inline bool RoleIsAuthorized([[maybe_unused]] const std::string & Path, [[maybe_unused]] const std::string & Method, [[maybe_unused]] std::string & Reason) { | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		inline void handleRequest(Poco::Net::HTTPServerRequest &RequestIn, | ||||
| 								  Poco::Net::HTTPServerResponse &ResponseIn) final { | ||||
| 			try { | ||||
| 				Request = &RequestIn; | ||||
| 				Response = &ResponseIn; | ||||
|  | ||||
| 				//				std::string th_name = "restsvr_" + std::to_string(TransactionId_); | ||||
| 				//				Utils::SetThreadName(th_name.c_str()); | ||||
|  | ||||
| 				if(Request->getContentLength()>0) { | ||||
| 					if(Request->getContentType().find("application/json")!=std::string::npos) { | ||||
| 						ParsedBody_ = IncomingParser_.parse(Request->stream()).extract<Poco::JSON::Object::Ptr>(); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) { | ||||
| 					return UnAuthorized(RESTAPI::Errors::RATE_LIMIT_EXCEEDED); | ||||
| 				} | ||||
|  | ||||
| 				if (!ContinueProcessing()) | ||||
| 					return; | ||||
|  | ||||
| 				bool Expired=false, Contacted=false; | ||||
| 				if (AlwaysAuthorize_ && !IsAuthorized(Expired, Contacted, SubOnlyService_)) { | ||||
| 					if(Expired) | ||||
| 						return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN); | ||||
| 					if(Contacted) | ||||
| 						return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN); | ||||
| 					else | ||||
| 						return UnAuthorized(RESTAPI::Errors::SECURITY_SERVICE_UNREACHABLE); | ||||
| 				} | ||||
|  | ||||
| 				std::string Reason; | ||||
| 				if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { | ||||
| 					return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 				} | ||||
|  | ||||
| 				ParseParameters(); | ||||
| 				if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) | ||||
| 					return DoGet(); | ||||
| 				else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) | ||||
| 					return DoPost(); | ||||
| 				else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) | ||||
| 					return DoDelete(); | ||||
| 				else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) | ||||
| 					return DoPut(); | ||||
| 				return BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); | ||||
| 			} catch (const Poco::Exception &E) { | ||||
| 				Logger_.log(E); | ||||
| 				return BadRequest(RESTAPI::Errors::InternalError); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline bool NeedAdditionalInfo() const { return QB_.AdditionalInfo; } | ||||
| 		[[nodiscard]] inline const std::vector<std::string> & SelectedRecords() const { return QB_.Select; } | ||||
|  | ||||
| 		inline static bool ParseBindings(const std::string & Request, const std::list<std::string> & EndPoints, BindingMap &bindings) { | ||||
| 			bindings.clear(); | ||||
| 			auto PathItems = Poco::StringTokenizer(Request, "/"); | ||||
|  | ||||
| 			for(const auto &EndPoint:EndPoints) { | ||||
| 				auto ParamItems = Poco::StringTokenizer(EndPoint, "/"); | ||||
| 				if (PathItems.count() != ParamItems.count()) | ||||
| 					continue; | ||||
|  | ||||
| 				bool Matched = true; | ||||
| 				for (size_t i = 0; i < PathItems.count(); i++) { | ||||
| 					if (PathItems[i] != ParamItems[i]) { | ||||
| 						if (ParamItems[i][0] == '{') { | ||||
| 							auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); | ||||
| 							bindings[Poco::toLower(ParamName)] = PathItems[i]; | ||||
| 						} else { | ||||
| 							Matched = false; | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				if(Matched) | ||||
| 					return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline void PrintBindings() { | ||||
| 			for (const auto &[key, value] : Bindings_) | ||||
| 				std::cout << "Key = " << key << "  Value= " << value << std::endl; | ||||
| 		} | ||||
|  | ||||
| 		inline void ParseParameters() { | ||||
| 			Poco::URI uri(Request->getURI()); | ||||
| 			Parameters_ = uri.getQueryParameters(); | ||||
| 			InitQueryBlock(); | ||||
| 		} | ||||
|  | ||||
| 		inline static bool is_number(const std::string &s) { | ||||
| 			return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); | ||||
| 		} | ||||
|  | ||||
| 		inline static bool is_bool(const std::string &s) { | ||||
| 			if (s == "true" || s == "false") | ||||
| 				return true; | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline uint64_t GetParameter(const std::string &Name, const uint64_t Default) { | ||||
| 			auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[&](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); | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default=false) { | ||||
| 			auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](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"; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default="") { | ||||
| 			auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; }); | ||||
| 			if(Hint==end(Parameters_)) | ||||
| 				return Default; | ||||
| 			return Hint->second; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline bool HasParameter(const std::string &Name, std::string &Value) { | ||||
| 			auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; }); | ||||
| 			if(Hint==end(Parameters_)) | ||||
| 				return false; | ||||
| 			Value = Hint->second; | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline bool HasParameter(const std::string &Name, uint64_t & Value) { | ||||
| 			auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](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; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline const std::string & 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; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline 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; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Types::UUIDvec_t & Value) { | ||||
| 			if(O->has(Field) && O->isArray(Field)) { | ||||
| 				auto Arr = O->getArray(Field); | ||||
| 				for(const auto &i:*Arr) | ||||
| 					Value.emplace_back(i.toString()); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool 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; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool 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; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, bool &Value) { | ||||
| 			if(O->has(Field)) { | ||||
| 				Value = O->get(Field).toString()=="true"; | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, double &Value) { | ||||
| 			if(O->has(Field)) { | ||||
| 				Value = (double) O->get(Field); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) { | ||||
| 			if(O->has(Field)) { | ||||
| 				std::string Content = O->get(Field).toString(); | ||||
| 				auto DecodedBlob = Utils::base64decode(Content); | ||||
| 				Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size()); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
|  | ||||
| 		template <typename T> bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) { | ||||
| 			if(O->has(Field)) { | ||||
| 				assignee = value; | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline void SetCommonHeaders(bool CloseConnection=false) { | ||||
| 			Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); | ||||
| 			Response->setChunkedTransferEncoding(true); | ||||
| 			Response->setContentType("application/json"); | ||||
| 			auto Origin = Request->find("Origin"); | ||||
| 			if (Origin != Request->end()) { | ||||
| 				Response->set("Access-Control-Allow-Origin", Origin->second); | ||||
| 			} else { | ||||
| 				Response->set("Access-Control-Allow-Origin", "*"); | ||||
| 			} | ||||
| 			Response->set("Vary", "Origin, Accept-Encoding"); | ||||
| 			if(CloseConnection) { | ||||
| 				Response->set("Connection", "close"); | ||||
| 				Response->setKeepAlive(false); | ||||
| 			} else { | ||||
| 				Response->setKeepAlive(true); | ||||
| 				Response->set("Connection", "Keep-Alive"); | ||||
| 				Response->set("Keep-Alive", "timeout=30, max=1000"); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		inline void ProcessOptions() { | ||||
| 			Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); | ||||
| 			Response->setChunkedTransferEncoding(true); | ||||
| 			auto Origin = Request->find("Origin"); | ||||
| 			if (Origin != Request->end()) { | ||||
| 				Response->set("Access-Control-Allow-Origin", Origin->second); | ||||
| 			} else { | ||||
| 				Response->set("Access-Control-Allow-Origin", "*"); | ||||
| 			} | ||||
| 			Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); | ||||
| 			auto RequestHeaders = Request->find("Access-Control-Request-Headers"); | ||||
| 			if(RequestHeaders!=Request->end()) | ||||
| 				Response->set("Access-Control-Allow-Headers", RequestHeaders->second); | ||||
| 			Response->set("Vary", "Origin, Accept-Encoding"); | ||||
| 			Response->set("Access-Control-Allow-Credentials", "true"); | ||||
| 			Response->set("Access-Control-Max-Age", "86400"); | ||||
| 			Response->set("Connection", "Keep-Alive"); | ||||
| 			Response->set("Keep-Alive", "timeout=30, max=1000"); | ||||
|  | ||||
| 			Response->setContentLength(0); | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); | ||||
| 			Response->send(); | ||||
| 		} | ||||
|  | ||||
| 		inline void PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, | ||||
| 									bool CloseConnection = false) { | ||||
| 			Response->setStatus(Status); | ||||
| 			SetCommonHeaders(CloseConnection); | ||||
| 		} | ||||
|  | ||||
| 		inline void BadRequest(const OpenWifi::RESTAPI::Errors::msg &E, const std::string & Extra="") { | ||||
| 			PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); | ||||
| 			Poco::JSON::Object	ErrorObject; | ||||
| 			ErrorObject.set("ErrorCode",400); | ||||
| 			ErrorObject.set("ErrorDetails",Request->getMethod()); | ||||
| 			if(Extra.empty()) | ||||
| 				ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; | ||||
| 			else | ||||
| 				ErrorObject.set("ErrorDescription",fmt::format("{}: {} ({})",E.err_num,E.err_txt, Extra)) ; | ||||
|  | ||||
| 			std::ostream &Answer = Response->send(); | ||||
| 			Poco::JSON::Stringifier::stringify(ErrorObject, Answer); | ||||
| 		} | ||||
|  | ||||
| 		inline void InternalError(const OpenWifi::RESTAPI::Errors::msg &E) { | ||||
| 			PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); | ||||
| 			Poco::JSON::Object	ErrorObject; | ||||
| 			ErrorObject.set("ErrorCode",500); | ||||
| 			ErrorObject.set("ErrorDetails",Request->getMethod()); | ||||
| 			ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; | ||||
| 			std::ostream &Answer = Response->send(); | ||||
| 			Poco::JSON::Stringifier::stringify(ErrorObject, Answer); | ||||
| 		} | ||||
|  | ||||
| 		inline void UnAuthorized(const OpenWifi::RESTAPI::Errors::msg &E) { | ||||
| 			PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); | ||||
| 			Poco::JSON::Object	ErrorObject; | ||||
| 			ErrorObject.set("ErrorCode",E.err_num); | ||||
| 			ErrorObject.set("ErrorDetails",Request->getMethod()); | ||||
| 			ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; | ||||
| 			std::ostream &Answer = Response->send(); | ||||
| 			Poco::JSON::Stringifier::stringify(ErrorObject, Answer); | ||||
| 		} | ||||
|  | ||||
| 		inline void NotFound() { | ||||
| 			PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); | ||||
| 			Poco::JSON::Object	ErrorObject; | ||||
| 			ErrorObject.set("ErrorCode",404); | ||||
| 			ErrorObject.set("ErrorDetails",Request->getMethod()); | ||||
| 			const auto & E = OpenWifi::RESTAPI::Errors::Error404; | ||||
| 			ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ; | ||||
| 			std::ostream &Answer = Response->send(); | ||||
| 			Poco::JSON::Stringifier::stringify(ErrorObject, Answer); | ||||
| 			poco_debug(Logger_,fmt::format("RES-NOTFOUND: User='{}@{}' Method='{}' Path='{}", | ||||
|                                             Requester(), | ||||
| 											Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
| 											Request->getMethod(), | ||||
| 											Request->getURI())); | ||||
| 		} | ||||
|  | ||||
| 		inline void 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); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		inline void SendCompressedTarFile(const std::string & FileName, const std::string & Content) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			Response->set("Content-Type","application/gzip"); | ||||
| 			Response->set("Content-Disposition", "attachment; filename=" + FileName ); | ||||
| 			Response->set("Content-Transfer-Encoding","binary"); | ||||
| 			Response->set("Accept-Ranges", "bytes"); | ||||
| 			Response->set("Cache-Control", "no-store"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); | ||||
| 			Response->setContentLength(Content.size()); | ||||
| 			Response->setChunkedTransferEncoding(true); | ||||
| 			std::ostream& OutputStream = Response->send(); | ||||
| 			OutputStream << Content; | ||||
| 		} | ||||
|  | ||||
| 		inline void SendFile(Poco::File & File, const std::string & UUID) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			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", "no-store"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			Response->setContentLength(File.getSize()); | ||||
| 			Response->sendFile(File.path(),"application/octet-stream"); | ||||
| 		} | ||||
|  | ||||
| 		inline void SendFile(Poco::File & File) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			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", "no-store"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			Response->sendFile(File.path(),MT.ContentType); | ||||
| 		} | ||||
|  | ||||
| 		inline void SendFile(Poco::TemporaryFile &TempAvatar, [[maybe_unused]] const std::string &Type, const std::string & Name) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			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", "no-store"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			Response->setContentLength(TempAvatar.getSize()); | ||||
| 			Response->sendFile(TempAvatar.path(),MT.ContentType); | ||||
| 		} | ||||
|  | ||||
| 		inline void SendFileContent(const std::string &Content, const std::string &Type, const std::string & Name) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			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", "no-store"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			Response->setContentLength(Content.size()); | ||||
| 			Response->setContentType(Type ); | ||||
| 			auto & OutputStream = Response->send(); | ||||
| 			OutputStream << Content ; | ||||
| 		} | ||||
|  | ||||
| 		inline void SendHTMLFileBack(Poco::File & File, | ||||
| 									 const Types::StringPairVec & FormVars) { | ||||
| 			Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK); | ||||
| 			SetCommonHeaders(); | ||||
| 			Response->set("Pragma", "private"); | ||||
| 			Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 			std::string FormContent = Utils::LoadFile(File.path()); | ||||
| 			Utils::ReplaceVariables(FormContent, FormVars); | ||||
| 			Response->setContentLength(FormContent.size()); | ||||
| 			Response->setChunkedTransferEncoding(true); | ||||
| 			Response->setContentType("text/html"); | ||||
| 			std::ostream& ostr = Response->send(); | ||||
| 			ostr << FormContent; | ||||
| 		} | ||||
|  | ||||
| 		inline void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection=false) { | ||||
| 			PrepareResponse(Status, CloseConnection); | ||||
| 			if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) { | ||||
| 				Response->setContentLength(0); | ||||
| 				Response->erase("Content-Type"); | ||||
| 				Response->setChunkedTransferEncoding(false); | ||||
| 			} | ||||
| 			Response->send(); | ||||
| 		} | ||||
|  | ||||
| 		inline bool 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; | ||||
| 		} | ||||
|  | ||||
| 		inline bool IsAuthorized(bool & Expired, bool & Contacted, bool SubOnly = false ); | ||||
|  | ||||
| 		inline void ReturnObject(Poco::JSON::Object &Object) { | ||||
| 			PrepareResponse(); | ||||
| 			if(Request!= nullptr) { | ||||
| 				//   can we compress ??? | ||||
| 				auto AcceptedEncoding = Request->find("Accept-Encoding"); | ||||
| 				if(AcceptedEncoding!=Request->end()) { | ||||
| 					if( AcceptedEncoding->second.find("gzip")!=std::string::npos || | ||||
| 						AcceptedEncoding->second.find("compress")!=std::string::npos) { | ||||
| 						Response->set("Content-Encoding", "gzip"); | ||||
| 						std::ostream &Answer = Response->send(); | ||||
| 						Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP); | ||||
| 						Poco::JSON::Stringifier::stringify(Object, deflater); | ||||
| 						deflater.close(); | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			std::ostream &Answer = Response->send(); | ||||
| 			Poco::JSON::Stringifier::stringify(Object, Answer); | ||||
| 		} | ||||
|  | ||||
| 		inline void ReturnRawJSON(const std::string &json_doc) { | ||||
| 			PrepareResponse(); | ||||
| 			if(Request!= nullptr) { | ||||
| 				//   can we compress ??? | ||||
| 				auto AcceptedEncoding = Request->find("Accept-Encoding"); | ||||
| 				if(AcceptedEncoding!=Request->end()) { | ||||
| 					if( AcceptedEncoding->second.find("gzip")!=std::string::npos || | ||||
| 						AcceptedEncoding->second.find("compress")!=std::string::npos) { | ||||
| 						Response->set("Content-Encoding", "gzip"); | ||||
| 						std::ostream &Answer = Response->send(); | ||||
| 						Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP); | ||||
|                         deflater << json_doc; | ||||
| 						deflater.close(); | ||||
| 						return; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			std::ostream &Answer = Response->send(); | ||||
|             Answer << json_doc; | ||||
| 		} | ||||
|  | ||||
| 		inline void ReturnCountOnly(uint64_t Count) { | ||||
| 			Poco::JSON::Object  Answer; | ||||
| 			Answer.set("count", Count); | ||||
| 			ReturnObject(Answer); | ||||
| 		} | ||||
|  | ||||
| 		inline bool 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, 0); | ||||
| 			QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); | ||||
| 			QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); | ||||
| 			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); | ||||
| 			QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false); | ||||
|  | ||||
| 			auto RawSelect = GetParameter(RESTAPI::Protocol::SELECT, ""); | ||||
|  | ||||
| 			auto Entries = Poco::StringTokenizer(RawSelect,","); | ||||
| 			for(const auto &i:Entries) { | ||||
| 				QB_.Select.emplace_back(i); | ||||
| 			} | ||||
| 			if(QB_.Offset<1) | ||||
| 				QB_.Offset=0; | ||||
| 			return true; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0){ | ||||
| 			if(Obj->has(Parameter)) | ||||
| 				return Obj->get(Parameter); | ||||
| 			return Default; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline std::string 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]] inline bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false){ | ||||
| 			if(Obj->has(Parameter)) | ||||
| 				return Obj->get(Parameter).toString()=="true"; | ||||
| 			return Default; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj) { | ||||
| 			return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj); | ||||
| 		} | ||||
|  | ||||
| 		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); | ||||
| 		} | ||||
|  | ||||
| 		template<typename T> void Object(const char *Name, const std::vector<T> & Objects) { | ||||
| 			Poco::JSON::Object  Answer; | ||||
| 			RESTAPI_utils::field_to_json(Answer,Name,Objects); | ||||
| 			ReturnObject(Answer); | ||||
| 		} | ||||
|  | ||||
| 		template <typename T> void Object(const T &O) { | ||||
| 			Poco::JSON::Object  Answer; | ||||
| 			O.to_json(Answer); | ||||
| 			ReturnObject(Answer); | ||||
| 		} | ||||
|  | ||||
| 		Poco::Logger & Logger() { return Logger_; } | ||||
|  | ||||
| 		virtual void DoGet() = 0 ; | ||||
| 		virtual void DoDelete() = 0 ; | ||||
| 		virtual void DoPost() = 0 ; | ||||
| 		virtual void DoPut() = 0 ; | ||||
|  | ||||
| 		Poco::Net::HTTPServerRequest        *Request= nullptr; | ||||
| 		Poco::Net::HTTPServerResponse       *Response= nullptr; | ||||
| 		SecurityObjects::UserInfoAndPolicy 	UserInfo_; | ||||
| 		QueryBlock					QB_; | ||||
| 		const std::string & Requester() const { return REST_Requester_; } | ||||
| 	  protected: | ||||
| 		BindingMap 					Bindings_; | ||||
| 		Poco::URI::QueryParameters 	Parameters_; | ||||
| 		Poco::Logger 				&Logger_; | ||||
| 		std::string 				SessionToken_; | ||||
| 		std::vector<std::string> 	Methods_; | ||||
| 		bool                        Internal_=false; | ||||
| 		bool                        RateLimited_=false; | ||||
| 		bool                        QueryBlockInitialized_=false; | ||||
| 		bool                        SubOnlyService_=false; | ||||
| 		bool                        AlwaysAuthorize_=true; | ||||
| 		Poco::JSON::Parser          IncomingParser_; | ||||
| 		RESTAPI_GenericServerAccounting       & Server_; | ||||
| 		RateLimit                   MyRates_; | ||||
| 		uint64_t                    TransactionId_; | ||||
| 		Poco::JSON::Object::Ptr     ParsedBody_; | ||||
| 		std::string					REST_Requester_; | ||||
| 	}; | ||||
|  | ||||
| #ifdef    TIP_SECURITY_SERVICE | ||||
| 	[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, | ||||
|                                                SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub ); | ||||
| #endif | ||||
| 	inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) { | ||||
| 		if(Internal_ && Request->has("X-INTERNAL-NAME")) { | ||||
| 			auto Allowed = MicroServiceIsValidAPIKEY(*Request); | ||||
| 			Contacted = true; | ||||
| 			if(!Allowed) { | ||||
| 				if(Server_.LogBadTokens(false)) { | ||||
| 					poco_debug(Logger_,fmt::format("I-REQ-DENIED({}): TID={} Method={} Path={}", | ||||
| 													Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
| 													TransactionId_, | ||||
| 													Request->getMethod(), Request->getURI())); | ||||
| 				} | ||||
| 			} else { | ||||
| 				auto Id = Request->get("X-INTERNAL-NAME", "unknown"); | ||||
| 				REST_Requester_ = Id; | ||||
| 				if(Server_.LogIt(Request->getMethod(),true)) { | ||||
| 					poco_debug(Logger_,fmt::format("I-REQ-ALLOWED({}): TID={} User='{}' Method={} Path={}", | ||||
| 													Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
| 													TransactionId_, | ||||
| 													Id, | ||||
| 													Request->getMethod(), Request->getURI())); | ||||
| 				} | ||||
| 			} | ||||
| 			return Allowed; | ||||
| 		} else if(!Internal_ && Request->has("X-API-KEY")) { | ||||
|             SessionToken_ = Request->get("X-API-KEY", ""); | ||||
| #ifdef    TIP_SECURITY_SERVICE | ||||
|             std::uint64_t expiresOn; | ||||
|             if (AuthService()->IsValidApiKey(SessionToken_, UserInfo_.webtoken, UserInfo_.userinfo, Expired, expiresOn)) { | ||||
| #else | ||||
|             if (AuthClient()->IsValidApiKey( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted)) { | ||||
| #endif | ||||
|                 REST_Requester_ = UserInfo_.userinfo.email; | ||||
|                 if(Server_.LogIt(Request->getMethod(),true)) { | ||||
|                     poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): APIKEY-ACCESS TID={} User='{}@{}' Method={} Path={}", | ||||
|                                                    UserInfo_.userinfo.email, | ||||
|                                                    TransactionId_, | ||||
|                                                    Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
|                                                    Request->clientAddress().toString(), | ||||
|                                                    Request->getMethod(), | ||||
|                                                    Request->getURI())); | ||||
|                 } | ||||
|                 return true; | ||||
|             } else { | ||||
|                 if(Server_.LogBadTokens(true)) { | ||||
|                     poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}", | ||||
|                                                    Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
|                                                    TransactionId_, | ||||
|                                                    Request->getMethod(), | ||||
|                                                    Request->getURI())); | ||||
|                 } | ||||
|             } | ||||
|             return false; | ||||
|         } 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 (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, TransactionId_, Expired, Sub)) { | ||||
| #else | ||||
| 			if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted, Sub)) { | ||||
| #endif | ||||
| 				REST_Requester_ = UserInfo_.userinfo.email; | ||||
| 				if(Server_.LogIt(Request->getMethod(),true)) { | ||||
| 					poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): TID={} User='{}@{}' Method={} Path={}", | ||||
| 													UserInfo_.userinfo.email, | ||||
| 													TransactionId_, | ||||
| 													Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
| 													Request->clientAddress().toString(), | ||||
| 													Request->getMethod(), | ||||
| 													Request->getURI())); | ||||
| 				} | ||||
| 				return true; | ||||
| 			} else { | ||||
| 				if(Server_.LogBadTokens(true)) { | ||||
| 					poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}", | ||||
| 													Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
| 													TransactionId_, | ||||
| 													Request->getMethod(), | ||||
| 													Request->getURI())); | ||||
| 				} | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal) | ||||
| 			: RESTAPIHandler(bindings, L, std::vector<std::string>{}, Server, TransactionId, Internal) {} | ||||
| 		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_GenericServerAccounting & Server, uint64_t TransactionId) { | ||||
| 			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, TransactionId, false); | ||||
| 			} | ||||
|  | ||||
| 			if constexpr (sizeof...(Args) == 0) { | ||||
| 				return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, false); | ||||
| 			} else { | ||||
| 				return RESTAPI_Router<Args...>(RequestedPath, Bindings, Logger, Server, TransactionId); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	template<typename T, typename... Args> | ||||
| 		RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, | ||||
| 										 Poco::Logger & Logger, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId) { | ||||
| 			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, TransactionId, true ); | ||||
| 			} | ||||
|  | ||||
| 			if constexpr (sizeof...(Args) == 0) { | ||||
| 				return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, true); | ||||
| 			} else { | ||||
| 				return RESTAPI_Router_I<Args...>(RequestedPath, Bindings, Logger, Server, TransactionId); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										17
									
								
								src/framework/RESTAPI_IntServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/framework/RESTAPI_IntServer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "RESTAPI_IntServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler * | ||||
| 	IntRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) { | ||||
| 		auto TID = NextTransactionId_++; | ||||
| 		Utils::SetThreadName(fmt::format("i-rest:{}", TID).c_str()); | ||||
| 		Poco::URI uri(Request.getURI()); | ||||
| 		return RESTAPI_IntServer()->CallServer(uri.getPath(), TID); | ||||
| 	} | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										104
									
								
								src/framework/RESTAPI_IntServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/framework/RESTAPI_IntServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Net/HTTPServer.h" | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings, | ||||
| 													 Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id); | ||||
|  | ||||
| 	class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	  public: | ||||
| 		inline IntRequestHandlerFactory() = default; | ||||
| 		Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override; | ||||
| 	  private: | ||||
| 		static inline std::atomic_uint64_t  NextTransactionId_ = 1; | ||||
| 	}; | ||||
|  | ||||
| 	class RESTAPI_IntServer : public SubSystemServer { | ||||
| 	  public: | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new RESTAPI_IntServer; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
| 		inline int Start() override { | ||||
| 			poco_information(Logger(),"Starting."); | ||||
| 			Server_.InitLogging(); | ||||
|  | ||||
| 			for(const auto & Svr: ConfigServersList_) { | ||||
|  | ||||
| 				if(MicroServiceNoAPISecurity()) { | ||||
| 					poco_information(Logger(),fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port())); | ||||
| 				} else { | ||||
| 					poco_information(Logger(),fmt::format("Starting: {}:{}. Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(), | ||||
| 													 Svr.KeyFile(),Svr.CertFile())); | ||||
| 					Svr.LogCert(Logger()); | ||||
| 					if (!Svr.RootCA().empty()) | ||||
| 						Svr.LogCas(Logger()); | ||||
| 				} | ||||
|  | ||||
| 				auto Params = new Poco::Net::HTTPServerParams; | ||||
| 				Params->setKeepAlive(true); | ||||
| 				Params->setName("ws:irest"); | ||||
|  | ||||
| 				std::unique_ptr<Poco::Net::HTTPServer>  NewServer; | ||||
| 				if(MicroServiceNoAPISecurity()) { | ||||
| 					auto Sock{Svr.CreateSocket(Logger())}; | ||||
| 					NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory, Pool_, Sock, Params); | ||||
| 				} else { | ||||
| 					auto Sock{Svr.CreateSecureSocket(Logger())}; | ||||
| 					NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory, Pool_, Sock, Params); | ||||
| 				}; | ||||
| 				NewServer->start(); | ||||
| 				RESTServers_.push_back(std::move(NewServer)); | ||||
| 			} | ||||
|  | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		inline void Stop() override { | ||||
| 			poco_information(Logger(),"Stopping..."); | ||||
| 			for( const auto & svr : RESTServers_ ) | ||||
| 				svr->stopAll(true); | ||||
| 			Pool_.stopAll(); | ||||
| 			Pool_.joinAll(); | ||||
| 			poco_information(Logger(),"Stopped..."); | ||||
| 		} | ||||
|  | ||||
| 		inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { | ||||
| 			MicroServiceLoadConfigurationFile(); | ||||
| 			poco_information(Logger(),"Reinitializing."); | ||||
| 			Stop(); | ||||
| 			Start(); | ||||
| 		} | ||||
|  | ||||
| 		inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) { | ||||
| 			RESTAPIHandler::BindingMap Bindings; | ||||
| 			Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str()); | ||||
| 			return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id); | ||||
| 		} | ||||
| 		const Poco::ThreadPool & Pool() { return Pool_; } | ||||
| 	  private: | ||||
| 		std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||
| 		Poco::ThreadPool	    Pool_{"i-rest",4,64}; | ||||
| 		RESTAPI_GenericServerAccounting   Server_; | ||||
|  | ||||
| 		RESTAPI_IntServer() noexcept: | ||||
| 									   SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi") | ||||
| 		{ | ||||
| 		} | ||||
| 	}; | ||||
|  | ||||
| 	inline auto RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); }; | ||||
|  | ||||
|  | ||||
| } // namespace OpenWifi | ||||
|  | ||||
							
								
								
									
										66
									
								
								src/framework/RESTAPI_PartHandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/framework/RESTAPI_PartHandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include "Poco/Net/PartHandler.h" | ||||
| #include "Poco/Net/MessageHeader.h" | ||||
| #include "Poco/CountingStream.h" | ||||
| #include "Poco/NullStream.h" | ||||
| #include "Poco/StreamCopier.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class RESTAPI_PartHandler: public Poco::Net::PartHandler { | ||||
| 	  public: | ||||
| 		RESTAPI_PartHandler(): | ||||
| 								_length(0) | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 		inline 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]] inline int length() const | ||||
| 		{ | ||||
| 			return _length; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline const std::string& name() const | ||||
| 		{ | ||||
| 			return _name; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline const std::string& fileName() const | ||||
| 		{ | ||||
| 			return _fileName; | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline const std::string& contentType() const | ||||
| 		{ | ||||
| 			return _type; | ||||
| 		} | ||||
|  | ||||
| 	  private: | ||||
| 		int _length; | ||||
| 		std::string _type; | ||||
| 		std::string _name; | ||||
| 		std::string _fileName; | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										75
									
								
								src/framework/RESTAPI_RateLimiter.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/framework/RESTAPI_RateLimiter.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| #include "Poco/URI.h" | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
| #include "Poco/ExpireLRUCache.h" | ||||
|  | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class RESTAPI_RateLimiter : public SubSystemServer { | ||||
| 	  public: | ||||
|  | ||||
| 		struct ClientCacheEntry { | ||||
| 			int64_t  Start=0; | ||||
| 			int      Count=0; | ||||
| 		}; | ||||
|  | ||||
| 		static auto instance() { | ||||
| 			static auto instance_ = new RESTAPI_RateLimiter; | ||||
| 			return instance_; | ||||
| 		} | ||||
|  | ||||
| 		inline int Start() final { return 0;}; | ||||
| 		inline void Stop() final { }; | ||||
|  | ||||
| 		inline bool IsRateLimited(const Poco::Net::HTTPServerRequest &R, int64_t Period, int64_t MaxCalls) { | ||||
| 			Poco::URI   uri(R.getURI()); | ||||
| 			auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); | ||||
| 			auto E = Cache_.get(H); | ||||
| 			auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | ||||
| 			if(E.isNull()) { | ||||
| 				Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1}); | ||||
| 				return false; | ||||
| 			} | ||||
| 			if((Now-E->Start)<Period) { | ||||
| 				E->Count++; | ||||
| 				Cache_.update(H,E); | ||||
| 				if(E->Count > MaxCalls) { | ||||
| 					poco_warning(Logger(),fmt::format("RATE-LIMIT-EXCEEDED: from '{}'", R.clientAddress().toString())); | ||||
| 					return true; | ||||
| 				} | ||||
| 				return false; | ||||
| 			} | ||||
| 			E->Start = Now; | ||||
| 			E->Count = 1; | ||||
| 			Cache_.update(H,E); | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline void Clear() { | ||||
| 			Cache_.clear(); | ||||
| 		} | ||||
|  | ||||
| 	  private: | ||||
| 		Poco::ExpireLRUCache<uint64_t,ClientCacheEntry>      Cache_{2048}; | ||||
| 		std::hash<std::string>          str_hash; | ||||
|  | ||||
| 		RESTAPI_RateLimiter() noexcept: | ||||
| 										 SubSystemServer("RateLimiter", "RATE-LIMITER", "rate.limiter") | ||||
| 		{ | ||||
| 		} | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	inline auto RESTAPI_RateLimiter() { return RESTAPI_RateLimiter::instance(); } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										157
									
								
								src/framework/RESTAPI_SystemCommand.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/framework/RESTAPI_SystemCommand.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
|  | ||||
| #include "Poco/Environment.h" | ||||
|  | ||||
| using namespace std::chrono_literals; | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class RESTAPI_system_command : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, 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, | ||||
| 							 TransactionId, | ||||
| 							 Internal) {} | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/system"};} | ||||
|  | ||||
| 		inline void DoGet() { | ||||
| 			std::string Arg; | ||||
| 			if(HasParameter("command",Arg) && Arg=="info") { | ||||
| 				Poco::JSON::Object Answer; | ||||
| 				Answer.set(RESTAPI::Protocol::VERSION, MicroServiceVersion()); | ||||
| 				Answer.set(RESTAPI::Protocol::UPTIME, MicroServiceUptimeTotalSeconds()); | ||||
| 				Answer.set(RESTAPI::Protocol::START, MicroServiceStartTimeEpochTime()); | ||||
| 				Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName()); | ||||
| 				Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount()); | ||||
| 				Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName()); | ||||
| 				Answer.set(RESTAPI::Protocol::UI, MicroServiceGetUIURI()); | ||||
|  | ||||
| 				Poco::JSON::Array   Certificates; | ||||
| 				auto SubSystems = MicroServiceGetFullSubSystems(); | ||||
| 				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()) { | ||||
| 							Poco::File  F1(CertFileName); | ||||
| 							if(F1.exists()) { | ||||
| 								auto InsertResult = CertNames.insert(CertFileName); | ||||
| 								if(InsertResult.second) { | ||||
| 									Poco::JSON::Object Inner; | ||||
| 									Poco::Path F(CertFileName); | ||||
| 									Inner.set("filename", F.getFileName()); | ||||
| 									Poco::Crypto::X509Certificate C(CertFileName); | ||||
| 									auto ExpiresOn = C.expiresOn(); | ||||
| 									Inner.set("expiresOn", ExpiresOn.timestamp().epochTime()); | ||||
| 									Certificates.add(Inner); | ||||
| 								} | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				Answer.set("certificates", Certificates); | ||||
| 				return ReturnObject(Answer); | ||||
| 			} | ||||
| 			if(GetBoolParameter("extraConfiguration")) { | ||||
| 				Poco::JSON::Object  Answer; | ||||
| 				MicroServiceGetExtraConfiguration(Answer); | ||||
| 				return ReturnObject(Answer); | ||||
| 			} | ||||
| 			BadRequest(RESTAPI::Errors::InvalidCommand); | ||||
| 		} | ||||
|  | ||||
| 		inline void DoPost() final { | ||||
| 			const auto & Obj = ParsedBody_; | ||||
| 			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); | ||||
| 								MicroServiceSetSubsystemLogLevel(Name, Value); | ||||
| 								poco_information(Logger_, | ||||
| 									fmt::format("Setting log level for {} at {}", Name, Value)); | ||||
| 							} | ||||
| 						} | ||||
| 						return OK(); | ||||
| 					} | ||||
| 				} else if (Command == RESTAPI::Protocol::GETLOGLEVELS) { | ||||
| 					auto CurrentLogLevels = MicroServiceGetLogLevels(); | ||||
| 					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); | ||||
| 					return ReturnObject(Result); | ||||
| 				} else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) { | ||||
| 					Poco::JSON::Object Result; | ||||
| 					Poco::JSON::Array LevelNamesArray; | ||||
| 					const Types::StringVec &LevelNames =  MicroServiceGetLogLevelNames(); | ||||
| 					for (const auto &i : LevelNames) | ||||
| 						LevelNamesArray.add(i); | ||||
| 					Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); | ||||
| 					return ReturnObject(Result); | ||||
| 				} else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) { | ||||
| 					Poco::JSON::Object Result; | ||||
| 					Poco::JSON::Array LevelNamesArray; | ||||
| 					const Types::StringVec &SubSystemNames =  MicroServiceGetSubSystems(); | ||||
| 					for (const auto &i : SubSystemNames) | ||||
| 						LevelNamesArray.add(i); | ||||
| 					Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); | ||||
| 					return ReturnObject(Result); | ||||
| 				} 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") | ||||
| 									MicroServiceReload(); | ||||
| 								else | ||||
| 									MicroServiceReload(i); | ||||
| 							} | ||||
| 						}); | ||||
| 						ReloadThread.detach(); | ||||
| 					} | ||||
| 					return OK(); | ||||
| 				} | ||||
| 			} else { | ||||
| 				return BadRequest(RESTAPI::Errors::InvalidCommand); | ||||
| 			} | ||||
| 			BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||
| 		} | ||||
|  | ||||
| 		void DoPut() final {}; | ||||
| 		void DoDelete() final {}; | ||||
| 	}; | ||||
|  | ||||
| } | ||||
							
								
								
									
										52
									
								
								src/framework/RESTAPI_SystemConfiguration.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/framework/RESTAPI_SystemConfiguration.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-31. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| using namespace std::chrono_literals; | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class RESTAPI_system_configuration : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		RESTAPI_system_configuration(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, | ||||
| 									 RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, | ||||
| 									 bool Internal) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_PUT, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_DELETE, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 							 Server, TransactionId, Internal) {} | ||||
|  | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/systemConfiguration"}; } | ||||
|  | ||||
| 		inline void DoPost() final {} | ||||
|  | ||||
| 		inline void DoGet() final { | ||||
|  | ||||
| 			return OK(); | ||||
| 		} | ||||
|  | ||||
| 		inline void DoPut() final{ | ||||
| 			if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { | ||||
| 				return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 			} | ||||
|  | ||||
| 			return OK(); | ||||
| 		}; | ||||
|  | ||||
| 		inline void DoDelete() final{ | ||||
| 			if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { | ||||
| 				return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED); | ||||
| 			} | ||||
| 			MicroServiceDeleteOverrideConfiguration(); | ||||
| 			return OK(); | ||||
| 		}; | ||||
| 	}; | ||||
|  | ||||
| } | ||||
							
								
								
									
										45
									
								
								src/framework/RESTAPI_WebSocketServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/framework/RESTAPI_WebSocketServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-26. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "framework/RESTAPI_Handler.h" | ||||
| #include "Poco/Net/WebSocket.h" | ||||
|  | ||||
| #include "framework/UI_WebSocketClientServer.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	class RESTAPI_webSocketServer : public RESTAPIHandler { | ||||
| 	  public: | ||||
| 		inline RESTAPI_webSocketServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal) | ||||
| 			: RESTAPIHandler(bindings, L, | ||||
| 							 std::vector<std::string>{	Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 													  	Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 							 Server, TransactionId, Internal,false) {} | ||||
| 		static auto PathName() { return std::list<std::string>{"/api/v1/ws"};} | ||||
| 		void DoGet() final { | ||||
| 				try | ||||
| 				{ | ||||
| 					if(Request->find("Upgrade") != Request->end() && Poco::icompare((*Request)["Upgrade"], "websocket") == 0) { | ||||
| 						try | ||||
| 						{ | ||||
| 							Poco::Net::WebSocket WS(*Request, *Response); | ||||
| 							auto Id = MicroServiceCreateUUID(); | ||||
| 							UI_WebSocketClientServer()->NewClient(WS,Id,UserInfo_.userinfo.email, TransactionId_); | ||||
| 						} | ||||
| 						catch (...) { | ||||
| 							std::cout << "Cannot create websocket client..." << std::endl; | ||||
| 						} | ||||
| 					} | ||||
| 				} catch(...) { | ||||
| 					std::cout << "Cannot upgrade connection..." << std::endl; | ||||
| 				} | ||||
| 			}; | ||||
| 		void DoDelete() final {}; | ||||
| 		void DoPost() final {}; | ||||
| 		void DoPut() final {}; | ||||
| 	  private: | ||||
| 	}; | ||||
| } | ||||
							
								
								
									
										484
									
								
								src/framework/RESTAPI_utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								src/framework/RESTAPI_utils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,484 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "Poco/Data/LOB.h" | ||||
| #include "Poco/Net/HTTPServerRequest.h" | ||||
|  | ||||
| #include "framework/OpenWifiTypes.h" | ||||
| #include "framework/utils.h" | ||||
|  | ||||
| namespace OpenWifi::RESTAPI_utils { | ||||
|  | ||||
| 	inline 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); | ||||
| 	} | ||||
|  | ||||
| 	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, double V) { | ||||
| 		Obj.set(Field,V); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, float 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, int16_t Value) { | ||||
| 		Obj.set(Field, Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int32_t Value) { | ||||
| 		Obj.set(Field, Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, int64_t Value) { | ||||
| 		Obj.set(Field, Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint16_t Value) { | ||||
| 		Obj.set(Field, Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint32_t Value) { | ||||
| 		Obj.set(Field, Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint64_t Value) { | ||||
| 		Obj.set(Field,Value); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Poco::Data::BLOB &Value) { | ||||
| 		auto Result = Utils::base64encode((const unsigned char *)Value.rawContent(),Value.size()); | ||||
| 		Obj.set(Field,Result); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringPairVec & S) { | ||||
| 		Poco::JSON::Array   Array; | ||||
| 		for(const auto &i:S) { | ||||
| 			Poco::JSON::Object  O; | ||||
| 			O.set("tag",i.first); | ||||
| 			O.set("value", i.second); | ||||
| 			Array.add(O); | ||||
| 		} | ||||
| 		Obj.set(Field,Array); | ||||
| 	} | ||||
|  | ||||
| 	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::TagList &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); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::Counted3DMapSII &M) { | ||||
| 		Poco::JSON::Array	A; | ||||
| 		for(const auto &[OrgName,MonthlyNumberMap]:M) { | ||||
| 			Poco::JSON::Object  OrgObject; | ||||
| 			OrgObject.set("tag",OrgName); | ||||
| 			Poco::JSON::Array   MonthlyArray; | ||||
| 			for(const auto &[Month,Counter]:MonthlyNumberMap) { | ||||
| 				Poco::JSON::Object  Inner; | ||||
| 				Inner.set("value", Month); | ||||
| 				Inner.set("counter", Counter); | ||||
| 				MonthlyArray.add(Inner); | ||||
| 			} | ||||
| 			OrgObject.set("index",MonthlyArray); | ||||
| 			A.add(OrgObject); | ||||
| 		} | ||||
| 		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<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_to_json(Poco::JSON::Object &Obj, const char *Field, const T &Value) { | ||||
| 		Poco::JSON::Object  Answer; | ||||
| 		Value.to_json(Answer); | ||||
| 		Obj.set(Field, Answer); | ||||
| 	} | ||||
|  | ||||
| 	/////////////////////////// | ||||
| 	/////////////////////////// | ||||
| 	/////////////////////////// | ||||
| 	/////////////////////////// | ||||
|  | ||||
| 	template<typename T> bool field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T & V, | ||||
| 						 std::function<T(const std::string &)> F) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			V = F(Obj->get(Field).toString()); | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::string &S) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			S = Obj->get(Field).toString(); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, double & Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (double)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, float & Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (float)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, bool &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (Obj->get(Field).toString() == "true"); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int16_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (int16_t)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int32_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (int32_t) Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, int64_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (int64_t)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint16_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (uint16_t)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint32_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (uint32_t)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, uint64_t &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) | ||||
| 			Value = (uint64_t)Obj->get(Field); | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Poco::Data::BLOB &Value) { | ||||
| 		if(Obj->has(Field) && !Obj->isNull(Field)) { | ||||
| 			auto Result = Utils::base64decode(Obj->get(Field).toString()); | ||||
| 			Value.assignRaw((const unsigned char *)&Result[0],Result.size()); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringPairVec &Vec) { | ||||
| 		if(Obj->isArray(Field) && !Obj->isNull(Field)) { | ||||
| 			auto O = Obj->getArray(Field); | ||||
| 			for(const auto &i:*O) { | ||||
| 				std::string S1,S2; | ||||
| 				auto Inner = i.extract<Poco::JSON::Object::Ptr>(); | ||||
| 				if(Inner->has("tag")) | ||||
| 					S1 = Inner->get("tag").toString(); | ||||
| 				if(Inner->has("value")) | ||||
| 					S2 = Inner->get("value").toString(); | ||||
| 				auto P = std::make_pair(S1,S2); | ||||
| 				Vec.push_back(P); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::StringVec &Value) { | ||||
| 		if(Obj->isArray(Field) && !Obj->isNull(Field)) { | ||||
| 			Value.clear(); | ||||
| 			Poco::JSON::Array::Ptr A = Obj->getArray(Field); | ||||
| 			for(const auto &i:*A) { | ||||
| 				Value.push_back(i.toString()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, Types::TagList &Value) { | ||||
| 		if(Obj->isArray(Field) && !Obj->isNull(Field)) { | ||||
| 			Value.clear(); | ||||
| 			Poco::JSON::Array::Ptr A = Obj->getArray(Field); | ||||
| 			for(const auto &i:*A) { | ||||
| 				Value.push_back(i); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	template<class T> void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector<T> &Value) { | ||||
| 		if(Obj->isArray(Field) && !Obj->isNull(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) && !Obj->isNull(Field)) { | ||||
| 			Poco::JSON::Object::Ptr	A = Obj->getObject(Field); | ||||
| 			Value.from_json(A); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline std::string to_string(const Types::TagList & ObjectArray) { | ||||
| 		Poco::JSON::Array OutputArr; | ||||
| 		if(ObjectArray.empty()) | ||||
| 			return "[]"; | ||||
| 		for(auto const &i:ObjectArray) { | ||||
| 			OutputArr.add(i); | ||||
| 		} | ||||
| 		std::ostringstream OS; | ||||
| 		Poco::JSON::Stringifier::stringify(OutputArr,OS, 0,0, Poco::JSON_PRESERVE_KEY_ORDER ); | ||||
| 		return OS.str(); | ||||
| 	} | ||||
|  | ||||
| 	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(); | ||||
| 	} | ||||
|  | ||||
| 	inline std::string to_string(const Types::StringPairVec & ObjectArray) { | ||||
| 		Poco::JSON::Array OutputArr; | ||||
| 		if(ObjectArray.empty()) | ||||
| 			return "[]"; | ||||
| 		for(auto const &i:ObjectArray) { | ||||
| 			Poco::JSON::Array InnerArray; | ||||
| 			InnerArray.add(i.first); | ||||
| 			InnerArray.add(i.second); | ||||
| 			OutputArr.add(InnerArray); | ||||
| 		} | ||||
| 		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 std::vector<std::vector<T>> & ObjectArray) { | ||||
| 		Poco::JSON::Array OutputArr; | ||||
| 		if(ObjectArray.empty()) | ||||
| 			return "[]"; | ||||
| 		for(auto const &i:ObjectArray) { | ||||
| 			Poco::JSON::Array InnerArr; | ||||
| 			for(auto const &j:i) { | ||||
| 				if constexpr(std::is_integral<T>::value) { | ||||
| 					InnerArr.add(j); | ||||
| 				} if constexpr(std::is_same_v<T,std::string>) { | ||||
| 					InnerArr.add(j); | ||||
| 				} else { | ||||
| 					InnerArr.add(j); | ||||
| 					Poco::JSON::Object O; | ||||
| 					j.to_json(O); | ||||
| 					InnerArr.add(O); | ||||
| 				} | ||||
| 			} | ||||
| 			OutputArr.add(InnerArr); | ||||
| 		} | ||||
| 		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; | ||||
| 	} | ||||
|  | ||||
| 	inline OpenWifi::Types::TagList to_taglist(const std::string & ObjectString) { | ||||
| 		Types::TagList 	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); | ||||
| 			} | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
| 		return Result; | ||||
| 	} | ||||
|  | ||||
| 	inline Types::StringPairVec to_stringpair_array(const std::string &S) { | ||||
| 		Types::StringPairVec   R; | ||||
| 		if(S.empty()) | ||||
| 			return R; | ||||
| 		try { | ||||
| 			Poco::JSON::Parser P; | ||||
| 			auto Object = P.parse(S).template extract<Poco::JSON::Array::Ptr>(); | ||||
| 			for (const auto &i : *Object) { | ||||
| 				auto InnerObject = i.template extract<Poco::JSON::Array::Ptr>(); | ||||
| 				if(InnerObject->size()==2) { | ||||
| 					auto S1 = InnerObject->getElement<std::string>(0); | ||||
| 					auto S2 = InnerObject->getElement<std::string>(1); | ||||
| 					R.push_back(std::make_pair(S1,S2)); | ||||
| 				} | ||||
| 			} | ||||
| 		} catch (...) { | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		return R; | ||||
| 	} | ||||
|  | ||||
| 	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> std::vector<std::vector<T>> to_array_of_array_of_object(const std::string & ObjectString) { | ||||
| 		std::vector<std::vector<T>>	Result; | ||||
| 		if(ObjectString.empty()) | ||||
| 			return Result; | ||||
| 		try { | ||||
| 			Poco::JSON::Parser P1; | ||||
| 			auto OutterArray = P1.parse(ObjectString).template extract<Poco::JSON::Array::Ptr>(); | ||||
| 			for (auto const &i : *OutterArray) { | ||||
| 				Poco::JSON::Parser P2; | ||||
| 				auto InnerArray = P2.parse(i).template extract<Poco::JSON::Array::Ptr>(); | ||||
| 				std::vector<T>  InnerVector; | ||||
| 				for(auto const &j: *InnerArray) { | ||||
| 					auto Object = j.template extract<Poco::JSON::Object::Ptr>(); | ||||
| 					T Obj; | ||||
| 					Obj.from_json(Object); | ||||
| 					InnerVector.push_back(Obj); | ||||
| 				} | ||||
| 				Result.push_back(InnerVector); | ||||
| 			} | ||||
| 		} 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; | ||||
| 	} | ||||
| } | ||||
| @@ -14,7 +14,8 @@ | ||||
| #include "Poco/Data/MySQL/Connector.h" | ||||
| #endif | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "framework/SubSystemServer.h" | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     enum DBType { | ||||
| @@ -34,7 +35,7 @@ namespace OpenWifi { | ||||
|             std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
|             Logger().notice("Starting."); | ||||
|             std::string DBType = MicroService::instance().ConfigGetString("storage.type"); | ||||
|             std::string DBType = MicroServiceConfigGetString("storage.type",""); | ||||
|  | ||||
|             if (DBType == "sqlite") { | ||||
|                 Setup_SQLite(); | ||||
| @@ -72,9 +73,9 @@ namespace OpenWifi { | ||||
|     inline int StorageClass::Setup_SQLite() { | ||||
|         Logger().notice("SQLite StorageClass enabled."); | ||||
|         dbType_ = sqlite; | ||||
|         auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); | ||||
|         int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); | ||||
|         int IdleTime = (int) MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); | ||||
|         auto DBName = MicroServiceDataDirectory() + "/" + MicroServiceConfigGetString("storage.type.sqlite.db",""); | ||||
|         int NumSessions = (int) MicroServiceConfigGetInt("storage.type.sqlite.maxsessions", 64); | ||||
|         int IdleTime = (int) MicroServiceConfigGetInt("storage.type.sqlite.idletime", 60); | ||||
|  | ||||
|         Poco::Data::SQLite::Connector::registerConnector(); | ||||
| //        Pool_ = std::make_unique<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 8, | ||||
| @@ -87,13 +88,13 @@ namespace OpenWifi { | ||||
|     inline int StorageClass::Setup_MySQL() { | ||||
|         Logger().notice("MySQL StorageClass enabled."); | ||||
|         dbType_ = mysql; | ||||
|         int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.mysql.maxsessions", 64); | ||||
|         int IdleTime = (int) 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"); | ||||
|         int NumSessions = (int) MicroServiceConfigGetInt("storage.type.mysql.maxsessions", 64); | ||||
|         int IdleTime = (int) MicroServiceConfigGetInt("storage.type.mysql.idletime", 60); | ||||
|         auto Host = MicroServiceConfigGetString("storage.type.mysql.host",""); | ||||
|         auto Username = MicroServiceConfigGetString("storage.type.mysql.username",""); | ||||
|         auto Password = MicroServiceConfigGetString("storage.type.mysql.password",""); | ||||
|         auto Database = MicroServiceConfigGetString("storage.type.mysql.database",""); | ||||
|         auto Port = MicroServiceConfigGetString("storage.type.mysql.port",""); | ||||
|  | ||||
|         std::string ConnectionStr = | ||||
|                 "host=" + Host + | ||||
| @@ -112,14 +113,14 @@ namespace OpenWifi { | ||||
|     inline int StorageClass::Setup_PostgreSQL() { | ||||
|         Logger().notice("PostgreSQL StorageClass enabled."); | ||||
|         dbType_ = pgsql; | ||||
|         int NumSessions = (int) MicroService::instance().ConfigGetInt("storage.type.postgresql.maxsessions", 64); | ||||
|         int IdleTime = (int) 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"); | ||||
|         int NumSessions = (int) MicroServiceConfigGetInt("storage.type.postgresql.maxsessions", 64); | ||||
|         int IdleTime = (int) MicroServiceConfigGetInt("storage.type.postgresql.idletime", 60); | ||||
|         auto Host = MicroServiceConfigGetString("storage.type.postgresql.host", ""); | ||||
|         auto Username = MicroServiceConfigGetString("storage.type.postgresql.username", ""); | ||||
|         auto Password = MicroServiceConfigGetString("storage.type.postgresql.password", ""); | ||||
|         auto Database = MicroServiceConfigGetString("storage.type.postgresql.database", ""); | ||||
|         auto Port = MicroServiceConfigGetString("storage.type.postgresql.port", ""); | ||||
|         auto ConnectionTimeout = MicroServiceConfigGetString("storage.type.postgresql.connectiontimeout", ""); | ||||
|  | ||||
|         std::string ConnectionStr = | ||||
|                 "host=" + Host + | ||||
|   | ||||
							
								
								
									
										320
									
								
								src/framework/SubSystemServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								src/framework/SubSystemServer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,320 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #include "fmt/format.h" | ||||
|  | ||||
| #include "framework/SubSystemServer.h" | ||||
|  | ||||
| #include "Poco/Net/SSLManager.h" | ||||
| #include "Poco/DateTimeFormatter.h" | ||||
| #include "Poco/DateTimeFormat.h" | ||||
|  | ||||
| #include "framework/MicroServiceFuncs.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	PropertiesFileServerEntry::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, | ||||
| 							  int backlog) | ||||
| 		: address_(std::move(Address)), | ||||
| 		  port_(port), | ||||
| 		  cert_file_(std::move(Cert_file)), | ||||
| 		  key_file_(std::move(Key_file)), | ||||
| 		  root_ca_(std::move(RootCa)), | ||||
| 		  key_file_password_(std::move(Key_file_password)), | ||||
| 		  issuer_cert_file_(std::move(Issuer)), | ||||
| 		  client_cas_(std::move(ClientCas)), | ||||
| 		  cas_(std::move(Cas)), | ||||
| 		  name_(std::move(Name)), | ||||
| 		  backlog_(backlog), | ||||
| 		  level_(M) { | ||||
|  | ||||
| 	  }; | ||||
|  | ||||
| 	[[nodiscard]] 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(fmt::format("Wrong Certificate({}) for Key({})", 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(60); | ||||
| 			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); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] Poco::Net::ServerSocket PropertiesFileServerEntry::CreateSocket([[maybe_unused]] Poco::Logger &L) const { | ||||
| 		Poco::Net::Context::Params P; | ||||
|  | ||||
| 		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::ServerSocket(SockAddr, backlog_); | ||||
| 		} else { | ||||
| 			Poco::Net::IPAddress Addr(address_); | ||||
| 			Poco::Net::SocketAddress SockAddr(Addr, port_); | ||||
| 			return Poco::Net::ServerSocket(SockAddr, backlog_); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void PropertiesFileServerEntry::LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const { | ||||
| 		L.information("============================================================================================="); | ||||
| 		L.information(fmt::format(">          Issuer: {}", C.issuerName())); | ||||
| 		L.information("---------------------------------------------------------------------------------------------"); | ||||
| 		L.information(fmt::format(">     Common Name: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); | ||||
| 		L.information(fmt::format(">         Country: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); | ||||
| 		L.information(fmt::format(">        Locality: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); | ||||
| 		L.information(fmt::format(">      State/Prov: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); | ||||
| 		L.information(fmt::format(">        Org name: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); | ||||
| 		L.information( | ||||
| 			fmt::format(">        Org unit: {}", | ||||
| 						C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); | ||||
| 		L.information( | ||||
| 			fmt::format(">           Email: {}", | ||||
| 						C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); | ||||
| 		L.information(fmt::format(">         Serial#: {}", | ||||
| 								  C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); | ||||
| 		L.information("---------------------------------------------------------------------------------------------"); | ||||
| 		L.information(fmt::format(">         Subject: {}", C.subjectName())); | ||||
| 		L.information("---------------------------------------------------------------------------------------------"); | ||||
| 		L.information(fmt::format(">     Common Name: {}", | ||||
| 								  C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); | ||||
| 		L.information(fmt::format(">         Country: {}", | ||||
| 								  C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); | ||||
| 		L.information(fmt::format(">        Locality: {}", | ||||
| 								  C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); | ||||
| 		L.information( | ||||
| 			fmt::format(">      State/Prov: {}", | ||||
| 						C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); | ||||
| 		L.information( | ||||
| 			fmt::format(">        Org name: {}", | ||||
| 						C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); | ||||
| 		L.information( | ||||
| 			fmt::format(">        Org unit: {}", | ||||
| 						C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); | ||||
| 		L.information( | ||||
| 			fmt::format(">           Email: {}", | ||||
| 						C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); | ||||
| 		L.information(fmt::format(">         Serial#: {}", | ||||
| 								  C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); | ||||
| 		L.information("---------------------------------------------------------------------------------------------"); | ||||
| 		L.information(fmt::format(">  Signature Algo: {}", C.signatureAlgorithm())); | ||||
| 		auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); | ||||
| 		L.information(fmt::format(">      Valid from: {}", From)); | ||||
| 		auto Expires = | ||||
| 			Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); | ||||
| 		L.information(fmt::format(">      Expires on: {}", Expires)); | ||||
| 		L.information(fmt::format(">         Version: {}", (int)C.version())); | ||||
| 		L.information(fmt::format(">        Serial #: {}", C.serialNumber())); | ||||
| 		L.information("============================================================================================="); | ||||
| 	} | ||||
|  | ||||
| 	void PropertiesFileServerEntry::LogCert(Poco::Logger &L) const { | ||||
| 		try { | ||||
| 			Poco::Crypto::X509Certificate C(cert_file_); | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information("============================================================================================="); | ||||
| 			L.information(fmt::format("Certificate Filename: {}", 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(fmt::format("Issues Certificate Filename: {}", 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(fmt::format("Client CAs Filename: {}", client_cas_)); | ||||
| 				L.information("============================================================================================="); | ||||
| 				auto i = 1; | ||||
| 				for (const auto &C3 : Certs) { | ||||
| 					L.information(fmt::format(" Index: {}", 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(fmt::format("CA Filename: {}", root_ca_)); | ||||
| 			L.information("============================================================================================="); | ||||
| 			auto i = 1; | ||||
| 			for (const auto &C : Certs) { | ||||
| 				L.information(fmt::format(" Index: {}", i)); | ||||
| 				L.information("============================================================================================="); | ||||
| 				LogCertInfo(L, C); | ||||
| 				i++; | ||||
| 			} | ||||
| 			L.information("============================================================================================="); | ||||
| 		} catch (const Poco::Exception &E) { | ||||
| 			L.log(E); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	SubSystemServer::SubSystemServer(const std::string &Name, const std::string &LoggingPrefix, | ||||
| 											const std::string &SubSystemConfigPrefix): | ||||
| 		Name_(Name), | ||||
| 		LoggerPrefix_(LoggingPrefix), | ||||
| 		SubSystemConfigPrefix_(SubSystemConfigPrefix) { | ||||
| 	} | ||||
|  | ||||
| 	void SubSystemServer::initialize([[maybe_unused]] Poco::Util::Application &self) { | ||||
| 		auto i = 0; | ||||
| 		bool good = true; | ||||
|  | ||||
| 		auto NewLevel = MicroServiceConfigGetString("logging.level." + Name_, ""); | ||||
| 		if(NewLevel.empty()) | ||||
| 			Logger_ = std::make_unique<LoggerWrapper>(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::root().getLevel())); | ||||
| 		else | ||||
| 			Logger_ = std::make_unique<LoggerWrapper>(Poco::Logger::create(LoggerPrefix_, Poco::Logger::root().getChannel(), Poco::Logger::parseLevel(NewLevel))); | ||||
|  | ||||
| 		ConfigServersList_.clear(); | ||||
| 		while (good) { | ||||
| 			std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; | ||||
|  | ||||
| 			std::string address{root + "address"}; | ||||
| 			if (MicroServiceConfigGetString(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 = MicroServiceConfigGetString(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(MicroServiceConfigGetString(address, ""), | ||||
| 												MicroServiceConfigGetInt(port, 0), | ||||
| 												MicroServiceConfigPath(key, ""), | ||||
| 												MicroServiceConfigPath(cert, ""), | ||||
| 												MicroServiceConfigPath(rootca, ""), | ||||
| 												MicroServiceConfigPath(issuer, ""), | ||||
| 												MicroServiceConfigPath(clientcas, ""), | ||||
| 												MicroServiceConfigPath(cas, ""), | ||||
| 												MicroServiceConfigGetString(key_password, ""), | ||||
| 												MicroServiceConfigGetString(name, ""), M, | ||||
| 												(int)MicroServiceConfigGetInt(backlog, 64)); | ||||
| 				ConfigServersList_.push_back(entry); | ||||
| 				i++; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| } // namespace OpenWifi | ||||
							
								
								
									
										124
									
								
								src/framework/SubSystemServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								src/framework/SubSystemServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2022-10-25. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
| #include <mutex> | ||||
|  | ||||
| #include "Poco/Util/Application.h" | ||||
| #include "Poco/Net/Context.h" | ||||
| #include "Poco/Net/SecureServerSocket.h" | ||||
| #include "Poco/Net/PrivateKeyPassphraseHandler.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| 	class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { | ||||
| 	  public: | ||||
| 		explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): | ||||
| 			PrivateKeyPassphraseHandler(true), | ||||
| 			Password_(Password), | ||||
| 			Logger_(Logger) { | ||||
| 		} | ||||
|  | ||||
| 		void onPrivateKeyRequested([[maybe_unused]] const void * pSender,std::string & privateKey) { | ||||
| 			poco_information(Logger_,"Returning key passphrase."); | ||||
| 			privateKey = Password_; | ||||
| 		}; | ||||
| 		inline Poco::Logger & Logger() { return Logger_; } | ||||
| 	  private: | ||||
| 		std::string Password_; | ||||
| 		Poco::Logger & Logger_; | ||||
| 	}; | ||||
|  | ||||
| 	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); | ||||
|  | ||||
| 		[[nodiscard]] inline const std::string &Address() const { return address_; }; | ||||
| 		[[nodiscard]] inline uint32_t Port() const { return port_; }; | ||||
| 		[[nodiscard]] inline auto KeyFile() const { return key_file_; }; | ||||
| 		[[nodiscard]] inline auto CertFile() const { return cert_file_; }; | ||||
| 		[[nodiscard]] inline auto RootCA() const { return root_ca_; }; | ||||
| 		[[nodiscard]] inline auto KeyFilePassword() const { return key_file_password_; }; | ||||
| 		[[nodiscard]] inline auto IssuerCertFile() const { return issuer_cert_file_; }; | ||||
| 		[[nodiscard]] inline auto Name() const { return name_; }; | ||||
| 		[[nodiscard]] inline int Backlog() const { return backlog_; } | ||||
| 		[[nodiscard]] inline auto Cas() const { return cas_; } | ||||
|  | ||||
| 		[[nodiscard]] Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const; | ||||
| 		[[nodiscard]] Poco::Net::ServerSocket CreateSocket([[maybe_unused]] Poco::Logger &L) const; | ||||
| 		void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C) const; | ||||
| 		void LogCert(Poco::Logger &L) const; | ||||
| 		void LogCas(Poco::Logger &L) const; | ||||
|  | ||||
| 	  private: | ||||
| 		std::string address_; | ||||
| 		uint32_t port_; | ||||
| 		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_; | ||||
| 		std::string name_; | ||||
| 		int backlog_; | ||||
| 		Poco::Net::Context::VerificationMode level_; | ||||
| 	}; | ||||
|  | ||||
| 	class SubSystemServer : public Poco::Util::Application::Subsystem { | ||||
| 	  public: | ||||
| 		SubSystemServer(const std::string & Name, const std::string &LoggingPrefix, | ||||
| 						const std::string & SubSystemConfigPrefix); | ||||
|  | ||||
| 		void initialize(Poco::Util::Application &self) override; | ||||
| 		inline void uninitialize() override { | ||||
| 		} | ||||
| 		inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override { | ||||
| 			poco_information(Logger_->L_,"Reloading of this subsystem is not supported."); | ||||
| 		} | ||||
| 		inline void defineOptions([[maybe_unused]] Poco::Util::OptionSet &options) override { | ||||
| 		} | ||||
| 		inline const std::string & Name() const { return Name_; }; | ||||
| 		inline const char * name() const override { return Name_.c_str(); } | ||||
|  | ||||
| 		inline const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; | ||||
| 		inline uint64_t HostSize() const { return ConfigServersList_.size(); } | ||||
| 		inline Poco::Logger & Logger() const { return Logger_->L_; } | ||||
| 		inline void SetLoggingLevel(const std::string & levelName) { | ||||
| 			Logger_->L_.setLevel(Poco::Logger::parseLevel(levelName)); | ||||
| 		} | ||||
| 		inline int GetLoggingLevel() { return Logger_->L_.getLevel(); } | ||||
|  | ||||
| 		virtual int Start() = 0; | ||||
| 		virtual void Stop() = 0; | ||||
|  | ||||
| 		struct LoggerWrapper { | ||||
| 			Poco::Logger & L_; | ||||
| 			LoggerWrapper(Poco::Logger &L) : | ||||
|  				L_(L) { | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 	  protected: | ||||
| 		std::recursive_mutex 			Mutex_; | ||||
| 		std::vector<PropertiesFileServerEntry> ConfigServersList_; | ||||
|  | ||||
| 	  private: | ||||
| 		std::unique_ptr<LoggerWrapper>  Logger_; | ||||
| 		std::string 					Name_; | ||||
| 		std::string         			LoggerPrefix_; | ||||
| 		std::string 					SubSystemConfigPrefix_; | ||||
| 	}; | ||||
|  | ||||
| 	typedef std::vector<SubSystemServer *>          SubSystemVec; | ||||
|  | ||||
| } // namespace OpenWifi | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user