mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
				synced 2025-10-30 18:27:49 +00:00 
			
		
		
		
	Compare commits
	
		
			92 Commits
		
	
	
		
			v2.2.0-RC1
			...
			release/v2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6a9b0f143e | ||
|   | 736d981853 | ||
|   | 423aca9892 | ||
|   | 9a27996d44 | ||
|   | 4840ff887f | ||
|   | 61140868b5 | ||
|   | 56308dfa5e | ||
|   | 8ff25257ca | ||
|   | 9ca6853791 | ||
|   | 064c486158 | ||
|   | 0e58d04b32 | ||
|   | d695614567 | ||
|   | ed13053648 | ||
|   | 5cb9e7566e | ||
|   | b00938eab0 | ||
|   | b9495264ee | ||
|   | 22ac42221e | ||
|   | 559ce2dc88 | ||
|   | 75fbabdc0b | ||
|   | b5b7d27abd | ||
|   | 1a7bf8dba7 | ||
|   | 35bc0d8a5c | ||
|   | b8ff262e01 | ||
|   | c577a4d23a | ||
|   | 0f26f359dd | ||
|   | 117e820d1e | ||
|   | 670e61640f | ||
|   | 75aaf4f45b | ||
|   | 7161175f03 | ||
|   | cc83b29756 | ||
|   | 581cc76625 | ||
|   | 184c30d7bb | ||
|   | 6057b421ac | ||
|   | fcd8157020 | ||
|   | cd7a6f4ebd | ||
|   | 615bf04df6 | ||
|   | 819c32edcf | ||
|   | d805fd2d50 | ||
|   | 217c680fce | ||
|   | 796eed2e2f | ||
|   | 29226c81e4 | ||
|   | f7cb82b2f2 | ||
|   | 9119b65516 | ||
|   | 1540df93e8 | ||
|   | 66832e1581 | ||
|   | 84238702cf | ||
|   | fd63f7fd31 | ||
|   | 8b427e30a2 | ||
|   | af6dff3f57 | ||
|   | 7070da80f7 | ||
|   | 36f046e589 | ||
|   | 166fa840d2 | ||
|   | f4865c933a | ||
|   | 6531d550c2 | ||
|   | 1e8bf5063f | ||
|   | be8b55f5fd | ||
|   | c28f0cf929 | ||
|   | 405ca345be | ||
|   | 85ffd8b68c | ||
|   | 20227b0cd9 | ||
|   | e66a498889 | ||
|   | a65d22ccb3 | ||
|   | 0013f47cbf | ||
|   | 69da5c17cf | ||
|   | a199d4e095 | ||
|   | 448b5949d8 | ||
|   | 82a6d61724 | ||
|   | 21ba9f2bb1 | ||
|   | 9debb06f21 | ||
|   | 1af2afc530 | ||
|   | fc454ad4f9 | ||
|   | 99c8eb2900 | ||
|   | 18591e2add | ||
|   | 0e0cb8a0c7 | ||
|   | f7e791c125 | ||
|   | 9e6ef8bb1b | ||
|   | 956ec15532 | ||
|   | 8721354284 | ||
|   | 659fbf9dc1 | ||
|   | 1209b772ee | ||
|   | e0e8f5fae6 | ||
|   | d6e5f379a0 | ||
|   | 4dda1ee5b3 | ||
|   | abd65c347c | ||
|   | 767c0fb9f5 | ||
|   | dc3d6042d5 | ||
|   | fcedf63ef9 | ||
|   | 30861ed934 | ||
|   | ee537b3383 | ||
|   | 7d9d985142 | ||
|   | daa060c849 | ||
|   | f25047cbe7 | 
| @@ -1,5 +1,5 @@ | |||||||
| cmake_minimum_required(VERSION 3.13) | cmake_minimum_required(VERSION 3.13) | ||||||
| project(owsec VERSION 2.2.0) | project(owsec VERSION 2.3.0) | ||||||
|  |  | ||||||
| set(CMAKE_CXX_STANDARD 17) | set(CMAKE_CXX_STANDARD 17) | ||||||
|  |  | ||||||
| @@ -41,48 +41,57 @@ set(Boost_USE_STATIC_RUNTIME OFF) | |||||||
| find_package(Boost REQUIRED system) | find_package(Boost REQUIRED system) | ||||||
| find_package(OpenSSL REQUIRED) | find_package(OpenSSL REQUIRED) | ||||||
| find_package(ZLIB REQUIRED) | find_package(ZLIB REQUIRED) | ||||||
|  | find_package(AWSSDK     REQUIRED COMPONENTS sns) | ||||||
|  |  | ||||||
| find_package(CppKafka REQUIRED) | find_package(CppKafka REQUIRED) | ||||||
| find_package(PostgreSQL REQUIRED) | find_package(PostgreSQL REQUIRED) | ||||||
| find_package(MySQL REQUIRED) | find_package(MySQL REQUIRED) | ||||||
| find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataSQLite DataPostgreSQL DataMySQL) | find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataSQLite DataPostgreSQL DataMySQL) | ||||||
|  |  | ||||||
|  | include_directories(/usr/local/include  /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include) | ||||||
|  |  | ||||||
| add_executable( owsec | add_executable( owsec | ||||||
|                 build |         build | ||||||
|                 src/Daemon.h src/Daemon.cpp |         src/framework/CountryCodes.h | ||||||
|                 src/MicroService.cpp src/MicroService.h |         src/framework/KafkaTopics.h | ||||||
|                 src/SubSystemServer.cpp src/SubSystemServer.h |         src/framework/MicroService.h | ||||||
|                 src/RESTAPI_oauth2Handler.h src/RESTAPI_oauth2Handler.cpp |         src/framework/OpenWifiTypes.h | ||||||
|                 src/RESTAPI_handler.h src/RESTAPI_handler.cpp |         src/framework/orm.h | ||||||
|                 src/RESTAPI_server.cpp src/RESTAPI_server.h |         src/framework/RESTAPI_errors.h | ||||||
|                 src/RESTAPI_SecurityObjects.cpp src/RESTAPI_SecurityObjects.h |         src/framework/RESTAPI_protocol.h | ||||||
|                 src/RESTAPI_system_command.h src/RESTAPI_system_command.cpp |         src/framework/StorageClass.h | ||||||
|                 src/RESTAPI_protocol.h |         src/framework/uCentral_Protocol.h | ||||||
|                 src/AuthService.h src/AuthService.cpp |         src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp | ||||||
|                 src/KafkaManager.h src/KafkaManager.cpp |         src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h | ||||||
|                 src/StorageService.cpp src/StorageService.h |         src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp | ||||||
|                 src/Utils.cpp src/Utils.h |         src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp | ||||||
|                 src/storage_setup.cpp |         src/RESTAPI/RESTAPI_oauth2Handler.h src/RESTAPI/RESTAPI_oauth2Handler.cpp | ||||||
|                 src/storage_tables.cpp src/SMTPMailerService.cpp src/SMTPMailerService.h |         src/RESTAPI/RESTAPI_users_handler.cpp src/RESTAPI/RESTAPI_users_handler.h | ||||||
|                 src/RESTAPI_users_handler.cpp src/RESTAPI_users_handler.h |         src/RESTAPI/RESTAPI_user_handler.cpp src/RESTAPI/RESTAPI_user_handler.h | ||||||
|                 src/RESTAPI_user_handler.cpp src/RESTAPI_user_handler.h |         src/RESTAPI/RESTAPI_action_links.cpp src/RESTAPI/RESTAPI_action_links.h | ||||||
|                 src/RESTAPI_action_links.cpp src/RESTAPI_action_links.h src/storage_users.cpp |         src/RESTAPI/RESTAPI_validateToken_handler.cpp src/RESTAPI/RESTAPI_validateToken_handler.h | ||||||
|                 src/RESTAPI_InternalServer.cpp src/RESTAPI_InternalServer.h |         src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp src/RESTAPI/RESTAPI_systemEndpoints_handler.h | ||||||
|                 src/RESTAPI_validateToken_handler.cpp src/RESTAPI_validateToken_handler.h |         src/RESTAPI/RESTAPI_AssetServer.cpp src/RESTAPI/RESTAPI_AssetServer.h | ||||||
|                 src/RESTAPI_systemEndpoints_handler.cpp src/RESTAPI_systemEndpoints_handler.h |         src/RESTAPI/RESTAPI_avatarHandler.cpp src/RESTAPI/RESTAPI_avatarHandler.h | ||||||
|                 src/RESTAPI_AssetServer.cpp src/RESTAPI_AssetServer.h |         src/RESTAPI/RESTAPI_email_handler.cpp src/RESTAPI/RESTAPI_email_handler.h | ||||||
|                 src/RESTAPI_avatarHandler.cpp src/RESTAPI_avatarHandler.h |         src/RESTAPI/RESTAPI_sms_handler.cpp src/RESTAPI/RESTAPI_sms_handler.h | ||||||
|                 src/storage_avatar.cpp src/storage_avatar.h src/storage_users.h |         src/storage/storage_avatar.cpp src/storage/storage_avatar.h src/storage/storage_users.h | ||||||
|                 src/OpenWifiTypes.h src/RESTAPI_email_handler.cpp src/RESTAPI_email_handler.h |         src/storage/storage_tables.cpp src/storage/storage_users.cpp src/storage/storage_tokens.cpp | ||||||
|                 src/storage_tokens.cpp |         src/APIServers.cpp | ||||||
|                 src/RESTAPI_GenericServer.h src/RESTAPI_GenericServer.cpp |         src/Daemon.h src/Daemon.cpp | ||||||
|                 src/RESTAPI_errors.h |         src/AuthService.h src/AuthService.cpp | ||||||
|                 ) |         src/StorageService.cpp src/StorageService.h | ||||||
|  |         src/SMTPMailerService.cpp src/SMTPMailerService.h | ||||||
|  |         src/SMSSender.cpp src/SMSSender.h | ||||||
|  |         src/MFAServer.cpp src/MFAServer.h | ||||||
|  |         src/SMS_provider_aws.cpp src/SMS_provider_aws.h | ||||||
|  |         src/SMS_provider.cpp src/SMS_provider.h | ||||||
|  |         src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h) | ||||||
|  |  | ||||||
| if(NOT SMALL_BUILD) | if(NOT SMALL_BUILD) | ||||||
|     target_link_libraries(owsec PUBLIC |     target_link_libraries(owsec PUBLIC | ||||||
|             ${Poco_LIBRARIES} ${Boost_LIBRARIES} ${MySQL_LIBRARIES}  ${ZLIB_LIBRARIES} |             ${Poco_LIBRARIES} ${Boost_LIBRARIES} ${MySQL_LIBRARIES}  ${ZLIB_LIBRARIES} | ||||||
|             CppKafka::cppkafka |             CppKafka::cppkafka ${AWSSDK_LINK_LIBRARIES} | ||||||
|             ) |             ) | ||||||
|     if(UNIX AND NOT APPLE) |     if(UNIX AND NOT APPLE) | ||||||
|         target_link_libraries(owsec PUBLIC PocoJSON) |         target_link_libraries(owsec PUBLIC PocoJSON) | ||||||
|   | |||||||
							
								
								
									
										26
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -3,14 +3,25 @@ FROM alpine AS builder | |||||||
| RUN apk add --update --no-cache \ | RUN apk add --update --no-cache \ | ||||||
|     openssl openssh \ |     openssl openssh \ | ||||||
|     ncurses-libs \ |     ncurses-libs \ | ||||||
|     bash util-linux coreutils curl \ |     bash util-linux coreutils curl libcurl \ | ||||||
|     make cmake gcc g++ libstdc++ libgcc git zlib-dev \ |     make cmake gcc g++ libstdc++ libgcc git zlib-dev \ | ||||||
|     openssl-dev boost-dev unixodbc-dev postgresql-dev mariadb-dev \ |     openssl-dev boost-dev curl-dev unixodbc-dev postgresql-dev mariadb-dev \ | ||||||
|     apache2-utils yaml-dev apr-util-dev \ |     apache2-utils yaml-dev apr-util-dev \ | ||||||
|     librdkafka-dev |     librdkafka-dev | ||||||
|  |  | ||||||
| RUN git clone https://github.com/stephb9959/poco /poco | RUN git clone https://github.com/stephb9959/poco /poco | ||||||
| RUN git clone https://github.com/stephb9959/cppkafka /cppkafka | RUN git clone https://github.com/stephb9959/cppkafka /cppkafka | ||||||
|  | RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp /aws-sdk-cpp | ||||||
|  |  | ||||||
|  | WORKDIR /aws-sdk-cpp | ||||||
|  | RUN mkdir cmake-build | ||||||
|  | WORKDIR cmake-build | ||||||
|  | RUN cmake .. -DBUILD_ONLY="sns;s3" \ | ||||||
|  |              -DCMAKE_BUILD_TYPE=Release \ | ||||||
|  |              -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 | ||||||
|  |  | ||||||
| WORKDIR /cppkafka | WORKDIR /cppkafka | ||||||
| RUN mkdir cmake-build | RUN mkdir cmake-build | ||||||
| @@ -48,16 +59,23 @@ RUN addgroup -S "$OWSEC_USER" && \ | |||||||
| RUN mkdir /openwifi | RUN mkdir /openwifi | ||||||
| RUN mkdir -p "$OWSEC_ROOT" "$OWSEC_CONFIG" && \ | RUN mkdir -p "$OWSEC_ROOT" "$OWSEC_CONFIG" && \ | ||||||
|     chown "$OWSEC_USER": "$OWSEC_ROOT" "$OWSEC_CONFIG" |     chown "$OWSEC_USER": "$OWSEC_ROOT" "$OWSEC_CONFIG" | ||||||
| RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates | RUN apk add --update --no-cache librdkafka mariadb-connector-c libpq unixodbc su-exec gettext ca-certificates libcurl curl-dev bash jq curl | ||||||
| COPY --from=builder /owsec/cmake-build/owsec /openwifi/owsec | COPY --from=builder /owsec/cmake-build/owsec /openwifi/owsec | ||||||
| COPY --from=builder /cppkafka/cmake-build/src/lib/* /lib/ | COPY --from=builder /cppkafka/cmake-build/src/lib/* /lib/ | ||||||
| COPY --from=builder /poco/cmake-build/lib/* /lib/ | COPY --from=builder /poco/cmake-build/lib/* /lib/ | ||||||
|  | COPY --from=builder /aws-sdk-cpp/cmake-build/aws-cpp-sdk-core/libaws-cpp-sdk-core.so /lib/ | ||||||
|  | COPY --from=builder /aws-sdk-cpp/cmake-build/aws-cpp-sdk-s3/libaws-cpp-sdk-s3.so /lib/ | ||||||
|  | COPY --from=builder /aws-sdk-cpp/cmake-build/aws-cpp-sdk-sns/libaws-cpp-sdk-sns.so /lib/ | ||||||
|  |  | ||||||
| COPY owsec.properties.tmpl ${OWSEC_CONFIG}/ | COPY owsec.properties.tmpl / | ||||||
|  | COPY wwwassets /dist/wwwassets | ||||||
|  | COPY templates /dist/templates | ||||||
| COPY docker-entrypoint.sh / | COPY docker-entrypoint.sh / | ||||||
| RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \ | RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \ | ||||||
|     -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem |     -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem | ||||||
|  |  | ||||||
|  | COPY readiness_check /readiness_check | ||||||
|  |  | ||||||
| EXPOSE 16001 17001 16101 | EXPOSE 16001 17001 16101 | ||||||
|  |  | ||||||
| ENTRYPOINT ["/docker-entrypoint.sh"] | ENTRYPOINT ["/docker-entrypoint.sh"] | ||||||
|   | |||||||
							
								
								
									
										59
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
									
									
									
									
								
							| @@ -13,12 +13,12 @@ into your own systems. If all you need it to access the uCentralGW for example ( | |||||||
| - choose one to manage (pick an endpoint that matches what you are trying to do by looking at its `type`. For the gateway, type = ucentrtalgw) | - choose one to manage (pick an endpoint that matches what you are trying to do by looking at its `type`. For the gateway, type = ucentrtalgw) | ||||||
| - make your calls (use the PublicEndPoint of the corresponding entry to make your calls, do not forget to add `/api/v1` as the root os the call) | - make your calls (use the PublicEndPoint of the corresponding entry to make your calls, do not forget to add `/api/v1` as the root os the call) | ||||||
|  |  | ||||||
| The CLI for the [uCentralGW](https://github.com/telecominfraproject/wlan-cloud-ucentralgw/blob/main/test_scripts/curl/cli) has a very good example of this. Loog for the `setgateway`  | The CLI for the [uCentralGW](https://github.com/telecominfraproject/wlan-cloud-ucentralgw/blob/main/test_scripts/curl/cli) has a very good example of this.  | ||||||
| function. | Look for the `setgateway` function. | ||||||
|  |  | ||||||
| ## Firewall Considerations | ## Firewall Considerations | ||||||
| The entire uCentral systems uses several MicroServices. In order for the whole system to work, you should provide the following port | The entire uCentral systems uses several MicroServices. In order for the whole system to work, you should provide the following port | ||||||
| access | access: | ||||||
|  |  | ||||||
| - Security | - Security | ||||||
|   - Properties file: owsec.properties |   - Properties file: owsec.properties | ||||||
| @@ -28,14 +28,21 @@ access | |||||||
|     - ALB: 16101 |     - ALB: 16101 | ||||||
|  |  | ||||||
| - Gateway: | - Gateway: | ||||||
|   - Properties file: ucentralgw.properties |   - Properties file: owgw.properties | ||||||
|   - Ports |   - Ports | ||||||
|     - Public: 16002 |     - Public: 16002 | ||||||
|     - Private: 17002 |     - Private: 17002 | ||||||
|     - ALB: 16102 |     - ALB: 16102 | ||||||
|  |  | ||||||
| - Firmware: | - Firmware: | ||||||
|   - Properties file: ucentralfms.properties |   - Properties file: owfms.properties | ||||||
|  |   - Ports | ||||||
|  |     - Public: 16004 | ||||||
|  |     - Private: 17004 | ||||||
|  |     - ALB: 16104 | ||||||
|  |  | ||||||
|  | - Provisioning: | ||||||
|  |   - Properties file: owprov.properties | ||||||
|   - Ports |   - Ports | ||||||
|     - Public: 16004 |     - Public: 16004 | ||||||
|     - Private: 17004 |     - Private: 17004 | ||||||
| @@ -79,7 +86,6 @@ Is this safe to show the hash in a text file? Let me put it this way, if you can | |||||||
| would have control over the entire internet. It's incredibly safe. If you love math, you can find a lot of videos explaining | would have control over the entire internet. It's incredibly safe. If you love math, you can find a lot of videos explaining | ||||||
| how hashes work and why they are safe. | how hashes work and why they are safe. | ||||||
|  |  | ||||||
|  |  | ||||||
| ### `authentication.validation.expression` | ### `authentication.validation.expression` | ||||||
| This is a regular expression (regex) to verify the incoming password. You can find many examples on the internet on how to create these expressions. I suggest  | This is a regular expression (regex) to verify the incoming password. You can find many examples on the internet on how to create these expressions. I suggest  | ||||||
| that using Google is your friend. Someone has figured out what you want to do already. Click [here](https://stackoverflow.com/questions/19605150/regex-for-password-must-contain-at-least-eight-characters-at-least-one-number-a) | that using Google is your friend. Someone has figured out what you want to do already. Click [here](https://stackoverflow.com/questions/19605150/regex-for-password-must-contain-at-least-eight-characters-at-least-one-number-a) | ||||||
| @@ -160,10 +166,11 @@ Here are other important values you must set. | |||||||
| openwifi.system.data = $OWSEC_ROOT/data | openwifi.system.data = $OWSEC_ROOT/data | ||||||
| openwifi.system.uri.private = https://localhost:17001 | openwifi.system.uri.private = https://localhost:17001 | ||||||
| openwifi.system.uri.public = https://openwifi.dpaas.arilia.com:16001 | openwifi.system.uri.public = https://openwifi.dpaas.arilia.com:16001 | ||||||
|  | openwifi.system.uri.ui = https://ucentral-ui.arilia.com | ||||||
| openwifi.system.commandchannel = /tmp/app.ucentralsec | openwifi.system.commandchannel = /tmp/app.ucentralsec | ||||||
| openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem | openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem | ||||||
|  | openwifi.service.key.password = mypassword | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| #### `openwifi.system.data` | #### `openwifi.system.data` | ||||||
| The location of some important data files including the user name database. | The location of some important data files including the user name database. | ||||||
|  |  | ||||||
| @@ -173,3 +180,41 @@ This is the FQDN used internally between services. | |||||||
| #### `openwifi.system.uri.public` | #### `openwifi.system.uri.public` | ||||||
| This is the FQDN used externally serving the OpenAPI interface. | This is the FQDN used externally serving the OpenAPI interface. | ||||||
|  |  | ||||||
|  | ### Sending SMS for Multifactor Aithentication | ||||||
|  | `owsec` hs the ability to send SMS messages to users during login or to send notifications. In order to do so, | ||||||
|  | an SMS provider must be configured. At present time, 2 providers are supported: Tilio and AWS SNS | ||||||
|  |  | ||||||
|  | #### AWS SNS | ||||||
|  | For SNS you must create an IAM ID that has sns:sendmessage rights.  | ||||||
|  |  | ||||||
|  | ```asm | ||||||
|  | smssender.provider = aws | ||||||
|  | smssender.aws.secretkey = *************************************** | ||||||
|  | smssender.aws.accesskey = *************************************** | ||||||
|  | smssender.aws.region = ************** | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Twilio | ||||||
|  | For Twilio, you must provide the following | ||||||
|  |  | ||||||
|  | ```asm | ||||||
|  | smssender.provider = twilio | ||||||
|  | smssender.twilio.sid = *********************** | ||||||
|  | smssender.twilio.token = ********************** | ||||||
|  | smssender.twilio.phonenumber = +18888888888 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ### `owsec` Messaging Configuration | ||||||
|  | `owsec` nay require to send e-mails. In order to do so, you must configure an email sender. We have run tests  | ||||||
|  | with GMail and AWS SES. For each, you must obtain the proper credentials and insert them in this configuration as well | ||||||
|  | as the proper mail host. | ||||||
|  |  | ||||||
|  | ```asm | ||||||
|  | mailer.hostname = smtp.gmail.com | ||||||
|  | mailer.username = ************************ | ||||||
|  | mailer.password = ************************ | ||||||
|  | mailer.sender = OpenWIFI | ||||||
|  | mailer.loginmethod = login | ||||||
|  | mailer.port = 587 | ||||||
|  | mailer.templates = $OWSEC_ROOT/templates | ||||||
|  | ``` | ||||||
| @@ -11,7 +11,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWSEC_CONFIG"/owsec.properties ]]; t | |||||||
|   RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWSEC_ROOT/certs/restapi-cert.pem"} \ |   RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWSEC_ROOT/certs/restapi-cert.pem"} \ | ||||||
|   RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\$OWSEC_ROOT/certs/restapi-key.pem"} \ |   RESTAPI_HOST_KEY=${RESTAPI_HOST_KEY:-"\$OWSEC_ROOT/certs/restapi-key.pem"} \ | ||||||
|   RESTAPI_HOST_KEY_PASSWORD=${RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \ |   RESTAPI_HOST_KEY_PASSWORD=${RESTAPI_HOST_KEY_PASSWORD:-"mypassword"} \ | ||||||
|   RESTAPI_WWWASSETS=${RESTAPI_WWWASSETS:-"\$OWSEC_ROOT/wwwassets"} \ |   RESTAPI_WWWASSETS=${RESTAPI_WWWASSETS:-"\$OWSEC_ROOT/persist/wwwassets"} \ | ||||||
|   INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\$OWSEC_ROOT/certs/restapi-ca.pem"} \ |   INTERNAL_RESTAPI_HOST_ROOTCA=${INTERNAL_RESTAPI_HOST_ROOTCA:-"\$OWSEC_ROOT/certs/restapi-ca.pem"} \ | ||||||
|   INTERNAL_RESTAPI_HOST_PORT=${INTERNAL_RESTAPI_HOST_PORT:-"17001"} \ |   INTERNAL_RESTAPI_HOST_PORT=${INTERNAL_RESTAPI_HOST_PORT:-"17001"} \ | ||||||
|   INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\$OWSEC_ROOT/certs/restapi-cert.pem"} \ |   INTERNAL_RESTAPI_HOST_CERT=${INTERNAL_RESTAPI_HOST_CERT:-"\$OWSEC_ROOT/certs/restapi-cert.pem"} \ | ||||||
| @@ -30,8 +30,11 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWSEC_CONFIG"/owsec.properties ]]; t | |||||||
|   MAILER_PASSWORD=${MAILER_PASSWORD:-"************************"} \ |   MAILER_PASSWORD=${MAILER_PASSWORD:-"************************"} \ | ||||||
|   MAILER_SENDER=${MAILER_SENDER:-"OpenWIFI"} \ |   MAILER_SENDER=${MAILER_SENDER:-"OpenWIFI"} \ | ||||||
|   MAILER_PORT=${MAILER_PORT:-"587"} \ |   MAILER_PORT=${MAILER_PORT:-"587"} \ | ||||||
|  |   MAILER_TEMPLATES=${MAILER_TEMPLATES:-"\$OWSEC_ROOT/persist/templates"} \ | ||||||
|   KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \ |   KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \ | ||||||
|   KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \ |   KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \ | ||||||
|  |   DOCUMENT_POLICY_ACCESS=${DOCUMENT_POLICY_ACCESS:-"\$OWSEC_ROOT/persist/wwwassets/access_policy.html"} \ | ||||||
|  |   DOCUMENT_POLICY_PASSWORD=${DOCUMENT_POLICY_PASSWORD:-"\$OWSEC_ROOT/persist/wwwassets/password_policy.html"} \ | ||||||
|   STORAGE_TYPE=${STORAGE_TYPE:-"sqlite"} \ |   STORAGE_TYPE=${STORAGE_TYPE:-"sqlite"} \ | ||||||
|   STORAGE_TYPE_POSTGRESQL_HOST=${STORAGE_TYPE_POSTGRESQL_HOST:-"localhost"} \ |   STORAGE_TYPE_POSTGRESQL_HOST=${STORAGE_TYPE_POSTGRESQL_HOST:-"localhost"} \ | ||||||
|   STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"owsec"} \ |   STORAGE_TYPE_POSTGRESQL_USERNAME=${STORAGE_TYPE_POSTGRESQL_USERNAME:-"owsec"} \ | ||||||
| @@ -43,7 +46,25 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWSEC_CONFIG"/owsec.properties ]]; t | |||||||
|   STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owsec"} \ |   STORAGE_TYPE_MYSQL_PASSWORD=${STORAGE_TYPE_MYSQL_PASSWORD:-"owsec"} \ | ||||||
|   STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owsec"} \ |   STORAGE_TYPE_MYSQL_DATABASE=${STORAGE_TYPE_MYSQL_DATABASE:-"owsec"} \ | ||||||
|   STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \ |   STORAGE_TYPE_MYSQL_PORT=${STORAGE_TYPE_MYSQL_PORT:-"3306"} \ | ||||||
|   envsubst < $OWSEC_CONFIG/owsec.properties.tmpl > $OWSEC_CONFIG/owsec.properties |   envsubst < /owsec.properties.tmpl > $OWSEC_CONFIG/owsec.properties | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Check if wwwassets directory exists | ||||||
|  | export RESTAPI_WWWASSETS=$(grep 'openwifi.restapi.wwwassets' $OWSEC_CONFIG/owsec.properties | awk -F '=' '{print $2}' | xargs | envsubst) | ||||||
|  | if [[ ! -d "$(dirname $RESTAPI_WWWASSETS)" ]]; then | ||||||
|  |   mkdir -p $(dirname $RESTAPI_WWWASSETS) | ||||||
|  | fi | ||||||
|  | if [[ ! -d "$RESTAPI_WWWASSETS" ]]; then | ||||||
|  |   cp -r /dist/wwwassets $RESTAPI_WWWASSETS | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Check if templates directory exists | ||||||
|  | export MAILER_TEMPLATES=$(grep 'mailer.templates' $OWSEC_CONFIG/owsec.properties | awk -F '=' '{print $2}' | xargs | envsubst) | ||||||
|  | if [[ ! -d "$(dirname $MAILER_TEMPLATES)" ]]; then | ||||||
|  |   mkdir -p $(dirname $MAILER_TEMPLATES) | ||||||
|  | fi | ||||||
|  | if [[ ! -d "$MAILER_TEMPLATES" ]]; then | ||||||
|  |   cp -r /dist/templates $MAILER_TEMPLATES | ||||||
| fi | fi | ||||||
|  |  | ||||||
| if [ "$1" = '/openwifi/owsec' -a "$(id -u)" = '0' ]; then | if [ "$1" = '/openwifi/owsec' -a "$(id -u)" = '0' ]; then | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ Currently this chart is not assembled in charts archives, so [helm-git](https:// | |||||||
| To install the chart with the release name `my-release`: | To install the chart with the release name `my-release`: | ||||||
|  |  | ||||||
| ```bash | ```bash | ||||||
| $ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralsec@helm?ref=main | $ helm install --name my-release git+https://github.com/Telecominfraproject/wlan-cloud-ucentralsec@helm/owsec-0.1.0.tgz?ref=main | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| The command deploys the Security on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. | The command deploys the Security on the Kubernetes cluster in the default configuration. The [configuration](#configuration) section lists the parameters that can be configured during installation. | ||||||
|   | |||||||
| @@ -8,7 +8,7 @@ fullnameOverride: "" | |||||||
| images: | images: | ||||||
|   owsec: |   owsec: | ||||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec |     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec | ||||||
|     tag: main |     tag: v2.3.0-RC2 | ||||||
|     pullPolicy: Always |     pullPolicy: Always | ||||||
| #    regcred: | #    regcred: | ||||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||||
| @@ -35,9 +35,10 @@ checks: | |||||||
|         path: / |         path: / | ||||||
|         port: 16101 |         port: 16101 | ||||||
|     readiness: |     readiness: | ||||||
|       httpGet: |       exec: | ||||||
|         path: / |         command: | ||||||
|         port: 16101 |           - /readiness_check | ||||||
|  |       failureThreshold: 1 | ||||||
|  |  | ||||||
| ingresses: | ingresses: | ||||||
|   restapi: |   restapi: | ||||||
| @@ -106,8 +107,14 @@ persistence: | |||||||
| public_env_variables: | public_env_variables: | ||||||
|   OWSEC_ROOT: /owsec-data |   OWSEC_ROOT: /owsec-data | ||||||
|   OWSEC_CONFIG: /owsec-data |   OWSEC_CONFIG: /owsec-data | ||||||
|  |   # Environment variables required for the readiness checks using script | ||||||
|  |   FLAGS: "-s --connect-timeout 3" | ||||||
|  |   # NOTE in order for readiness check to use system info you need to set READINESS_METHOD to "systeminfo" and set OWSEC to the OWSEC's REST API endpoint | ||||||
|  |   #READINESS_METHOD: systeminfo | ||||||
|  |  | ||||||
| secret_env_variables: {} | secret_env_variables: | ||||||
|  |   OWSEC_USERNAME: tip@ucentral.com | ||||||
|  |   OWSEC_PASSWORD: openwifi | ||||||
|  |  | ||||||
| configProperties: | configProperties: | ||||||
|   # -> Public part |   # -> Public part | ||||||
| @@ -119,7 +126,7 @@ configProperties: | |||||||
|   openwifi.restapi.host.0.port: 16001 |   openwifi.restapi.host.0.port: 16001 | ||||||
|   openwifi.restapi.host.0.cert: $OWSEC_ROOT/certs/restapi-cert.pem |   openwifi.restapi.host.0.cert: $OWSEC_ROOT/certs/restapi-cert.pem | ||||||
|   openwifi.restapi.host.0.key: $OWSEC_ROOT/certs/restapi-key.pem |   openwifi.restapi.host.0.key: $OWSEC_ROOT/certs/restapi-key.pem | ||||||
|   openwifi.restapi.wwwassets: $OWSEC_ROOT/wwwassets |   openwifi.restapi.wwwassets: $OWSEC_ROOT/persist/wwwassets | ||||||
|   openwifi.internal.restapi.host.0.backlog: 100 |   openwifi.internal.restapi.host.0.backlog: 100 | ||||||
|   openwifi.internal.restapi.host.0.security: relaxed |   openwifi.internal.restapi.host.0.security: relaxed | ||||||
|   openwifi.internal.restapi.host.0.rootca: $OWSEC_ROOT/certs/restapi-ca.pem |   openwifi.internal.restapi.host.0.rootca: $OWSEC_ROOT/certs/restapi-ca.pem | ||||||
| @@ -136,7 +143,7 @@ configProperties: | |||||||
|   mailer.sender: OpenWIFI |   mailer.sender: OpenWIFI | ||||||
|   mailer.loginmethod: login |   mailer.loginmethod: login | ||||||
|   mailer.port: 587 |   mailer.port: 587 | ||||||
|   mailer.templates: $OWSEC_ROOT/templates |   mailer.templates: $OWSEC_ROOT/persist/templates | ||||||
|   # ALB |   # ALB | ||||||
|   alb.enable: "true" |   alb.enable: "true" | ||||||
|   alb.port: 16101 |   alb.port: 16101 | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| openapi: 3.0.1 | openapi: 3.0.1 | ||||||
| info: | info: | ||||||
|   title: uCentral Security API |   title: uCentral Security API | ||||||
|   description: A process to manage security logins |   description: A process to manage security logins. | ||||||
|   version: 2.0.0 |   version: 2.0.0 | ||||||
|   license: |   license: | ||||||
|     name: BSD3 |     name: BSD3 | ||||||
| @@ -196,6 +196,38 @@ components: | |||||||
|           items: |           items: | ||||||
|             $ref: '#/components/schemas/SystemEndpoint' |             $ref: '#/components/schemas/SystemEndpoint' | ||||||
|  |  | ||||||
|  |     MobilePhoneNumber: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         number: | ||||||
|  |           type: string | ||||||
|  |         verified: | ||||||
|  |           type: boolean | ||||||
|  |         primary: | ||||||
|  |           type: boolean | ||||||
|  |  | ||||||
|  |     MfaAuthInfo: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         enabled: | ||||||
|  |           type: boolean | ||||||
|  |         method: | ||||||
|  |           type: string | ||||||
|  |           enum: | ||||||
|  |             - sms | ||||||
|  |             - email | ||||||
|  |             - voice | ||||||
|  |  | ||||||
|  |     UserLoginLoginExtensions: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         mobiles: | ||||||
|  |           type: array | ||||||
|  |           items: | ||||||
|  |             $ref: '#/components/schemas/MobilePhoneNumber' | ||||||
|  |         mfa: | ||||||
|  |           $ref: '#/components/schemas/MfaAuthInfo' | ||||||
|  |  | ||||||
|     UserInfo: |     UserInfo: | ||||||
|       type: object |       type: object | ||||||
|       properties: |       properties: | ||||||
| @@ -267,10 +299,12 @@ components: | |||||||
|           enum: |           enum: | ||||||
|             - root |             - root | ||||||
|             - admin |             - admin | ||||||
|             - sub |             - subscriber | ||||||
|             - csr |             - csr | ||||||
|             - system |             - system | ||||||
|             - special |             - installer | ||||||
|  |             - noc | ||||||
|  |             - accounting | ||||||
|         oauthType: |         oauthType: | ||||||
|           type: string |           type: string | ||||||
|           enum: |           enum: | ||||||
| @@ -288,7 +322,7 @@ components: | |||||||
|           type: integer |           type: integer | ||||||
|           format: int64 |           format: int64 | ||||||
|         userTypeProprietaryInfo: |         userTypeProprietaryInfo: | ||||||
|           type: string |           $ref: '#/components/schemas/UserLoginLoginExtensions' | ||||||
|  |  | ||||||
|     UserList: |     UserList: | ||||||
|       type: object |       type: object | ||||||
| @@ -314,6 +348,39 @@ components: | |||||||
|         text: |         text: | ||||||
|           type: string |           type: string | ||||||
|  |  | ||||||
|  |     SMSInfo: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         from: | ||||||
|  |           type: string | ||||||
|  |         to: | ||||||
|  |           type: string | ||||||
|  |         text: | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|  |     MFAChallengeRequest: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         uuid: | ||||||
|  |           type: string | ||||||
|  |           format: uuid | ||||||
|  |         question: | ||||||
|  |           type: string | ||||||
|  |         created: | ||||||
|  |           type: integer | ||||||
|  |           format: integer64 | ||||||
|  |         method: | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|  |     MFAChallengeResponse: | ||||||
|  |       type: object | ||||||
|  |       properties: | ||||||
|  |         uuid: | ||||||
|  |           type: string | ||||||
|  |           format: uuid | ||||||
|  |         answer: | ||||||
|  |           type: string | ||||||
|  |  | ||||||
|     ######################################################################################### |     ######################################################################################### | ||||||
|     ## |     ## | ||||||
|     ## These are endpoints that all services in the uCentral stack must provide |     ## These are endpoints that all services in the uCentral stack must provide | ||||||
| @@ -590,20 +657,34 @@ paths: | |||||||
|           schema: |           schema: | ||||||
|             type: boolean |             type: boolean | ||||||
|           required: false |           required: false | ||||||
|  |         - in: query | ||||||
|  |           name: resendMFACode | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           name: completeMFAChallenge | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |           required: false | ||||||
|       requestBody: |       requestBody: | ||||||
|         description: User id and password |         description: User id and password | ||||||
|         required: true |         required: true | ||||||
|         content: |         content: | ||||||
|           application/json: |           application/json: | ||||||
|             schema: |             schema: | ||||||
|               $ref: '#/components/schemas/WebTokenRequest' |               oneOf: | ||||||
|  |                 - $ref: '#/components/schemas/WebTokenRequest' | ||||||
|  |                 - $ref: '#/components/schemas/MFAChallengeResponse' | ||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
|           description: successful operation |           description: successful operation | ||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
|               schema: |               schema: | ||||||
|                 $ref: '#/components/schemas/WebTokenResult' |                 oneOf: | ||||||
|  |                   - $ref: '#/components/schemas/WebTokenResult' | ||||||
|  |                   - $ref: '#/components/schemas/MFAChallengeRequest' | ||||||
|         403: |         403: | ||||||
|           $ref: '#/components/responses/Unauthorized' |           $ref: '#/components/responses/Unauthorized' | ||||||
|         404: |         404: | ||||||
| @@ -638,7 +719,7 @@ paths: | |||||||
|     get: |     get: | ||||||
|       tags: |       tags: | ||||||
|         - Authentication |         - Authentication | ||||||
|       summary: retrieve the system layout |       summary: Retrieve the system layout. | ||||||
|       operationId: getSystemInfo |       operationId: getSystemInfo | ||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
| @@ -703,7 +784,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - User Management |         - User Management | ||||||
|       operationId: getUser |       operationId: getUser | ||||||
|       summary: Retrieve the information for a single user |       summary: Retrieve the information for a single user. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -723,7 +804,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - User Management |         - User Management | ||||||
|       operationId: deleteUser |       operationId: deleteUser | ||||||
|       summary: Delete s single user |       summary: Delete a single user. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -743,7 +824,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - User Management |         - User Management | ||||||
|       operationId: createUser |       operationId: createUser | ||||||
|       summary: Create a single user |       summary: Create a single user. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -775,7 +856,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - User Management |         - User Management | ||||||
|       operationId: updateUser |       operationId: updateUser | ||||||
|       summary: Modifying a single user |       summary: Modify a single user. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -807,7 +888,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - Avatar |         - Avatar | ||||||
|       operationId: getAvatar |       operationId: getAvatar | ||||||
|       summary: Retrieve teh avatar associated with a user ID |       summary: Retrieve the avatar associated with a user ID. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -841,7 +922,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - Avatar |         - Avatar | ||||||
|       operationId: deleteAvatar |       operationId: deleteAvatar | ||||||
|       summary: Remove an Avatar associated with a user ID |       summary: Remove an avatar associated with a user ID. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -861,7 +942,7 @@ paths: | |||||||
|       tags: |       tags: | ||||||
|         - Avatar |         - Avatar | ||||||
|       operationId: createAvatar |       operationId: createAvatar | ||||||
|       summary: Create an Avatar associated with a user ID |       summary: Create an avatar associated with a user ID. | ||||||
|       parameters: |       parameters: | ||||||
|         - in: path |         - in: path | ||||||
|           name: id |           name: id | ||||||
| @@ -897,8 +978,8 @@ paths: | |||||||
|   /email: |   /email: | ||||||
|     post: |     post: | ||||||
|       tags: |       tags: | ||||||
|         - EMail |         - Email | ||||||
|       summary: Send test email with the system |       summary: Send test email with the system. | ||||||
|       operationId: Send a test email |       operationId: Send a test email | ||||||
|       requestBody: |       requestBody: | ||||||
|         description: The requested message |         description: The requested message | ||||||
| @@ -925,6 +1006,53 @@ paths: | |||||||
|                     items: |                     items: | ||||||
|                       type: string |                       type: string | ||||||
|  |  | ||||||
|  |   /sms: | ||||||
|  |     post: | ||||||
|  |       tags: | ||||||
|  |         - Email | ||||||
|  |       summary: Send test email with the system. | ||||||
|  |       operationId: Send a test SMS | ||||||
|  |       parameters: | ||||||
|  |         - in: query | ||||||
|  |           name: validateNumber | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           name: completeValidation | ||||||
|  |           schema: | ||||||
|  |             type: boolean | ||||||
|  |           required: false | ||||||
|  |         - in: query | ||||||
|  |           name: validationCode | ||||||
|  |           schema: | ||||||
|  |             type: string | ||||||
|  |           required: false | ||||||
|  |       requestBody: | ||||||
|  |         description: The requested message | ||||||
|  |         content: | ||||||
|  |           application/json: | ||||||
|  |             schema: | ||||||
|  |               $ref: '#/components/schemas/SMSInfo' | ||||||
|  |       responses: | ||||||
|  |         200: | ||||||
|  |           $ref: '#/components/responses/Success' | ||||||
|  |         403: | ||||||
|  |           $ref: '#/components/responses/Unauthorized' | ||||||
|  |         404: | ||||||
|  |           $ref: '#/components/responses/NotFound' | ||||||
|  |         500: | ||||||
|  |           description: Error description | ||||||
|  |           content: | ||||||
|  |             application/json: | ||||||
|  |               schema: | ||||||
|  |                 type: object | ||||||
|  |                 properties: | ||||||
|  |                   errors: | ||||||
|  |                     type: array | ||||||
|  |                     items: | ||||||
|  |                       type: string | ||||||
|  |  | ||||||
|   ######################################################################################### |   ######################################################################################### | ||||||
|   ## |   ## | ||||||
|   ## These are endpoints that all services in the uCentral stack must provide |   ## These are endpoints that all services in the uCentral stack must provide | ||||||
| @@ -934,7 +1062,7 @@ paths: | |||||||
|     get: |     get: | ||||||
|       tags: |       tags: | ||||||
|         - Security |         - Security | ||||||
|       summary: Retrieve the list of security profiles for a specific service type |       summary: Retrieve the list of security profiles for a specific service type. | ||||||
|       operationId: getSecurituProfiles |       operationId: getSecurituProfiles | ||||||
|       parameters: |       parameters: | ||||||
|         - in: query |         - in: query | ||||||
| @@ -1004,7 +1132,7 @@ paths: | |||||||
|     post: |     post: | ||||||
|       tags: |       tags: | ||||||
|         - System Commands |         - System Commands | ||||||
|       summary: Perform some systeme wide commands |       summary: Perform some system wide commands. | ||||||
|       operationId: systemCommand |       operationId: systemCommand | ||||||
|       requestBody: |       requestBody: | ||||||
|         description: Command details |         description: Command details | ||||||
| @@ -1019,7 +1147,7 @@ paths: | |||||||
|                 - $ref: '#/components/schemas/SystemCommandGetSubsystemNames' |                 - $ref: '#/components/schemas/SystemCommandGetSubsystemNames' | ||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
|           description: Successfull command execution |           description: Successful command execution | ||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
|               schema: |               schema: | ||||||
| @@ -1048,7 +1176,7 @@ paths: | |||||||
|  |  | ||||||
|       responses: |       responses: | ||||||
|         200: |         200: | ||||||
|           description: Successfull command execution |           description: Successful command execution | ||||||
|           content: |           content: | ||||||
|             application/json: |             application/json: | ||||||
|               schema: |               schema: | ||||||
|   | |||||||
| @@ -40,6 +40,16 @@ openwifi.system.commandchannel = /tmp/app.ucentralsec | |||||||
| openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem | openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem | ||||||
| openwifi.service.key.password = mypassword | openwifi.service.key.password = mypassword | ||||||
|  |  | ||||||
|  | smssender.provider = aws | ||||||
|  | smssender.aws.secretkey = *************************************** | ||||||
|  | smssender.aws.accesskey = *************************************** | ||||||
|  | smssender.aws.region = ************** | ||||||
|  |  | ||||||
|  | #smssender.provider = twilio | ||||||
|  | #smssender.twilio.sid = *********************** | ||||||
|  | #smssender.twilio.token = ********************** | ||||||
|  | #smssender.twilio.phonenumber = +18888888888 | ||||||
|  |  | ||||||
| # | # | ||||||
| # Security Microservice Specific Section | # Security Microservice Specific Section | ||||||
| # | # | ||||||
|   | |||||||
| @@ -49,7 +49,7 @@ mailer.password = ${MAILER_PASSWORD} | |||||||
| mailer.sender = ${MAILER_SENDER} | mailer.sender = ${MAILER_SENDER} | ||||||
| mailer.loginmethod = login | mailer.loginmethod = login | ||||||
| mailer.port = ${MAILER_PORT} | mailer.port = ${MAILER_PORT} | ||||||
| mailer.templates = $UCENTRALSEC_ROOT/templates | mailer.templates = ${MAILER_TEMPLATES} | ||||||
|  |  | ||||||
|  |  | ||||||
| ############################# | ############################# | ||||||
| @@ -71,8 +71,8 @@ openwifi.kafka.brokerlist = ${KAFKA_BROKERLIST} | |||||||
| openwifi.kafka.auto.commit = false | openwifi.kafka.auto.commit = false | ||||||
| openwifi.kafka.queue.buffering.max.ms = 50 | openwifi.kafka.queue.buffering.max.ms = 50 | ||||||
|  |  | ||||||
| openwifi.document.policy.access = /wwwassets/access_policy.html | openwifi.document.policy.access = ${DOCUMENT_POLICY_ACCESS} | ||||||
| openwifi.document.policy.password = /wwwassets/password_policy.html | openwifi.document.policy.password = ${DOCUMENT_POLICY_PASSWORD} | ||||||
| openwifi.avatar.maxsize = 2000000 | openwifi.avatar.maxsize = 2000000 | ||||||
| # | # | ||||||
| # This section select which form of persistence you need | # This section select which form of persistence you need | ||||||
| @@ -118,7 +118,7 @@ logging.channels.c1.formatter = f1 | |||||||
|  |  | ||||||
| # This is where the logs will be written. This path MUST exist | # This is where the logs will be written. This path MUST exist | ||||||
| logging.channels.c2.class = FileChannel | logging.channels.c2.class = FileChannel | ||||||
| logging.channels.c2.path = $UCENTRALSEC_ROOT/logs/log | logging.channels.c2.path = $OWSEC_ROOT/logs/log | ||||||
| logging.channels.c2.formatter.class = PatternFormatter | logging.channels.c2.formatter.class = PatternFormatter | ||||||
| logging.channels.c2.formatter.pattern = %Y-%m-%d %H:%M:%S %s: [%p] %t | logging.channels.c2.formatter.pattern = %Y-%m-%d %H:%M:%S %s: [%p] %t | ||||||
| logging.channels.c2.rotation = 20 M | logging.channels.c2.rotation = 20 M | ||||||
|   | |||||||
							
								
								
									
										58
									
								
								readiness_check
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								readiness_check
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,58 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | set -e | ||||||
|  |  | ||||||
|  | if [[ "$(which jq)" == "" ]] | ||||||
|  | then | ||||||
|  |   echo "You need the package jq installed to use this script." | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | if [[ "$(which curl)" == "" ]] | ||||||
|  | then | ||||||
|  |   echo "You need the package curl installed to use this script." | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | if [[ "${OWSEC_USERNAME}" == "" ]] | ||||||
|  | then | ||||||
|  |   echo "You must set the variable OWSEC_USERNAME in order to use this script. Something like" | ||||||
|  |   echo "OWSEC_USERNAME=tip@ucentral.com" | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | if [[ "${OWSEC_PASSWORD}" == "" ]] | ||||||
|  | then | ||||||
|  |   echo "You must set the variable OWSEC_PASSWORD in order to use this script. Something like" | ||||||
|  |   echo "OWSEC_PASSWORD=openwifi" | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | if [[ "${READINESS_METHOD}" == "systeminfo" ]] | ||||||
|  | then | ||||||
|  |   export RESTAPI_PORT=$(grep 'openwifi.restapi.host.0.port' $OWSEC_CONFIG/owsec.properties | awk -F '=' '{print $2}' | xargs | envsubst) | ||||||
|  |   # Get OAuth token from OWSEC and cache it or use cached one | ||||||
|  |   payload="{ \"userId\" : \"$OWSEC_USERNAME\" , \"password\" : \"$OWSEC_PASSWORD\" }" | ||||||
|  |   if [[ -f "/tmp/token" ]] | ||||||
|  |   then | ||||||
|  |     token=$(cat /tmp/token) | ||||||
|  |   else | ||||||
|  |     token=$(curl ${FLAGS} -k -X POST -H "Content-Type: application/json" -d "$payload" "https://localhost:$RESTAPI_PORT/api/v1/oauth2" | jq -r '.access_token') | ||||||
|  |   fi | ||||||
|  |   if [[ "${token}" == "" ]] | ||||||
|  |   then | ||||||
|  |     echo "Could not login. Please verify the host and username/password." | ||||||
|  |     exit 13 | ||||||
|  |   fi | ||||||
|  |   echo -n $token > /tmp/token | ||||||
|  |  | ||||||
|  |   # Make systeminfo request to the local owsec instance | ||||||
|  |   curl ${FLAGS} -k -X GET "https://localhost:$RESTAPI_PORT/api/v1/system?command=info" \ | ||||||
|  |     -H "accept: application/json" \ | ||||||
|  |     -H "Authorization: Bearer ${token}" > /tmp/result.json | ||||||
|  |   exit_code=$? | ||||||
|  |   jq < /tmp/result.json | ||||||
|  |   exit $exit_code | ||||||
|  | else | ||||||
|  |   export ALB_PORT=$(grep 'alb.port' $OWSEC_CONFIG/owsec.properties | awk -F '=' '{print $2}' | xargs | envsubst) | ||||||
|  |   curl localhost:$ALB_PORT | ||||||
|  | fi | ||||||
| @@ -1,118 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_ALBHEALTHCHECKSERVER_H |  | ||||||
| #define UCENTRALGW_ALBHEALTHCHECKSERVER_H |  | ||||||
|  |  | ||||||
| #include <memory> |  | ||||||
| #include <iostream> |  | ||||||
| #include <fstream> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| #include "Poco/Thread.h" |  | ||||||
| #include "Poco/Net/HTTPServer.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Net/HTTPServerResponse.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandler.h" |  | ||||||
| #include "Poco/Logger.h" |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	class ALBRequestHandler: public Poco::Net::HTTPRequestHandler |  | ||||||
| 			/// Return a HTML document with the current date and time. |  | ||||||
| 		{ |  | ||||||
| 		  public: |  | ||||||
| 			explicit ALBRequestHandler(Poco::Logger & L) |  | ||||||
| 				: Logger_(L) |  | ||||||
| 			{ |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			void handleRequest(Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override |  | ||||||
| 			{ |  | ||||||
| 				Logger_.information(Poco::format("ALB-REQUEST(%s): New ALB request.",Request.clientAddress().toString())); |  | ||||||
| 				Response.setChunkedTransferEncoding(true); |  | ||||||
| 				Response.setContentType("text/html"); |  | ||||||
| 				Response.setDate(Poco::Timestamp()); |  | ||||||
| 				Response.setStatus(Poco::Net::HTTPResponse::HTTP_OK); |  | ||||||
| 				Response.setKeepAlive(true); |  | ||||||
| 				Response.set("Connection","keep-alive"); |  | ||||||
| 				Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1); |  | ||||||
| 				std::ostream &Answer = Response.send(); |  | ||||||
| 				Answer << "uCentralGW Alive and kicking!" ; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 	  private: |  | ||||||
| 		Poco::Logger 	& Logger_; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory |  | ||||||
| 		{ |  | ||||||
| 		  public: |  | ||||||
| 			explicit ALBRequestHandlerFactory(Poco::Logger & L): |  | ||||||
| 				Logger_(L) |  | ||||||
| 			{ |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override |  | ||||||
| 			{ |  | ||||||
| 				if (request.getURI() == "/") |  | ||||||
| 					return new ALBRequestHandler(Logger_); |  | ||||||
| 				else |  | ||||||
| 					return nullptr; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		  private: |  | ||||||
| 			Poco::Logger	&Logger_; |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
|     class ALBHealthCheckServer : public SubSystemServer { |  | ||||||
|         public: |  | ||||||
|             ALBHealthCheckServer() noexcept: |  | ||||||
|                     SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb") |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             static ALBHealthCheckServer *instance() { |  | ||||||
|                 if (instance_ == nullptr) { |  | ||||||
|                     instance_ = new ALBHealthCheckServer; |  | ||||||
|                 } |  | ||||||
|                 return instance_; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             int Start() override { |  | ||||||
|                 if(Daemon()->ConfigGetBool("alb.enable",false)) { |  | ||||||
|                     Port_ = (int)Daemon()->ConfigGetInt("alb.port",15015); |  | ||||||
|                     Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_); |  | ||||||
|                     auto Params = new Poco::Net::HTTPServerParams; |  | ||||||
|                     Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger_), *Socket_, Params); |  | ||||||
|                     Server_->start(); |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             void Stop() override { |  | ||||||
|                 if(Server_) |  | ||||||
|                     Server_->stop(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|           private: |  | ||||||
|             static ALBHealthCheckServer *instance_; |  | ||||||
|             std::unique_ptr<Poco::Net::HTTPServer>   	Server_; |  | ||||||
|             std::unique_ptr<Poco::Net::ServerSocket> 	Socket_; |  | ||||||
|             int                                     	Port_ = 0; |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|     inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } |  | ||||||
|     inline class ALBHealthCheckServer * ALBHealthCheckServer::instance_ = nullptr; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_ALBHEALTHCHECKSERVER_H |  | ||||||
							
								
								
									
										47
									
								
								src/APIServers.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/APIServers.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-23. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | #include "RESTAPI/RESTAPI_oauth2Handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_user_handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_users_handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_action_links.h" | ||||||
|  | #include "RESTAPI/RESTAPI_systemEndpoints_handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_AssetServer.h" | ||||||
|  | #include "RESTAPI/RESTAPI_avatarHandler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_email_handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_sms_handler.h" | ||||||
|  | #include "RESTAPI/RESTAPI_validateToken_handler.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |  | ||||||
|  |     Poco::Net::HTTPRequestHandler * RESTAPI_external_server(const char *Path, RESTAPIHandler::BindingMap &Bindings, | ||||||
|  |                                                             Poco::Logger & L, RESTAPI_GenericServer & S) { | ||||||
|  |         return RESTAPI_Router< | ||||||
|  |             RESTAPI_oauth2Handler, | ||||||
|  |             RESTAPI_users_handler, | ||||||
|  |             RESTAPI_user_handler, | ||||||
|  |             RESTAPI_system_command, | ||||||
|  |             RESTAPI_AssetServer, | ||||||
|  |             RESTAPI_systemEndpoints_handler, | ||||||
|  |             RESTAPI_action_links, | ||||||
|  |             RESTAPI_avatarHandler, | ||||||
|  |             RESTAPI_email_handler, | ||||||
|  |             RESTAPI_sms_handler | ||||||
|  |         >(Path, Bindings, L, S); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     Poco::Net::HTTPRequestHandler * RESTAPI_internal_server(const char *Path, RESTAPIHandler::BindingMap &Bindings, | ||||||
|  |                                                             Poco::Logger & L, RESTAPI_GenericServer & S) { | ||||||
|  |         return RESTAPI_Router_I< | ||||||
|  |             RESTAPI_users_handler, | ||||||
|  |             RESTAPI_user_handler, | ||||||
|  |             RESTAPI_system_command, | ||||||
|  |             RESTAPI_action_links, | ||||||
|  |             RESTAPI_validateToken_handler, | ||||||
|  |             RESTAPI_sms_handler | ||||||
|  |         >(Path, Bindings, L, S); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,93 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include <utility> |  | ||||||
|  |  | ||||||
| #include "AuthClient.h" |  | ||||||
| #include "RESTAPI_SecurityObjects.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "OpenAPIRequest.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| 	class AuthClient * AuthClient::instance_ = nullptr; |  | ||||||
|  |  | ||||||
| 	int AuthClient::Start() { |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void AuthClient::Stop() { |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void AuthClient::RemovedCachedToken(const std::string &Token) { |  | ||||||
| 		std::lock_guard	G(Mutex_); |  | ||||||
| 		UserCache_.erase(Token); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool IsTokenExpired(const SecurityObjects::WebToken &T) { |  | ||||||
| 		return ((T.expires_in_+T.created_)<std::time(nullptr)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool AuthClient::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) { |  | ||||||
| 		std::lock_guard G(Mutex_); |  | ||||||
|  |  | ||||||
| 		auto User = UserCache_.find(SessionToken); |  | ||||||
| 		if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { |  | ||||||
| 			UInfo = User->second; |  | ||||||
| 			return true; |  | ||||||
| 		} else { |  | ||||||
| 			Types::StringPairVec QueryData; |  | ||||||
| 			QueryData.push_back(std::make_pair("token",SessionToken)); |  | ||||||
| 			OpenAPIRequestGet	Req(    uSERVICE_SECURITY, |  | ||||||
| 								  	"/api/v1/validateToken", |  | ||||||
| 									 QueryData, |  | ||||||
| 								  5000); |  | ||||||
| 			Poco::JSON::Object::Ptr Response; |  | ||||||
| 			if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { |  | ||||||
| 				if(Response->has("tokenInfo") && Response->has("userInfo")) { |  | ||||||
| 					SecurityObjects::UserInfoAndPolicy	P; |  | ||||||
| 					P.from_json(Response); |  | ||||||
| 					UserCache_[SessionToken] = P; |  | ||||||
| 					UInfo = P; |  | ||||||
| 				} |  | ||||||
| 				return true; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool AuthClient::IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) { |  | ||||||
| 		std::lock_guard G(Mutex_); |  | ||||||
|  |  | ||||||
| 		auto User = UserCache_.find(SessionToken); |  | ||||||
| 		if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { |  | ||||||
| 			UInfo = User->second; |  | ||||||
| 			return true; |  | ||||||
| 		} else { |  | ||||||
| 			Types::StringPairVec QueryData; |  | ||||||
| 			QueryData.push_back(std::make_pair("token",SessionToken)); |  | ||||||
| 			OpenAPIRequestGet	Req(uSERVICE_SECURITY, |  | ||||||
| 									 "/api/v1/validateToken", |  | ||||||
| 									 QueryData, |  | ||||||
| 									 5000); |  | ||||||
| 			Poco::JSON::Object::Ptr Response; |  | ||||||
| 			if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { |  | ||||||
| 				if(Response->has("tokenInfo") && Response->has("userInfo")) { |  | ||||||
| 					SecurityObjects::UserInfoAndPolicy	P; |  | ||||||
| 					P.from_json(Response); |  | ||||||
| 					UserCache_[SessionToken] = P; |  | ||||||
| 					UInfo = P; |  | ||||||
| 				} |  | ||||||
| 				return true; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,45 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-06-30. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_AUTHCLIENT_H |  | ||||||
| #define UCENTRALGW_AUTHCLIENT_H |  | ||||||
|  |  | ||||||
| #include "Poco/JSON/Object.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Net/HTTPServerResponse.h" |  | ||||||
| #include "Poco/JWT/Signer.h" |  | ||||||
| #include "Poco/SHA2Engine.h" |  | ||||||
| #include "RESTAPI_SecurityObjects.h" |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| class AuthClient : public SubSystemServer { |  | ||||||
| 	  public: |  | ||||||
| 		explicit AuthClient() noexcept: |  | ||||||
| 			SubSystemServer("Authentication", "AUTH-CLNT", "authentication") |  | ||||||
| 		{ |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		static AuthClient *instance() { |  | ||||||
| 			if (instance_ == nullptr) { |  | ||||||
| 				instance_ = new AuthClient; |  | ||||||
| 			} |  | ||||||
| 			return instance_; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		int Start() override; |  | ||||||
| 		void Stop() override; |  | ||||||
| 		bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, OpenWifi::SecurityObjects::UserInfoAndPolicy & UInfo ); |  | ||||||
| 		void RemovedCachedToken(const std::string &Token); |  | ||||||
| 		bool IsTokenAuthorized(const std::string &Token, SecurityObjects::UserInfoAndPolicy & UInfo); |  | ||||||
| 	  private: |  | ||||||
| 		static AuthClient 					*instance_; |  | ||||||
| 		OpenWifi::SecurityObjects::UserInfoCache 		UserCache_; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	inline AuthClient * AuthClient() { return AuthClient::instance(); } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_AUTHCLIENT_H |  | ||||||
| @@ -12,15 +12,13 @@ | |||||||
| #include "Poco/JWT/Token.h" | #include "Poco/JWT/Token.h" | ||||||
| #include "Poco/JWT/Signer.h" | #include "Poco/JWT/Signer.h" | ||||||
|  |  | ||||||
| #include "Daemon.h" | #include "framework/MicroService.h" | ||||||
| #include "RESTAPI_handler.h" |  | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "AuthService.h" | #include "AuthService.h" | ||||||
| #include "Utils.h" | #include "framework/KafkaTopics.h" | ||||||
| #include "KafkaManager.h" |  | ||||||
| #include "Kafka_topics.h" |  | ||||||
|  |  | ||||||
| #include "SMTPMailerService.h" | #include "SMTPMailerService.h" | ||||||
|  | #include "MFAServer.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class AuthService *AuthService::instance_ = nullptr; |     class AuthService *AuthService::instance_ = nullptr; | ||||||
| @@ -45,16 +43,16 @@ namespace OpenWifi { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
|     int AuthService::Start() { |     int AuthService::Start() { | ||||||
| 		Signer_.setRSAKey(Daemon()->Key()); | 		Signer_.setRSAKey(MicroService::instance().Key()); | ||||||
| 		Signer_.addAllAlgorithms(); | 		Signer_.addAllAlgorithms(); | ||||||
| 		Logger_.notice("Starting..."); | 		Logger_.notice("Starting..."); | ||||||
|         Secure_ = Daemon()->ConfigGetBool("authentication.enabled",true); |         Secure_ = MicroService::instance().ConfigGetBool("authentication.enabled",true); | ||||||
|         DefaultPassword_ = Daemon()->ConfigGetString("authentication.default.password",""); |         DefaultPassword_ = MicroService::instance().ConfigGetString("authentication.default.password",""); | ||||||
|         DefaultUserName_ = Daemon()->ConfigGetString("authentication.default.username",""); |         DefaultUserName_ = MicroService::instance().ConfigGetString("authentication.default.username",""); | ||||||
|         Mechanism_ = Daemon()->ConfigGetString("authentication.service.type","internal"); |         Mechanism_ = MicroService::instance().ConfigGetString("authentication.service.type","internal"); | ||||||
|         PasswordValidation_ = PasswordValidationStr_ = Daemon()->ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"); |         PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"); | ||||||
|         TokenAging_ = (uint64_t) Daemon()->ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); |         TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); | ||||||
|         HowManyOldPassword_ = Daemon()->ConfigGetInt("authentication.oldpasswords", 5); |         HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5); | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -81,7 +79,7 @@ namespace OpenWifi { | |||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if(!CallToken.empty()) { | 		if(!CallToken.empty()) { | ||||||
| 		    if(Storage()->IsTokenRevoked(CallToken)) | 		    if(StorageService()->IsTokenRevoked(CallToken)) | ||||||
| 		        return false; | 		        return false; | ||||||
| 		    auto Client = UserCache_.find(CallToken); | 		    auto Client = UserCache_.find(CallToken); | ||||||
| 		    if( Client == UserCache_.end() ) | 		    if( Client == UserCache_.end() ) | ||||||
| @@ -93,7 +91,7 @@ namespace OpenWifi { | |||||||
| 		        return true; | 		        return true; | ||||||
| 		    } | 		    } | ||||||
| 		    UserCache_.erase(CallToken); | 		    UserCache_.erase(CallToken); | ||||||
| 		    Storage()->RevokeToken(CallToken); | 		    StorageService()->RevokeToken(CallToken); | ||||||
| 		    return false; | 		    return false; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -105,7 +103,7 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|         for(auto i=UserCache_.begin();i!=UserCache_.end();) { |         for(auto i=UserCache_.begin();i!=UserCache_.end();) { | ||||||
|             if (i->second.userinfo.email==UserName) { |             if (i->second.userinfo.email==UserName) { | ||||||
|                 Logout(i->first); |                 Logout(i->first, false); | ||||||
|                 i = UserCache_.erase(i); |                 i = UserCache_.erase(i); | ||||||
|             } else { |             } else { | ||||||
|                 ++i; |                 ++i; | ||||||
| @@ -114,32 +112,43 @@ namespace OpenWifi { | |||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     bool AuthService::RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo) { | ||||||
|  |         return (UInfo.userinfo.userTypeProprietaryInfo.mfa.enabled && MFAServer().MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     bool AuthService::ValidatePassword(const std::string &Password) { |     bool AuthService::ValidatePassword(const std::string &Password) { | ||||||
|         return std::regex_match(Password, PasswordValidation_); |         return std::regex_match(Password, PasswordValidation_); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void AuthService::Logout(const std::string &token) { |     void AuthService::Logout(const std::string &token, bool EraseFromCache) { | ||||||
| 		std::lock_guard		Guard(Mutex_); | 		std::lock_guard		Guard(Mutex_); | ||||||
|  |  | ||||||
| 		UserCache_.erase(token); | 		if(EraseFromCache) | ||||||
|  | 		    UserCache_.erase(token); | ||||||
|  |  | ||||||
|         try { |         try { | ||||||
|             Poco::JSON::Object Obj; |             Poco::JSON::Object Obj; | ||||||
|             Obj.set("event", "remove-token"); |             Obj.set("event", "remove-token"); | ||||||
|             Obj.set("id", Daemon()->ID()); |             Obj.set("id", MicroService::instance().ID()); | ||||||
|             Obj.set("token", token); |             Obj.set("token", token); | ||||||
|             std::stringstream ResultText; |             std::stringstream ResultText; | ||||||
|             Poco::JSON::Stringifier::stringify(Obj, ResultText); |             Poco::JSON::Stringifier::stringify(Obj, ResultText); | ||||||
|             std::string Tmp{token}; |             std::string Tmp{token}; | ||||||
|             Storage()->RevokeToken(Tmp); |             StorageService()->RevokeToken(Tmp); | ||||||
|             KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, Daemon()->PrivateEndPoint(), ResultText.str(), |             KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(), | ||||||
|                                         false); |                                         false); | ||||||
|         } catch (const Poco::Exception &E) { |         } catch (const Poco::Exception &E) { | ||||||
|             Logger_.log(E); |             Logger_.log(E); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     std::string AuthService::GenerateToken(const std::string & Identity, ACCESS_TYPE Type) { |     [[nodiscard]] std::string AuthService::GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type) { | ||||||
|  |         std::string Identity(UserName + ":" + Poco::format("%d",(int)std::time(nullptr)) + ":" + std::to_string(rand())); | ||||||
|  |         HMAC_.update(Identity); | ||||||
|  |         return Poco::DigestEngine::digestToHex(HMAC_.digest()); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     std::string AuthService::GenerateTokenJWT(const std::string & Identity, ACCESS_TYPE Type) { | ||||||
|         std::lock_guard		Guard(Mutex_); |         std::lock_guard		Guard(Mutex_); | ||||||
|  |  | ||||||
| 		Poco::JWT::Token	T; | 		Poco::JWT::Token	T; | ||||||
| @@ -161,13 +170,12 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
| 	bool AuthService::ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo  ) { | 	bool AuthService::ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo  ) { | ||||||
|         std::lock_guard		Guard(Mutex_); |         std::lock_guard		Guard(Mutex_); | ||||||
| 		Poco::JWT::Token	DecryptedToken; |  | ||||||
|  |  | ||||||
| 		try { | 		try { | ||||||
|             auto E = UserCache_.find(SessionToken); |             auto E = UserCache_.find(SessionToken); | ||||||
|             if(E == UserCache_.end()) { |             if(E == UserCache_.end()) { | ||||||
|                 if(Storage()->GetToken(SessionToken,UInfo)) { |                 if(StorageService()->GetToken(SessionToken,UInfo)) { | ||||||
|                     if(Storage()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { |                     if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { | ||||||
|                         UserCache_[UInfo.webtoken.access_token_] = UInfo; |                         UserCache_[UInfo.webtoken.access_token_] = UInfo; | ||||||
|                         return true; |                         return true; | ||||||
|                     } |                     } | ||||||
| @@ -192,16 +200,16 @@ namespace OpenWifi { | |||||||
|         UInfo.webtoken.expires_in_ = TokenAging_ ; |         UInfo.webtoken.expires_in_ = TokenAging_ ; | ||||||
|         UInfo.webtoken.idle_timeout_ = 5 * 60; |         UInfo.webtoken.idle_timeout_ = 5 * 60; | ||||||
|         UInfo.webtoken.token_type_ = "Bearer"; |         UInfo.webtoken.token_type_ = "Bearer"; | ||||||
|         UInfo.webtoken.access_token_ = GenerateToken(UInfo.userinfo.Id,USERNAME); |         UInfo.webtoken.access_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,USERNAME); | ||||||
|         UInfo.webtoken.id_token_ = GenerateToken(UInfo.userinfo.Id,USERNAME); |         UInfo.webtoken.id_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,USERNAME); | ||||||
|         UInfo.webtoken.refresh_token_ = GenerateToken(UInfo.userinfo.Id,CUSTOM); |         UInfo.webtoken.refresh_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,CUSTOM); | ||||||
|         UInfo.webtoken.created_ = time(nullptr); |         UInfo.webtoken.created_ = time(nullptr); | ||||||
|         UInfo.webtoken.username_ = UserName; |         UInfo.webtoken.username_ = UserName; | ||||||
|         UInfo.webtoken.errorCode = 0; |         UInfo.webtoken.errorCode = 0; | ||||||
|         UInfo.webtoken.userMustChangePassword = false; |         UInfo.webtoken.userMustChangePassword = false; | ||||||
|         UserCache_[UInfo.webtoken.access_token_] = UInfo; |         UserCache_[UInfo.webtoken.access_token_] = UInfo; | ||||||
|         Storage()->SetLastLogin(UInfo.userinfo.Id); |         StorageService()->SetLastLogin(UInfo.userinfo.Id); | ||||||
|         Storage()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_, |         StorageService()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_, | ||||||
|                             UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_, |                             UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_, | ||||||
|                                 UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_); |                                 UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_); | ||||||
|     } |     } | ||||||
| @@ -230,7 +238,7 @@ namespace OpenWifi { | |||||||
|         Poco::toLowerInPlace(UserName); |         Poco::toLowerInPlace(UserName); | ||||||
|         auto PasswordHash = ComputePasswordHash(UserName, Password); |         auto PasswordHash = ComputePasswordHash(UserName, Password); | ||||||
|  |  | ||||||
|         if(Storage()->GetUserByEmail(UserName,UInfo.userinfo)) { |         if(StorageService()->GetUserByEmail(UserName,UInfo.userinfo)) { | ||||||
|             if(UInfo.userinfo.waitingForEmailCheck) { |             if(UInfo.userinfo.waitingForEmailCheck) { | ||||||
|                 return USERNAME_PENDING_VERIFICATION; |                 return USERNAME_PENDING_VERIFICATION; | ||||||
|             } |             } | ||||||
| @@ -255,12 +263,12 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|                 UInfo.userinfo.lastPasswordChange = std::time(nullptr); |                 UInfo.userinfo.lastPasswordChange = std::time(nullptr); | ||||||
|                 UInfo.userinfo.changePassword = false; |                 UInfo.userinfo.changePassword = false; | ||||||
|                 Storage()->UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.Id,UInfo.userinfo); |                 StorageService()->UpdateUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.Id,UInfo.userinfo); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             //  so we have a good password, password up date has taken place if need be, now generate the token. |             //  so we have a good password, password up date has taken place if need be, now generate the token. | ||||||
|             UInfo.userinfo.lastLogin=std::time(nullptr); |             UInfo.userinfo.lastLogin=std::time(nullptr); | ||||||
|             Storage()->SetLastLogin(UInfo.userinfo.Id); |             StorageService()->SetLastLogin(UInfo.userinfo.Id); | ||||||
|             CreateToken(UserName, UInfo ); |             CreateToken(UserName, UInfo ); | ||||||
|             return SUCCESS; |             return SUCCESS; | ||||||
|         } |         } | ||||||
| @@ -272,6 +280,7 @@ namespace OpenWifi { | |||||||
|             UInfo.userinfo.email = DefaultUserName_; |             UInfo.userinfo.email = DefaultUserName_; | ||||||
|             UInfo.userinfo.currentPassword = DefaultPassword_; |             UInfo.userinfo.currentPassword = DefaultPassword_; | ||||||
|             UInfo.userinfo.name = DefaultUserName_; |             UInfo.userinfo.name = DefaultUserName_; | ||||||
|  |             UInfo.userinfo.userRole = SecurityObjects::ROOT; | ||||||
|             CreateToken(UserName, UInfo ); |             CreateToken(UserName, UInfo ); | ||||||
|             return SUCCESS; |             return SUCCESS; | ||||||
|         } |         } | ||||||
| @@ -287,7 +296,7 @@ namespace OpenWifi { | |||||||
|     bool AuthService::SendEmailToUser(std::string &Email, EMAIL_REASON Reason) { |     bool AuthService::SendEmailToUser(std::string &Email, EMAIL_REASON Reason) { | ||||||
|         SecurityObjects::UserInfo   UInfo; |         SecurityObjects::UserInfo   UInfo; | ||||||
|  |  | ||||||
|         if(Storage()->GetUserByEmail(Email,UInfo)) { |         if(StorageService()->GetUserByEmail(Email,UInfo)) { | ||||||
|             switch (Reason) { |             switch (Reason) { | ||||||
|                 case FORGOT_PASSWORD: { |                 case FORGOT_PASSWORD: { | ||||||
|                         MessageAttributes Attrs; |                         MessageAttributes Attrs; | ||||||
| @@ -296,7 +305,7 @@ namespace OpenWifi { | |||||||
|                         Attrs[LOGO] = "logo.jpg"; |                         Attrs[LOGO] = "logo.jpg"; | ||||||
|                         Attrs[SUBJECT] = "Password reset link"; |                         Attrs[SUBJECT] = "Password reset link"; | ||||||
|                         Attrs[ACTION_LINK] = |                         Attrs[ACTION_LINK] = | ||||||
|                                 Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + UInfo.Id ; |                                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + UInfo.Id ; | ||||||
|                         SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); |                         SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); | ||||||
|                     } |                     } | ||||||
|                     break; |                     break; | ||||||
| @@ -308,7 +317,7 @@ namespace OpenWifi { | |||||||
|                         Attrs[LOGO] = "logo.jpg"; |                         Attrs[LOGO] = "logo.jpg"; | ||||||
|                         Attrs[SUBJECT] = "EMail Address Verification"; |                         Attrs[SUBJECT] = "EMail Address Verification"; | ||||||
|                         Attrs[ACTION_LINK] = |                         Attrs[ACTION_LINK] = | ||||||
|                                 Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; |                                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; | ||||||
|                         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); |                         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); | ||||||
|                         UInfo.waitingForEmailCheck = true; |                         UInfo.waitingForEmailCheck = true; | ||||||
|                     } |                     } | ||||||
| @@ -328,7 +337,7 @@ namespace OpenWifi { | |||||||
|         Attrs[LOGO] = "logo.jpg"; |         Attrs[LOGO] = "logo.jpg"; | ||||||
|         Attrs[SUBJECT] = "EMail Address Verification"; |         Attrs[SUBJECT] = "EMail Address Verification"; | ||||||
|         Attrs[ACTION_LINK] = |         Attrs[ACTION_LINK] = | ||||||
|                 Daemon()->GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; |                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; | ||||||
|         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); |         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); | ||||||
|         UInfo.waitingForEmailCheck = true; |         UInfo.waitingForEmailCheck = true; | ||||||
|         return true; |         return true; | ||||||
|   | |||||||
| @@ -11,15 +11,16 @@ | |||||||
|  |  | ||||||
| #include <regex> | #include <regex> | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
|  |  | ||||||
| #include "Poco/JSON/Object.h" | #include "Poco/JSON/Object.h" | ||||||
| #include "Poco/Net/HTTPServerRequest.h" | #include "Poco/Net/HTTPServerRequest.h" | ||||||
| #include "Poco/Net/HTTPServerResponse.h" | #include "Poco/Net/HTTPServerResponse.h" | ||||||
| #include "Poco/JWT/Signer.h" | #include "Poco/JWT/Signer.h" | ||||||
| #include "Poco/SHA2Engine.h" | #include "Poco/SHA2Engine.h" | ||||||
|  | #include "Poco/Crypto/DigestEngine.h" | ||||||
|  | #include "Poco/HMACEngine.h" | ||||||
|  |  | ||||||
| #include "RESTAPI_SecurityObjects.h" | #include "framework/MicroService.h" | ||||||
|  | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
|  |  | ||||||
| namespace OpenWifi{ | namespace OpenWifi{ | ||||||
|  |  | ||||||
| @@ -68,13 +69,14 @@ namespace OpenWifi{ | |||||||
|         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UserInfo  ); |         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UserInfo  ); | ||||||
|         [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo); |         [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo); | ||||||
|         [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;}; |         [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;}; | ||||||
|         void Logout(const std::string &token); |         void Logout(const std::string &token, bool EraseFromCache=true); | ||||||
|  |  | ||||||
|         bool ValidatePassword(const std::string &pwd); |         bool ValidatePassword(const std::string &pwd); | ||||||
|  |  | ||||||
|         [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo); |         [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo); | ||||||
|         [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); |         [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | ||||||
|         [[nodiscard]] std::string GenerateToken(const std::string & UserName, ACCESS_TYPE Type); |         [[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type); | ||||||
|  |         [[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type); | ||||||
|         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::WebToken & UserInfo  ); |         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::WebToken & UserInfo  ); | ||||||
|         [[nodiscard]] std::string ComputePasswordHash(const std::string &UserName, const std::string &Password); |         [[nodiscard]] std::string ComputePasswordHash(const std::string &UserName, const std::string &Password); | ||||||
|         [[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword); |         [[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword); | ||||||
| @@ -83,6 +85,7 @@ namespace OpenWifi{ | |||||||
|         [[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); |         [[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); | ||||||
|         [[nodiscard]] static bool SendEmailToUser(std::string &Email, EMAIL_REASON Reason); |         [[nodiscard]] static bool SendEmailToUser(std::string &Email, EMAIL_REASON Reason); | ||||||
|         [[nodiscard]] bool DeleteUserFromCache(const std::string &UserName); |         [[nodiscard]] bool DeleteUserFromCache(const std::string &UserName); | ||||||
|  |         [[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); | ||||||
|  |  | ||||||
|     private: |     private: | ||||||
| 		static AuthService *instance_; | 		static AuthService *instance_; | ||||||
| @@ -97,6 +100,25 @@ namespace OpenWifi{ | |||||||
| 		std::regex          PasswordValidation_; | 		std::regex          PasswordValidation_; | ||||||
| 		uint64_t            TokenAging_ = 30 * 24 * 60 * 60; | 		uint64_t            TokenAging_ = 30 * 24 * 60 * 60; | ||||||
|         uint64_t            HowManyOldPassword_=5; |         uint64_t            HowManyOldPassword_=5; | ||||||
|  |  | ||||||
|  |         class SHA256Engine : public Poco::Crypto::DigestEngine | ||||||
|  |                 { | ||||||
|  |                 public: | ||||||
|  |                     enum | ||||||
|  |                     { | ||||||
|  |                         BLOCK_SIZE = 64, | ||||||
|  |                         DIGEST_SIZE = 32 | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|  |                     SHA256Engine() | ||||||
|  |                     : DigestEngine("SHA256") | ||||||
|  |                     { | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |         Poco::HMACEngine<SHA256Engine> HMAC_{"tipopenwifi"}; | ||||||
|  |  | ||||||
|         AuthService() noexcept: |         AuthService() noexcept: | ||||||
|             SubSystemServer("Authentication", "AUTH-SVR", "authentication") |             SubSystemServer("Authentication", "AUTH-SVR", "authentication") | ||||||
|         { |         { | ||||||
| @@ -105,6 +127,10 @@ namespace OpenWifi{ | |||||||
|  |  | ||||||
|     inline AuthService * AuthService() { return AuthService::instance(); } |     inline AuthService * AuthService() { return AuthService::instance(); } | ||||||
|  |  | ||||||
|  |     [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) { | ||||||
|  |         return AuthService()->IsAuthorized(Request, SessionToken, UInfo ); | ||||||
|  |     } | ||||||
|  |  | ||||||
| } // end of namespace | } // end of namespace | ||||||
|  |  | ||||||
| #endif //UCENTRAL_UAUTHSERVICE_H | #endif //UCENTRAL_UAUTHSERVICE_H | ||||||
|   | |||||||
| @@ -19,13 +19,17 @@ | |||||||
|  |  | ||||||
| #include "Daemon.h" | #include "Daemon.h" | ||||||
|  |  | ||||||
| #include "ALBHealthCheckServer.h" | #include <aws/core/Aws.h> | ||||||
| #include "KafkaManager.h" | #include <aws/s3/model/CreateBucketRequest.h> | ||||||
|  | #include <aws/s3/model/PutObjectRequest.h> | ||||||
|  | #include <aws/s3/model/AccessControlPolicy.h> | ||||||
|  | #include <aws/s3/model/PutBucketAclRequest.h> | ||||||
|  | #include <aws/s3/model/GetBucketAclRequest.h> | ||||||
|  |  | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "RESTAPI_server.h" |  | ||||||
| #include "SMTPMailerService.h" | #include "SMTPMailerService.h" | ||||||
| #include "RESTAPI_InternalServer.h" |  | ||||||
| #include "AuthService.h" | #include "AuthService.h" | ||||||
|  | #include "SMSSender.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class Daemon *Daemon::instance_ = nullptr; |     class Daemon *Daemon::instance_ = nullptr; | ||||||
| @@ -37,10 +41,9 @@ namespace OpenWifi { | |||||||
|                                    vDAEMON_CONFIG_ENV_VAR, |                                    vDAEMON_CONFIG_ENV_VAR, | ||||||
|                                    vDAEMON_APP_NAME, |                                    vDAEMON_APP_NAME, | ||||||
|                                    vDAEMON_BUS_TIMER, |                                    vDAEMON_BUS_TIMER, | ||||||
|                                    Types::SubSystemVec{ |                                    SubSystemVec{ | ||||||
|                                            Storage(), |                                            StorageService(), | ||||||
|                                            RESTAPI_Server(), |                                            SMSSender(), | ||||||
|                                            RESTAPI_InternalServer(), |  | ||||||
|                                            SMTPMailerService(), |                                            SMTPMailerService(), | ||||||
|                                            AuthService() |                                            AuthService() | ||||||
|                                    }); |                                    }); | ||||||
| @@ -48,21 +51,36 @@ namespace OpenWifi { | |||||||
|         return instance_; |         return instance_; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void Daemon::initialize(Poco::Util::Application &self) { |     void Daemon::initialize() { | ||||||
|         MicroService::initialize(*this); |         AssetDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); | ||||||
|  |         AccessPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); | ||||||
|  |         PasswordPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void MicroServicePostInitialization() { | ||||||
|  |         Daemon()->initialize(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||||
|     try { |     try { | ||||||
|         auto App = OpenWifi::Daemon::instance(); |         SSL_library_init(); | ||||||
|         auto ExitCode =  App->run(argc, argv); |         Aws::SDKOptions AwsOptions; | ||||||
|         delete App; |         AwsOptions.memoryManagementOptions.memoryManager = nullptr; | ||||||
|  |         AwsOptions.cryptoOptions.initAndCleanupOpenSSL = false; | ||||||
|  |         AwsOptions.httpOptions.initAndCleanupCurl = true; | ||||||
|  |  | ||||||
|  |         Aws::InitAPI(AwsOptions); | ||||||
|  |  | ||||||
|  |         int ExitCode=0; | ||||||
|  |         { | ||||||
|  |             auto App = OpenWifi::Daemon::instance(); | ||||||
|  |             ExitCode =  App->run(argc, argv); | ||||||
|  |         } | ||||||
|  |         ShutdownAPI(AwsOptions); | ||||||
|         return ExitCode; |         return ExitCode; | ||||||
|  |  | ||||||
|     } catch (Poco::Exception &exc) { |     } catch (Poco::Exception &exc) { | ||||||
|         std::cerr << exc.displayText() << std::endl; |         std::cout << exc.displayText() << std::endl; | ||||||
|         return Poco::Util::Application::EXIT_SOFTWARE; |         return Poco::Util::Application::EXIT_SOFTWARE; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										17
									
								
								src/Daemon.h
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/Daemon.h
									
									
									
									
									
								
							| @@ -20,9 +20,8 @@ | |||||||
| #include "Poco/Crypto/CipherFactory.h" | #include "Poco/Crypto/CipherFactory.h" | ||||||
| #include "Poco/Crypto/Cipher.h" | #include "Poco/Crypto/Cipher.h" | ||||||
|  |  | ||||||
|  | #include "framework/OpenWifiTypes.h" | ||||||
| #include "OpenWifiTypes.h" | #include "framework/MicroService.h" | ||||||
| #include "MicroService.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
| @@ -39,13 +38,19 @@ namespace OpenWifi { | |||||||
|                         const std::string & ConfigEnv, |                         const std::string & ConfigEnv, | ||||||
|                         const std::string & AppName, |                         const std::string & AppName, | ||||||
|                         uint64_t BusTimer, |                         uint64_t BusTimer, | ||||||
|                         const Types::SubSystemVec & SubSystems) : |                         const SubSystemVec & SubSystems) : | ||||||
|                 MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {}; |                 MicroService( PropFile, RootEnv, ConfigEnv, AppName, BusTimer, SubSystems) {}; | ||||||
|  |  | ||||||
|         void initialize(Poco::Util::Application &self) override; |         void initialize(); | ||||||
|         static Daemon *instance(); |         static Daemon *instance(); | ||||||
|  |         inline const std::string & AssetDir() { return AssetDir_; } | ||||||
|  |         inline const std::string & GetPasswordPolicy() const { return PasswordPolicy_; } | ||||||
|  |         inline const std::string & GetAccessPolicy() const { return AccessPolicy_; } | ||||||
|     private: |     private: | ||||||
|         static Daemon 				*instance_; |         static Daemon 		*instance_; | ||||||
|  |         std::string         AssetDir_; | ||||||
|  |         std::string         PasswordPolicy_; | ||||||
|  |         std::string         AccessPolicy_; | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     inline Daemon * Daemon() { return Daemon::instance(); } |     inline Daemon * Daemon() { return Daemon::instance(); } | ||||||
|   | |||||||
| @@ -1,221 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
| #include <thread> |  | ||||||
|  |  | ||||||
| #include "KafkaManager.h" |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	class KafkaManager *KafkaManager::instance_ = nullptr; |  | ||||||
|  |  | ||||||
| 	KafkaManager::KafkaManager() noexcept: |  | ||||||
| 		SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") |  | ||||||
| 	{ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::initialize(Poco::Util::Application & self) { |  | ||||||
| 		SubSystemServer::initialize(self); |  | ||||||
| 		KafkaEnabled_ = Daemon()->ConfigGetBool("openwifi.kafka.enable",false); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| #ifdef SMALL_BUILD |  | ||||||
|  |  | ||||||
| 	int KafkaManager::Start() { |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
| 	void KafkaManager::Stop() { |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| #else |  | ||||||
|  |  | ||||||
| 	int KafkaManager::Start() { |  | ||||||
| 		if(!KafkaEnabled_) |  | ||||||
| 			return 0; |  | ||||||
| 		ProducerThr_ = std::make_unique<std::thread>([this]() { this->ProducerThr(); }); |  | ||||||
| 		ConsumerThr_ = std::make_unique<std::thread>([this]() { this->ConsumerThr(); }); |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::Stop() { |  | ||||||
| 		if(KafkaEnabled_) { |  | ||||||
| 			ProducerRunning_ = ConsumerRunning_ = false; |  | ||||||
| 			ProducerThr_->join(); |  | ||||||
| 			ConsumerThr_->join(); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::ProducerThr() { |  | ||||||
| 		cppkafka::Configuration Config({ |  | ||||||
| 										   { "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") }, |  | ||||||
| 										   { "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") } |  | ||||||
| 									   }); |  | ||||||
| 		SystemInfoWrapper_ = 	R"lit({ "system" : { "id" : )lit" + |  | ||||||
| 								  	std::to_string(Daemon()->ID()) + |  | ||||||
| 									R"lit( , "host" : ")lit" + Daemon()->PrivateEndPoint() + |  | ||||||
| 									R"lit(" } , "payload" : )lit" ; |  | ||||||
| 		cppkafka::Producer	Producer(Config); |  | ||||||
| 		ProducerRunning_ = true; |  | ||||||
| 		while(ProducerRunning_) { |  | ||||||
| 			std::this_thread::sleep_for(std::chrono::milliseconds(200)); |  | ||||||
| 			try |  | ||||||
| 			{ |  | ||||||
| 				std::lock_guard G(ProducerMutex_); |  | ||||||
| 				auto Num=0; |  | ||||||
| 				while (!Queue_.empty()) { |  | ||||||
| 					const auto M = Queue_.front(); |  | ||||||
| 					Producer.produce( |  | ||||||
| 						cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); |  | ||||||
| 					Queue_.pop(); |  | ||||||
| 					Num++; |  | ||||||
| 				} |  | ||||||
| 				if(Num) |  | ||||||
| 					Producer.flush(); |  | ||||||
| 			} catch (const cppkafka::HandleException &E ) { |  | ||||||
| 				Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); |  | ||||||
| 			} catch (const Poco::Exception &E) { |  | ||||||
| 				Logger_.log(E); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { |  | ||||||
| 		Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); |  | ||||||
| 	} |  | ||||||
| 	void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) { |  | ||||||
| 		Logger_.information(Poco::format("Partition revocation: %Lu...",(uint64_t )partitions.front().get_partition())); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::ConsumerThr() { |  | ||||||
| 		cppkafka::Configuration Config({ |  | ||||||
| 										   { "client.id", Daemon()->ConfigGetString("openwifi.kafka.client.id") }, |  | ||||||
| 										   { "metadata.broker.list", Daemon()->ConfigGetString("openwifi.kafka.brokerlist") }, |  | ||||||
| 										   { "group.id", Daemon()->ConfigGetString("openwifi.kafka.group.id") }, |  | ||||||
| 										   { "enable.auto.commit", Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false) }, |  | ||||||
| 										   { "auto.offset.reset", "latest" } , |  | ||||||
| 										   { "enable.partition.eof", false } |  | ||||||
| 									   }); |  | ||||||
|  |  | ||||||
| 		cppkafka::TopicConfiguration topic_config = { |  | ||||||
| 			{ "auto.offset.reset", "smallest" } |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 		// Now configure it to be the default topic config |  | ||||||
| 		Config.set_default_topic_configuration(topic_config); |  | ||||||
|  |  | ||||||
| 		cppkafka::Consumer Consumer(Config); |  | ||||||
| 		Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) { |  | ||||||
| 			if(!partitions.empty()) { |  | ||||||
| 				Logger_.information(Poco::format("Partition assigned: %Lu...", |  | ||||||
| 												 (uint64_t)partitions.front().get_partition())); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { |  | ||||||
| 			if(!partitions.empty()) { |  | ||||||
| 				Logger_.information(Poco::format("Partition revocation: %Lu...", |  | ||||||
| 												 (uint64_t)partitions.front().get_partition())); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
|         bool AutoCommit = Daemon()->ConfigGetBool("openwifi.kafka.auto.commit",false); |  | ||||||
|         auto BatchSize = Daemon()->ConfigGetInt("openwifi.kafka.consumer.batchsize",20); |  | ||||||
|  |  | ||||||
|         Types::StringVec    Topics; |  | ||||||
| 		for(const auto &i:Notifiers_) |  | ||||||
| 			Topics.push_back(i.first); |  | ||||||
|  |  | ||||||
| 		Consumer.subscribe(Topics); |  | ||||||
|  |  | ||||||
| 		ConsumerRunning_ = true; |  | ||||||
| 		while(ConsumerRunning_) { |  | ||||||
| 			try { |  | ||||||
| 				std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200)); |  | ||||||
| 				for(auto const &Msg:MsgVec) { |  | ||||||
|                     if (!Msg) |  | ||||||
|                         continue; |  | ||||||
|                     if (Msg.get_error()) { |  | ||||||
|                         if (!Msg.is_eof()) { |  | ||||||
|                             Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); |  | ||||||
|                         }if(!AutoCommit) |  | ||||||
|                             Consumer.async_commit(Msg); |  | ||||||
|                         continue; |  | ||||||
|                     } |  | ||||||
|                     std::lock_guard G(ConsumerMutex_); |  | ||||||
|                     auto It = Notifiers_.find(Msg.get_topic()); |  | ||||||
|                     if (It != Notifiers_.end()) { |  | ||||||
|                         Types::TopicNotifyFunctionList &FL = It->second; |  | ||||||
|                         std::string Key{Msg.get_key()}; |  | ||||||
|                         std::string Payload{Msg.get_payload()}; |  | ||||||
|                         for (auto &F : FL) { |  | ||||||
|                             std::thread T(F.first, Key, Payload); |  | ||||||
|                             T.detach(); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     if (!AutoCommit) |  | ||||||
|                         Consumer.async_commit(Msg); |  | ||||||
|                 } |  | ||||||
| 			} catch (const cppkafka::HandleException &E) { |  | ||||||
| 				Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); |  | ||||||
| 			} catch (const Poco::Exception &E) { |  | ||||||
| 				Logger_.log(E); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string KafkaManager::WrapSystemId(const std::string & PayLoad) { |  | ||||||
| 		return std::move( SystemInfoWrapper_ + PayLoad + "}"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) { |  | ||||||
| 		if(KafkaEnabled_) { |  | ||||||
| 			std::lock_guard G(Mutex_); |  | ||||||
| 			KMessage M{ |  | ||||||
| 				.Topic = topic, |  | ||||||
| 				.Key = key, |  | ||||||
| 				.PayLoad = WrapMessage ? WrapSystemId(PayLoad) : PayLoad }; |  | ||||||
| 			Queue_.push(M); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	int KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) { |  | ||||||
| 		if(KafkaEnabled_) { |  | ||||||
| 			std::lock_guard G(Mutex_); |  | ||||||
| 			auto It = Notifiers_.find(Topic); |  | ||||||
| 			if(It == Notifiers_.end()) { |  | ||||||
| 				Types::TopicNotifyFunctionList L; |  | ||||||
| 				L.emplace(L.end(),std::make_pair(F,FunctionId_)); |  | ||||||
| 				Notifiers_[Topic] = std::move(L); |  | ||||||
| 			} else { |  | ||||||
| 				It->second.emplace(It->second.end(),std::make_pair(F,FunctionId_)); |  | ||||||
| 			} |  | ||||||
| 			return FunctionId_++; |  | ||||||
| 		} else { |  | ||||||
| 			return 0; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, int Id) { |  | ||||||
| 		if(KafkaEnabled_) { |  | ||||||
| 			std::lock_guard G(Mutex_); |  | ||||||
| 			auto It = Notifiers_.find(Topic); |  | ||||||
| 			if(It != Notifiers_.end()) { |  | ||||||
| 				Types::TopicNotifyFunctionList & L = It->second; |  | ||||||
| 				for(auto it=L.begin(); it!=L.end(); it++) |  | ||||||
| 					if(it->second == Id) { |  | ||||||
| 						L.erase(it); |  | ||||||
| 						break; |  | ||||||
| 					} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| #endif |  | ||||||
| } // namespace |  | ||||||
| @@ -1,74 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_KAFKAMANAGER_H |  | ||||||
| #define UCENTRALGW_KAFKAMANAGER_H |  | ||||||
|  |  | ||||||
| #include <queue> |  | ||||||
| #include <thread> |  | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
| #include "OpenWifiTypes.h" |  | ||||||
|  |  | ||||||
| #include "cppkafka/cppkafka.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	class KafkaManager : public SubSystemServer { |  | ||||||
| 	  public: |  | ||||||
|  |  | ||||||
| 		struct KMessage { |  | ||||||
| 					std::string Topic, |  | ||||||
| 								Key, |  | ||||||
| 								PayLoad; |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 		void initialize(Poco::Util::Application & self) override; |  | ||||||
| 		static KafkaManager *instance() { |  | ||||||
| 			if(instance_== nullptr) |  | ||||||
| 				instance_ = new KafkaManager; |  | ||||||
| 			return instance_; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		void ProducerThr(); |  | ||||||
| 		void ConsumerThr(); |  | ||||||
|  |  | ||||||
| 		int Start() override; |  | ||||||
| 		void Stop() override; |  | ||||||
|  |  | ||||||
| 		void PostMessage(const std::string &topic, const std::string & key, const std::string &payload, bool WrapMessage = true); |  | ||||||
| 		[[nodiscard]] std::string WrapSystemId(const std::string & PayLoad); |  | ||||||
| 		[[nodiscard]] bool Enabled() { return KafkaEnabled_; } |  | ||||||
| 		int RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction & F); |  | ||||||
| 		void UnregisterTopicWatcher(const std::string &Topic, int FunctionId); |  | ||||||
| 		void WakeUp(); |  | ||||||
| 		void PartitionAssignment(const cppkafka::TopicPartitionList& partitions); |  | ||||||
| 		void PartitionRevocation(const cppkafka::TopicPartitionList& partitions); |  | ||||||
|  |  | ||||||
| 	  private: |  | ||||||
| 		static KafkaManager 			*instance_; |  | ||||||
| 		std::mutex 						ProducerMutex_; |  | ||||||
| 		std::mutex						ConsumerMutex_; |  | ||||||
| 		bool 							KafkaEnabled_ = false; |  | ||||||
| 		std::atomic_bool 				ProducerRunning_ = false; |  | ||||||
| 		std::atomic_bool 				ConsumerRunning_ = false; |  | ||||||
| 		std::queue<KMessage>			Queue_; |  | ||||||
| 		std::string 					SystemInfoWrapper_; |  | ||||||
| 		std::unique_ptr<std::thread>	ConsumerThr_; |  | ||||||
| 		std::unique_ptr<std::thread>	ProducerThr_; |  | ||||||
| 		int                       		FunctionId_=1; |  | ||||||
| 		Types::NotifyTable        		Notifiers_; |  | ||||||
| 		std::unique_ptr<cppkafka::Configuration>    Config_; |  | ||||||
|  |  | ||||||
| 		KafkaManager() noexcept; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	inline KafkaManager * KafkaManager() { return KafkaManager::instance(); } |  | ||||||
| }	// NameSpace |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_KAFKAMANAGER_H |  | ||||||
							
								
								
									
										109
									
								
								src/MFAServer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/MFAServer.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,109 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-11. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "MFAServer.h" | ||||||
|  | #include "SMSSender.h" | ||||||
|  | #include "SMTPMailerService.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |  | ||||||
|  |     class MFAServer * MFAServer::instance_ = nullptr; | ||||||
|  |  | ||||||
|  |     int MFAServer::Start() { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void MFAServer::Stop() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool MFAServer::StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &ChallengeStart) { | ||||||
|  |         std::lock_guard G(Mutex_); | ||||||
|  |  | ||||||
|  |         CleanCache(); | ||||||
|  |  | ||||||
|  |         if(!MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method)) | ||||||
|  |             return false; | ||||||
|  |  | ||||||
|  |         std::string Challenge = MakeChallenge(); | ||||||
|  |         std::string uuid = MicroService::instance().CreateUUID(); | ||||||
|  |         uint64_t Created = std::time(nullptr); | ||||||
|  |  | ||||||
|  |         ChallengeStart.set("uuid",uuid); | ||||||
|  |         ChallengeStart.set("created", Created); | ||||||
|  |         ChallengeStart.set("method", UInfo.userinfo.userTypeProprietaryInfo.mfa.method); | ||||||
|  |  | ||||||
|  |         Cache_[uuid] = MFACacheEntry{ .UInfo = UInfo, .Answer=Challenge, .Created=Created, .Method=UInfo.userinfo.userTypeProprietaryInfo.mfa.method }; | ||||||
|  |         return SendChallenge(UInfo, UInfo.userinfo.userTypeProprietaryInfo.mfa.method, Challenge); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool MFAServer::SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge) { | ||||||
|  |         if(Method=="sms" && SMSSender()->Enabled() && !UInfo.userinfo.userTypeProprietaryInfo.mobiles.empty()) { | ||||||
|  |             std::string Message = "This is your login code: " + Challenge + " Please enter this in your login screen."; | ||||||
|  |             return SMSSender()->Send(UInfo.userinfo.userTypeProprietaryInfo.mobiles[0].number, Message); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(Method=="email" && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) { | ||||||
|  |             MessageAttributes Attrs; | ||||||
|  |             Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; | ||||||
|  |             Attrs[LOGO] = "logo.jpg"; | ||||||
|  |             Attrs[SUBJECT] = "Login validation code"; | ||||||
|  |             Attrs[CHALLENGE_CODE] = Challenge; | ||||||
|  |             return SMTPMailerService()->SendMessage(UInfo.userinfo.email, "verification_code.txt", Attrs); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool MFAServer::ResendCode(const std::string &uuid) { | ||||||
|  |         std::lock_guard G(Mutex_); | ||||||
|  |         auto Hint = Cache_.find(uuid); | ||||||
|  |         if(Hint==Cache_.end()) | ||||||
|  |             return false; | ||||||
|  |         return SendChallenge(Hint->second.UInfo, Hint->second.Method, Hint->second.Answer); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool MFAServer::CompleteMFAChallenge(Poco::JSON::Object::Ptr &ChallengeResponse, SecurityObjects::UserInfoAndPolicy &UInfo) { | ||||||
|  |         std::lock_guard G(Mutex_); | ||||||
|  |  | ||||||
|  |         if(!ChallengeResponse->has("uuid") || !ChallengeResponse->has("answer")) | ||||||
|  |             return false; | ||||||
|  |  | ||||||
|  |         auto uuid = ChallengeResponse->get("uuid").toString(); | ||||||
|  |         auto Hint = Cache_.find(uuid); | ||||||
|  |         if(Hint == end(Cache_)) | ||||||
|  |             return false; | ||||||
|  |  | ||||||
|  |         auto answer = ChallengeResponse->get("answer").toString(); | ||||||
|  |         if(Hint->second.Answer!=answer) { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         UInfo = Hint->second.UInfo; | ||||||
|  |         Cache_.erase(Hint); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool MFAServer::MethodEnabled(const std::string &Method) { | ||||||
|  |         if(Method=="sms") | ||||||
|  |             return SMSSender()->Enabled(); | ||||||
|  |  | ||||||
|  |         if(Method=="email") | ||||||
|  |             return SMTPMailerService()->Enabled(); | ||||||
|  |  | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void MFAServer::CleanCache() { | ||||||
|  |         // it is assumed that you have locked Cache_ at this point. | ||||||
|  |         uint64_t Now = std::time(nullptr); | ||||||
|  |         for(auto i=begin(Cache_);i!=end(Cache_);) { | ||||||
|  |             if((Now-i->second.Created)>300) { | ||||||
|  |                 i = Cache_.erase(i); | ||||||
|  |             } else { | ||||||
|  |                 ++i; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										57
									
								
								src/MFAServer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/MFAServer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-11. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_MFASERVER_H | ||||||
|  | #define OWSEC_MFASERVER_H | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  | #include "Poco/JSON/Object.h" | ||||||
|  | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     struct MFACacheEntry { | ||||||
|  |         SecurityObjects::UserInfoAndPolicy  UInfo; | ||||||
|  |         std::string                         Answer; | ||||||
|  |         uint64_t                            Created; | ||||||
|  |         std::string                         Method; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     typedef std::map<std::string,MFACacheEntry>     MFAChallengeCache; | ||||||
|  |  | ||||||
|  |     class MFAServer : public SubSystemServer{ | ||||||
|  |     public: | ||||||
|  |         int Start() override; | ||||||
|  |         void Stop() override; | ||||||
|  |         static MFAServer *instance() { | ||||||
|  |             if (instance_ == nullptr) { | ||||||
|  |                 instance_ = new MFAServer; | ||||||
|  |             } | ||||||
|  |             return instance_; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge); | ||||||
|  |         bool CompleteMFAChallenge(Poco::JSON::Object::Ptr &ChallengeResponse, SecurityObjects::UserInfoAndPolicy &UInfo); | ||||||
|  |         bool MethodEnabled(const std::string &Method); | ||||||
|  |         bool ResendCode(const std::string &uuid); | ||||||
|  |         bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); | ||||||
|  |  | ||||||
|  |         static inline std::string MakeChallenge() { | ||||||
|  |             return std::to_string(rand() % 999999); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         static MFAServer *  instance_; | ||||||
|  |         MFAChallengeCache   Cache_; | ||||||
|  |         MFAServer() noexcept: | ||||||
|  |             SubSystemServer("MFServer", "MFA-SVR", "mfa") | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |         void CleanCache(); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     inline MFAServer & MFAServer() { return *MFAServer::instance(); } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OWSEC_MFASERVER_H | ||||||
| @@ -1,532 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include <cstdlib> |  | ||||||
| #include <boost/algorithm/string.hpp> |  | ||||||
|  |  | ||||||
| #include "Poco/Util/Application.h" |  | ||||||
| #include "Poco/Util/ServerApplication.h" |  | ||||||
| #include "Poco/Util/Option.h" |  | ||||||
| #include "Poco/Util/OptionSet.h" |  | ||||||
| #include "Poco/Util/HelpFormatter.h" |  | ||||||
| #include "Poco/Environment.h" |  | ||||||
| #include "Poco/Net/HTTPSStreamFactory.h" |  | ||||||
| #include "Poco/Net/HTTPStreamFactory.h" |  | ||||||
| #include "Poco/Net/FTPSStreamFactory.h" |  | ||||||
| #include "Poco/Net/FTPStreamFactory.h" |  | ||||||
| #include "Poco/Path.h" |  | ||||||
| #include "Poco/File.h" |  | ||||||
| #include "Poco/String.h" |  | ||||||
| #include "Poco/JSON/Object.h" |  | ||||||
| #include "Poco/JSON/Parser.h" |  | ||||||
| #include "Poco/JSON/Stringifier.h" |  | ||||||
|  |  | ||||||
| #include "ALBHealthCheckServer.h" |  | ||||||
| #ifndef SMALL_BUILD |  | ||||||
| #include "KafkaManager.h" |  | ||||||
| #endif |  | ||||||
| #include "Kafka_topics.h" |  | ||||||
|  |  | ||||||
| #include "MicroService.h" |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| #ifndef TIP_SECURITY_SERVICE |  | ||||||
| #include "AuthClient.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	void MyErrorHandler::exception(const Poco::Exception & E) { |  | ||||||
| 		Poco::Thread * CurrentThread = Poco::Thread::current(); |  | ||||||
| 		App_.logger().log(E); |  | ||||||
| 		App_.logger().error(Poco::format("Exception occurred in %s",CurrentThread->getName())); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MyErrorHandler::exception(const std::exception & E) { |  | ||||||
| 		Poco::Thread * CurrentThread = Poco::Thread::current(); |  | ||||||
| 		App_.logger().warning(Poco::format("std::exception on %s",CurrentThread->getName())); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MyErrorHandler::exception() { |  | ||||||
| 		Poco::Thread * CurrentThread = Poco::Thread::current(); |  | ||||||
| 		App_.logger().warning(Poco::format("exception on %s",CurrentThread->getName())); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::Exit(int Reason) { |  | ||||||
| 		std::exit(Reason); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::BusMessageReceived(const std::string &Key, const std::string & Message) { |  | ||||||
| 		std::lock_guard G(InfraMutex_); |  | ||||||
| 		try { |  | ||||||
| 			Poco::JSON::Parser P; |  | ||||||
| 			auto Object = P.parse(Message).extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 			if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) && |  | ||||||
| 				Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) { |  | ||||||
| 				uint64_t 	ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID); |  | ||||||
| 				auto 		Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString(); |  | ||||||
| 				if (ID != ID_) { |  | ||||||
| 					if(	Event==KafkaTopics::ServiceEvents::EVENT_JOIN || |  | ||||||
| 						Event==KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE || |  | ||||||
| 						Event==KafkaTopics::ServiceEvents::EVENT_LEAVE ) { |  | ||||||
| 						if(	Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) && |  | ||||||
| 						   	Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) && |  | ||||||
| 							Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) && |  | ||||||
| 						   	Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) && |  | ||||||
| 							Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) { |  | ||||||
|  |  | ||||||
| 							if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(ID) != Services_.end()) { |  | ||||||
| 								Services_[ID].LastUpdate = std::time(nullptr); |  | ||||||
| 							} else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) { |  | ||||||
| 								Services_.erase(ID); |  | ||||||
| 								logger().information(Poco::format("Service %s ID=%Lu leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); |  | ||||||
| 							} else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) { |  | ||||||
| 								logger().information(Poco::format("Service %s ID=%Lu joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID)); |  | ||||||
| 								Services_[ID] = MicroServiceMeta{ |  | ||||||
| 									.Id = ID, |  | ||||||
| 									.Type = Poco::toLower(Object->get(KafkaTopics::ServiceEvents::Fields::TYPE).toString()), |  | ||||||
| 									.PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(), |  | ||||||
| 									.PublicEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC).toString(), |  | ||||||
| 									.AccessKey = Object->get(KafkaTopics::ServiceEvents::Fields::KEY).toString(), |  | ||||||
| 									.Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN).toString(), |  | ||||||
| 									.LastUpdate = (uint64_t)std::time(nullptr)}; |  | ||||||
| 								for (const auto &[Id, Svc] : Services_) { |  | ||||||
| 									logger().information(Poco::format("ID: %Lu Type: %s EndPoint: %s",Id,Svc.Type,Svc.PrivateEndPoint)); |  | ||||||
| 								} |  | ||||||
| 							} |  | ||||||
| 						} else { |  | ||||||
| 							logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing a field.",Event)); |  | ||||||
| 						} |  | ||||||
| 					} else if (Event==KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) { |  | ||||||
| 						if(Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) { |  | ||||||
| #ifndef TIP_SECURITY_SERVICE |  | ||||||
| 							AuthClient()->RemovedCachedToken(Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString()); |  | ||||||
| #endif |  | ||||||
| 						} else { |  | ||||||
| 							logger().error(Poco::format("KAFKA-MSG: invalid event '%s', missing token",Event)); |  | ||||||
| 						} |  | ||||||
| 					} else { |  | ||||||
| 						logger().error(Poco::format("Unknown Event: %s Source: %Lu", Event, ID)); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				logger().error("Bad bus message."); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			auto i=Services_.begin(); |  | ||||||
| 			auto Now = (uint64_t )std::time(nullptr); |  | ||||||
| 			for(;i!=Services_.end();) { |  | ||||||
| 			    if((Now - i->second.LastUpdate)>60) { |  | ||||||
| 			        i = Services_.erase(i); |  | ||||||
| 			    } else |  | ||||||
| 			        ++i; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 		} catch (const Poco::Exception &E) { |  | ||||||
| 			logger().log(E); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	MicroServiceMetaVec MicroService::GetServices(const std::string & Type) { |  | ||||||
| 		std::lock_guard G(InfraMutex_); |  | ||||||
|  |  | ||||||
| 		auto T = Poco::toLower(Type); |  | ||||||
| 		MicroServiceMetaVec	Res; |  | ||||||
| 		for(const auto &[Id,ServiceRec]:Services_) { |  | ||||||
| 			if(ServiceRec.Type==T) |  | ||||||
| 				Res.push_back(ServiceRec); |  | ||||||
| 		} |  | ||||||
| 		return Res; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	MicroServiceMetaVec MicroService::GetServices() { |  | ||||||
| 		std::lock_guard G(InfraMutex_); |  | ||||||
|  |  | ||||||
| 		MicroServiceMetaVec	Res; |  | ||||||
| 		for(const auto &[Id,ServiceRec]:Services_) { |  | ||||||
| 			Res.push_back(ServiceRec); |  | ||||||
| 		} |  | ||||||
| 		return Res; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void MicroService::LoadConfigurationFile() { |  | ||||||
| 	    std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,"."); |  | ||||||
| 	    Poco::Path ConfigFile; |  | ||||||
|  |  | ||||||
| 	    ConfigFile = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_; |  | ||||||
|  |  | ||||||
| 	    if(!ConfigFile.isFile()) |  | ||||||
| 	    { |  | ||||||
| 	        std::cerr << DAEMON_APP_NAME << ": Configuration " |  | ||||||
| 	        << ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR |  | ||||||
| 	        + " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl; |  | ||||||
| 	        std::exit(Poco::Util::Application::EXIT_CONFIG); |  | ||||||
| 	    } |  | ||||||
|  |  | ||||||
| 	    loadConfiguration(ConfigFile.toString()); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void MicroService::Reload() { |  | ||||||
| 	    LoadConfigurationFile(); |  | ||||||
| 	    LoadMyConfig(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void MicroService::LoadMyConfig() { |  | ||||||
| 	    std::string KeyFile = ConfigPath("openwifi.service.key"); |  | ||||||
| 	    std::string KeyFilePassword = ConfigPath("openwifi.service.key.password" , "" ); |  | ||||||
| 	    AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword)); |  | ||||||
| 	    Cipher_ = CipherFactory_.createCipher(*AppKey_); |  | ||||||
| 	    ID_ = Utils::GetSystemId(); |  | ||||||
| 	    if(!DebugMode_) |  | ||||||
| 	        DebugMode_ = ConfigGetBool("openwifi.system.debug",false); |  | ||||||
| 	    MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private"); |  | ||||||
| 	    MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public"); |  | ||||||
| 	    UIURI_ = ConfigGetString("openwifi.system.uri.ui"); |  | ||||||
| 	    MyHash_ = CreateHash(MyPublicEndPoint_); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::initialize(Poco::Util::Application &self) { |  | ||||||
| 		// add the default services |  | ||||||
| 		SubSystems_.push_back(KafkaManager()); |  | ||||||
| 		SubSystems_.push_back(ALBHealthCheckServer()); |  | ||||||
|  |  | ||||||
| 		Poco::Net::initializeSSL(); |  | ||||||
| 		Poco::Net::HTTPStreamFactory::registerFactory(); |  | ||||||
| 		Poco::Net::HTTPSStreamFactory::registerFactory(); |  | ||||||
| 		Poco::Net::FTPStreamFactory::registerFactory(); |  | ||||||
| 		Poco::Net::FTPSStreamFactory::registerFactory(); |  | ||||||
|  |  | ||||||
| 		LoadConfigurationFile(); |  | ||||||
|  |  | ||||||
|         static const char * LogFilePathKey = "logging.channels.c2.path"; |  | ||||||
|  |  | ||||||
| 		if(LogDir_.empty()) { |  | ||||||
| 			std::string OriginalLogFileValue = ConfigPath(LogFilePathKey); |  | ||||||
| 			config().setString(LogFilePathKey, OriginalLogFileValue); |  | ||||||
| 		} else { |  | ||||||
| 			config().setString(LogFilePathKey, LogDir_); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Poco::File	DataDir(ConfigPath("openwifi.system.data")); |  | ||||||
| 		DataDir_ = DataDir.path(); |  | ||||||
| 		if(!DataDir.exists()) { |  | ||||||
| 			try { |  | ||||||
| 				DataDir.createDirectory(); |  | ||||||
| 			} catch (const Poco::Exception &E) { |  | ||||||
| 				logger().log(E); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		LoadMyConfig(); |  | ||||||
|  |  | ||||||
| 		InitializeSubSystemServers(); |  | ||||||
| 		ServerApplication::initialize(self); |  | ||||||
|  |  | ||||||
| 		Types::TopicNotifyFunction F = [this](std::string s1,std::string s2) { this->BusMessageReceived(s1,s2); }; |  | ||||||
| 		KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::uninitialize() { |  | ||||||
| 		// add your own uninitialization code here |  | ||||||
| 		ServerApplication::uninitialize(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::reinitialize(Poco::Util::Application &self) { |  | ||||||
| 		ServerApplication::reinitialize(self); |  | ||||||
| 		// add your own reinitialization code here |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::defineOptions(Poco::Util::OptionSet &options) { |  | ||||||
| 		ServerApplication::defineOptions(options); |  | ||||||
|  |  | ||||||
| 		options.addOption( |  | ||||||
| 			Poco::Util::Option("help", "", "display help information on command line arguments") |  | ||||||
| 				.required(false) |  | ||||||
| 				.repeatable(false) |  | ||||||
| 				.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleHelp))); |  | ||||||
|  |  | ||||||
| 		options.addOption( |  | ||||||
| 			Poco::Util::Option("file", "", "specify the configuration file") |  | ||||||
| 				.required(false) |  | ||||||
| 				.repeatable(false) |  | ||||||
| 				.argument("file") |  | ||||||
| 				.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleConfig))); |  | ||||||
|  |  | ||||||
| 		options.addOption( |  | ||||||
| 			Poco::Util::Option("debug", "", "to run in debug, set to true") |  | ||||||
| 				.required(false) |  | ||||||
| 				.repeatable(false) |  | ||||||
| 				.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleDebug))); |  | ||||||
|  |  | ||||||
| 		options.addOption( |  | ||||||
| 			Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)") |  | ||||||
| 				.required(false) |  | ||||||
| 				.repeatable(false) |  | ||||||
| 				.argument("dir") |  | ||||||
| 				.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleLogs))); |  | ||||||
|  |  | ||||||
| 		options.addOption( |  | ||||||
| 			Poco::Util::Option("version", "", "get the version and quit.") |  | ||||||
| 				.required(false) |  | ||||||
| 				.repeatable(false) |  | ||||||
| 				.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleVersion))); |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::handleHelp(const std::string &name, const std::string &value) { |  | ||||||
| 		HelpRequested_ = true; |  | ||||||
| 		displayHelp(); |  | ||||||
| 		stopOptionsProcessing(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::handleVersion(const std::string &name, const std::string &value) { |  | ||||||
| 		HelpRequested_ = true; |  | ||||||
| 		std::cout << Version() << std::endl; |  | ||||||
| 		stopOptionsProcessing(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::handleDebug(const std::string &name, const std::string &value) { |  | ||||||
| 		if(value == "true") |  | ||||||
| 			DebugMode_ = true ; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::handleLogs(const std::string &name, const std::string &value) { |  | ||||||
| 		LogDir_ = value; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::handleConfig(const std::string &name, const std::string &value) { |  | ||||||
| 		ConfigFileName_ = value; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::displayHelp() { |  | ||||||
| 		Poco::Util::HelpFormatter helpFormatter(options()); |  | ||||||
| 		helpFormatter.setCommand(commandName()); |  | ||||||
| 		helpFormatter.setUsage("OPTIONS"); |  | ||||||
| 		helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP."); |  | ||||||
| 		helpFormatter.format(std::cout); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::InitializeSubSystemServers() { |  | ||||||
| 		for(auto i:SubSystems_) |  | ||||||
| 			addSubsystem(i); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::StartSubSystemServers() { |  | ||||||
| 		for(auto i:SubSystems_) { |  | ||||||
| 			i->Start(); |  | ||||||
| 		} |  | ||||||
| 		BusEventManager_.Start(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::StopSubSystemServers() { |  | ||||||
| 		BusEventManager_.Stop(); |  | ||||||
| 		for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) |  | ||||||
| 			(*i)->Stop(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::CreateUUID() { |  | ||||||
| 		return UUIDGenerator_.create().toString(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { |  | ||||||
| 		try { |  | ||||||
| 			auto P = Poco::Logger::parseLevel(Level); |  | ||||||
| 			auto Sub = Poco::toLower(SubSystem); |  | ||||||
|  |  | ||||||
| 			if (Sub == "all") { |  | ||||||
| 				for (auto i : SubSystems_) { |  | ||||||
| 					i->Logger().setLevel(P); |  | ||||||
| 				} |  | ||||||
| 				return true; |  | ||||||
| 			} else { |  | ||||||
| 				// std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl; |  | ||||||
| 				for (auto i : SubSystems_) { |  | ||||||
| 					if (Sub == Poco::toLower(i->Name())) { |  | ||||||
| 						i->Logger().setLevel(P); |  | ||||||
| 						return true; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} catch (const Poco::Exception & E) { |  | ||||||
| 			std::cout << "Exception" << std::endl; |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::Reload(const std::string &Sub) { |  | ||||||
| 		for (auto i : SubSystems_) { |  | ||||||
| 			if (Poco::toLower(Sub) == Poco::toLower(i->Name())) { |  | ||||||
| 				i->reinitialize(Poco::Util::Application::instance()); |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Types::StringVec MicroService::GetSubSystems() const { |  | ||||||
| 		Types::StringVec Result; |  | ||||||
| 		for(auto i:SubSystems_) |  | ||||||
| 			Result.push_back(Poco::toLower(i->Name())); |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	Types::StringPairVec MicroService::GetLogLevels() { |  | ||||||
| 		Types::StringPairVec Result; |  | ||||||
|  |  | ||||||
| 		for(auto &i:SubSystems_) { |  | ||||||
| 			auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel())); |  | ||||||
| 			Result.push_back(P); |  | ||||||
| 		} |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const Types::StringVec & MicroService::GetLogLevelNames() { |  | ||||||
| 		static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" }; |  | ||||||
| 		return LevelNames; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) { |  | ||||||
| 		return (uint64_t) config().getInt64(Key,Default); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t MicroService::ConfigGetInt(const std::string &Key) { |  | ||||||
| 		return config().getInt(Key); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) { |  | ||||||
| 		return config().getBool(Key,Default); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t MicroService::ConfigGetBool(const std::string &Key) { |  | ||||||
| 		return config().getBool(Key); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) { |  | ||||||
| 		return config().getString(Key, Default); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::ConfigGetString(const std::string &Key) { |  | ||||||
| 		return config().getString(Key); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) { |  | ||||||
| 		std::string R = config().getString(Key, Default); |  | ||||||
| 		return Poco::Path::expand(R); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::ConfigPath(const std::string &Key) { |  | ||||||
| 		std::string R = config().getString(Key); |  | ||||||
| 		return Poco::Path::expand(R); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::Encrypt(const std::string &S) { |  | ||||||
| 		return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::Decrypt(const std::string &S) { |  | ||||||
| 		return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::CreateHash(const std::string &S) { |  | ||||||
| 		SHA2_.update(S); |  | ||||||
| 		return Utils::ToHex(SHA2_.digest()); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const { |  | ||||||
| 		Poco::JSON::Object	Obj; |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::ID,ID_); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE,Poco::toLower(DAEMON_APP_NAME)); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC,MyPublicEndPoint_); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE,MyPrivateEndPoint_); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::KEY,MyHash_); |  | ||||||
| 		Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN,Version_); |  | ||||||
| 		std::stringstream ResultText; |  | ||||||
| 		Poco::JSON::Stringifier::stringify(Obj, ResultText); |  | ||||||
| 		return ResultText.str(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void BusEventManager::run() { |  | ||||||
| 		Running_ = true; |  | ||||||
| 		auto Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN); |  | ||||||
| 		KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); |  | ||||||
| 		while(Running_) { |  | ||||||
| 			Poco::Thread::trySleep((unsigned long)Daemon()->DaemonBusTimer()); |  | ||||||
| 			if(!Running_) |  | ||||||
| 				break; |  | ||||||
| 			Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE); |  | ||||||
| 			KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); |  | ||||||
| 		} |  | ||||||
| 		Msg = Daemon()->MakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE); |  | ||||||
| 		KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,Daemon()->PrivateEndPoint(),Msg, false); |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	void BusEventManager::Start() { |  | ||||||
| 		if(KafkaManager()->Enabled()) { |  | ||||||
| 			Thread_.start(*this); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void BusEventManager::Stop() { |  | ||||||
| 		if(KafkaManager()->Enabled()) { |  | ||||||
| 			Running_ = false; |  | ||||||
| 			Thread_.wakeUp(); |  | ||||||
| 			Thread_.join(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) { |  | ||||||
| 		try { |  | ||||||
| 			auto APIKEY = Request.get("X-API-KEY"); |  | ||||||
| 			return APIKEY == MyHash_; |  | ||||||
| 		} catch (const Poco::Exception &E) { |  | ||||||
| 			logger().log(E); |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void MicroService::SavePID() { |  | ||||||
| 		try { |  | ||||||
| 			std::ofstream O; |  | ||||||
| 			O.open(Daemon()->DataDir() + "/pidfile",std::ios::binary | std::ios::trunc); |  | ||||||
| 			O << Poco::Process::id(); |  | ||||||
| 			O.close(); |  | ||||||
| 		} catch (...) |  | ||||||
| 		{ |  | ||||||
| 			std::cout << "Could not save system ID" << std::endl; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	int MicroService::main(const ArgVec &args) { |  | ||||||
|  |  | ||||||
| 		MyErrorHandler	ErrorHandler(*this); |  | ||||||
| 		Poco::ErrorHandler::set(&ErrorHandler); |  | ||||||
|  |  | ||||||
| 		if (!HelpRequested_) { |  | ||||||
| 			SavePID(); |  | ||||||
| 			Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME); |  | ||||||
| 			logger.notice(Poco::format("Starting %s version %s.",DAEMON_APP_NAME, Version())); |  | ||||||
|  |  | ||||||
| 			if(Poco::Net::Socket::supportsIPv6()) |  | ||||||
| 				logger.information("System supports IPv6."); |  | ||||||
| 			else |  | ||||||
| 				logger.information("System does NOT support IPv6."); |  | ||||||
|  |  | ||||||
| 			if (config().getBool("application.runAsDaemon", false)) { |  | ||||||
| 				logger.information("Starting as a daemon."); |  | ||||||
| 			} |  | ||||||
| 			logger.information(Poco::format("System ID set to %Lu",ID_)); |  | ||||||
| 			StartSubSystemServers(); |  | ||||||
| 			waitForTerminationRequest(); |  | ||||||
| 			StopSubSystemServers(); |  | ||||||
|  |  | ||||||
| 			logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME)); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return Application::EXIT_OK; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,183 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_MICROSERVICE_H |  | ||||||
| #define UCENTRALGW_MICROSERVICE_H |  | ||||||
|  |  | ||||||
| #include <array> |  | ||||||
| #include <iostream> |  | ||||||
| #include <cstdlib> |  | ||||||
| #include <vector> |  | ||||||
| #include <set> |  | ||||||
|  |  | ||||||
| #include "Poco/Util/Application.h" |  | ||||||
| #include "Poco/Util/ServerApplication.h" |  | ||||||
| #include "Poco/Util/Option.h" |  | ||||||
| #include "Poco/Util/OptionSet.h" |  | ||||||
| #include "Poco/UUIDGenerator.h" |  | ||||||
| #include "Poco/ErrorHandler.h" |  | ||||||
| #include "Poco/Crypto/RSAKey.h" |  | ||||||
| #include "Poco/Crypto/CipherFactory.h" |  | ||||||
| #include "Poco/Crypto/Cipher.h" |  | ||||||
| #include "Poco/SHA2Engine.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Process.h" |  | ||||||
|  |  | ||||||
| #include "OpenWifiTypes.h" |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	static const std::string uSERVICE_SECURITY{"owsec"}; |  | ||||||
| 	static const std::string uSERVICE_GATEWAY{"owgw"}; |  | ||||||
| 	static const std::string uSERVICE_FIRMWARE{ "owfms"}; |  | ||||||
|     static const std::string uSERVICE_TOPOLOGY{ "owtopo"}; |  | ||||||
|     static const std::string uSERVICE_PROVISIONING{ "owprov"}; |  | ||||||
|     static const std::string uSERVICE_OWLS{ "owls"}; |  | ||||||
|  |  | ||||||
| 	class MyErrorHandler : public Poco::ErrorHandler { |  | ||||||
| 	  public: |  | ||||||
| 		explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {} |  | ||||||
| 		void exception(const Poco::Exception & E) override; |  | ||||||
| 		void exception(const std::exception & E) override; |  | ||||||
| 		void exception() override; |  | ||||||
| 	  private: |  | ||||||
| 		Poco::Util::Application	&App_; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	class BusEventManager : public Poco::Runnable { |  | ||||||
| 	  public: |  | ||||||
| 		void run() override; |  | ||||||
| 		void Start(); |  | ||||||
| 		void Stop(); |  | ||||||
| 	  private: |  | ||||||
| 		std::atomic_bool 	Running_ = false; |  | ||||||
| 		Poco::Thread		Thread_; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	struct MicroServiceMeta { |  | ||||||
| 		uint64_t 		Id=0; |  | ||||||
| 		std::string 	Type; |  | ||||||
| 		std::string 	PrivateEndPoint; |  | ||||||
| 		std::string 	PublicEndPoint; |  | ||||||
| 		std::string 	AccessKey; |  | ||||||
| 		std::string		Version; |  | ||||||
| 		uint64_t 		LastUpdate=0; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	typedef std::map<uint64_t, MicroServiceMeta>	MicroServiceMetaMap; |  | ||||||
| 	typedef std::vector<MicroServiceMeta>			MicroServiceMetaVec; |  | ||||||
|  |  | ||||||
| 	class MicroService : public Poco::Util::ServerApplication { |  | ||||||
| 	  public: |  | ||||||
| 		explicit MicroService( 	std::string PropFile, |  | ||||||
| 					 	std::string RootEnv, |  | ||||||
| 					 	std::string ConfigVar, |  | ||||||
| 					 	std::string AppName, |  | ||||||
| 					  	uint64_t BusTimer, |  | ||||||
| 					  	Types::SubSystemVec Subsystems) : |  | ||||||
| 			DAEMON_PROPERTIES_FILENAME(std::move(PropFile)), |  | ||||||
| 			DAEMON_ROOT_ENV_VAR(std::move(RootEnv)), |  | ||||||
| 			DAEMON_CONFIG_ENV_VAR(std::move(ConfigVar)), |  | ||||||
| 			DAEMON_APP_NAME(std::move(AppName)), |  | ||||||
| 			DAEMON_BUS_TIMER(BusTimer), |  | ||||||
| 			SubSystems_(std::move(Subsystems)) { |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		int main(const ArgVec &args) override; |  | ||||||
| 		void initialize(Application &self) override; |  | ||||||
| 		void uninitialize() override; |  | ||||||
| 		void reinitialize(Application &self) override; |  | ||||||
| 		void defineOptions(Poco::Util::OptionSet &options) override; |  | ||||||
| 		void handleHelp(const std::string &name, const std::string &value); |  | ||||||
| 		void handleVersion(const std::string &name, const std::string &value); |  | ||||||
| 		void handleDebug(const std::string &name, const std::string &value); |  | ||||||
| 		void handleLogs(const std::string &name, const std::string &value); |  | ||||||
| 		void handleConfig(const std::string &name, const std::string &value); |  | ||||||
| 		void displayHelp(); |  | ||||||
|  |  | ||||||
| 		void InitializeSubSystemServers(); |  | ||||||
| 		void StartSubSystemServers(); |  | ||||||
| 		void StopSubSystemServers(); |  | ||||||
| 		void Exit(int Reason); |  | ||||||
| 		bool SetSubsystemLogLevel(const std::string & SubSystem, const std::string & Level); |  | ||||||
| 		[[nodiscard]] std::string Version() { return Version_; } |  | ||||||
| 		[[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; } |  | ||||||
| 		[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } |  | ||||||
| 		[[nodiscard]] std::string CreateUUID(); |  | ||||||
| 		[[nodiscard]] bool Debug() const { return DebugMode_; } |  | ||||||
| 		[[nodiscard]] uint64_t ID() const { return ID_; } |  | ||||||
| 		[[nodiscard]] Types::StringVec GetSubSystems() const; |  | ||||||
| 		[[nodiscard]] Types::StringPairVec GetLogLevels() ; |  | ||||||
| 		[[nodiscard]] static const Types::StringVec & GetLogLevelNames(); |  | ||||||
| 		[[nodiscard]] std::string ConfigGetString(const std::string &Key,const std::string & Default); |  | ||||||
| 		[[nodiscard]] std::string ConfigGetString(const std::string &Key); |  | ||||||
| 		[[nodiscard]] std::string ConfigPath(const std::string &Key,const std::string & Default); |  | ||||||
| 		[[nodiscard]] std::string ConfigPath(const std::string &Key); |  | ||||||
| 		[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key,uint64_t Default); |  | ||||||
| 		[[nodiscard]] uint64_t ConfigGetInt(const std::string &Key); |  | ||||||
| 		[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key,bool Default); |  | ||||||
| 		[[nodiscard]] uint64_t ConfigGetBool(const std::string &Key); |  | ||||||
| 		[[nodiscard]] std::string Encrypt(const std::string &S); |  | ||||||
| 		[[nodiscard]] std::string Decrypt(const std::string &S); |  | ||||||
| 		[[nodiscard]] std::string CreateHash(const std::string &S); |  | ||||||
| 		[[nodiscard]] std::string Hash() const { return MyHash_; }; |  | ||||||
| 		[[nodiscard]] std::string ServiceType() const { return DAEMON_APP_NAME; }; |  | ||||||
| 		[[nodiscard]] std::string PrivateEndPoint() const { return MyPrivateEndPoint_; }; |  | ||||||
| 		[[nodiscard]] std::string PublicEndPoint() const { return MyPublicEndPoint_; }; |  | ||||||
| 		[[nodiscard]] std::string MakeSystemEventMessage( const std::string & Type ) const ; |  | ||||||
| 		[[nodiscard]] const Types::SubSystemVec & GetFullSubSystems() { return SubSystems_; } |  | ||||||
| 		inline uint64_t DaemonBusTimer() const { return DAEMON_BUS_TIMER; }; |  | ||||||
|  |  | ||||||
| 		void BusMessageReceived( const std::string & Key, const std::string & Message); |  | ||||||
| 		[[nodiscard]] MicroServiceMetaVec GetServices(const std::string & type); |  | ||||||
| 		[[nodiscard]] MicroServiceMetaVec GetServices(); |  | ||||||
| 		[[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); |  | ||||||
|  |  | ||||||
| 		static void SavePID(); |  | ||||||
| 		static inline uint64_t GetPID() { return Poco::Process::id(); }; |  | ||||||
| 		[[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; }; |  | ||||||
| 		[[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;}; |  | ||||||
|  |  | ||||||
| 		void Reload(const std::string &Name);   //  reload a subsystem |  | ||||||
| 		void Reload();                          //  reload the daemon itself |  | ||||||
| 		void LoadMyConfig(); |  | ||||||
|  |  | ||||||
| 		void LoadConfigurationFile(); |  | ||||||
|  |  | ||||||
| 	  private: |  | ||||||
| 		bool                        HelpRequested_ = false; |  | ||||||
| 		std::string                 LogDir_; |  | ||||||
| 		std::string                 ConfigFileName_; |  | ||||||
| 		Poco::UUIDGenerator         UUIDGenerator_; |  | ||||||
| 		uint64_t                    ID_ = 1; |  | ||||||
| 		Poco::SharedPtr<Poco::Crypto::RSAKey>	AppKey_ = nullptr; |  | ||||||
| 		bool                        DebugMode_ = false; |  | ||||||
| 		std::string 				DataDir_; |  | ||||||
| 		Types::SubSystemVec			SubSystems_; |  | ||||||
| 		Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); |  | ||||||
| 		Poco::Crypto::Cipher        * Cipher_ = nullptr; |  | ||||||
| 		Poco::SHA2Engine			SHA2_; |  | ||||||
| 		MicroServiceMetaMap			Services_; |  | ||||||
| 		std::string 				MyHash_; |  | ||||||
| 		std::string 				MyPrivateEndPoint_; |  | ||||||
| 		std::string 				MyPublicEndPoint_; |  | ||||||
| 		std::string                 UIURI_; |  | ||||||
| 		std::string 				Version_{std::string(APP_VERSION) + "("+ BUILD_NUMBER + ")"}; |  | ||||||
| 		BusEventManager				BusEventManager_; |  | ||||||
| 		std::mutex 					InfraMutex_; |  | ||||||
|  |  | ||||||
| 		std::string DAEMON_PROPERTIES_FILENAME; |  | ||||||
| 		std::string DAEMON_ROOT_ENV_VAR; |  | ||||||
| 		std::string DAEMON_CONFIG_ENV_VAR; |  | ||||||
| 		std::string DAEMON_APP_NAME; |  | ||||||
| 		uint64_t 	DAEMON_BUS_TIMER; |  | ||||||
| 	}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_MICROSERVICE_H |  | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include <iostream> |  | ||||||
|  |  | ||||||
| #include "OpenAPIRequest.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Net/HTTPSClientSession.h" |  | ||||||
| #include <Poco/Net/HTTPRequest.h> |  | ||||||
| #include <Poco/Net/HTTPResponse.h> |  | ||||||
| #include <Poco/JSON/Parser.h> |  | ||||||
| #include <Poco/URI.h> |  | ||||||
| #include <Poco/Exception.h> |  | ||||||
| #include "Utils.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	OpenAPIRequestGet::OpenAPIRequestGet( 	std::string ServiceType, |  | ||||||
| 											std::string EndPoint, |  | ||||||
| 									 		Types::StringPairVec & QueryData, |  | ||||||
| 											uint64_t msTimeout): |  | ||||||
|  		Type_(std::move(ServiceType)), |  | ||||||
|  		EndPoint_(std::move(EndPoint)), |  | ||||||
| 		QueryData_(QueryData), |  | ||||||
| 		msTimeout_(msTimeout) { |  | ||||||
|  |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	int OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject) { |  | ||||||
| 		try { |  | ||||||
| 		    auto Services = Daemon()->GetServices(Type_); |  | ||||||
| 			for(auto const &Svc:Services) { |  | ||||||
| 				Poco::URI	URI(Svc.PrivateEndPoint); |  | ||||||
| 				Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort()); |  | ||||||
|  |  | ||||||
| 				URI.setPath(EndPoint_); |  | ||||||
| 				for (const auto &qp : QueryData_) |  | ||||||
| 					URI.addQueryParameter(qp.first, qp.second); |  | ||||||
|  |  | ||||||
| 				std::string Path(URI.getPathAndQuery()); |  | ||||||
| 				Session.setTimeout(Poco::Timespan(msTimeout_/1000, msTimeout_ % 1000)); |  | ||||||
|  |  | ||||||
| 				Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET, |  | ||||||
| 											   Path, |  | ||||||
| 											   Poco::Net::HTTPMessage::HTTP_1_1); |  | ||||||
| 				Request.add("X-API-KEY", Svc.AccessKey); |  | ||||||
| 				Session.sendRequest(Request); |  | ||||||
|  |  | ||||||
| 				Poco::Net::HTTPResponse Response; |  | ||||||
| 				std::istream &is = Session.receiveResponse(Response); |  | ||||||
| 				if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { |  | ||||||
| 					Poco::JSON::Parser	P; |  | ||||||
| 					ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 				} |  | ||||||
| 				return Response.getStatus(); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		catch (const Poco::Exception &E) |  | ||||||
| 		{ |  | ||||||
| 			std::cerr << E.displayText() << std::endl; |  | ||||||
| 		} |  | ||||||
| 		return -1; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,33 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_OPENAPIREQUEST_H |  | ||||||
| #define UCENTRALGW_OPENAPIREQUEST_H |  | ||||||
|  |  | ||||||
| #include "Poco/JSON/Object.h" |  | ||||||
|  |  | ||||||
| #include "OpenWifiTypes.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
| 	class OpenAPIRequestGet { |  | ||||||
| 	  public: |  | ||||||
| 		explicit OpenAPIRequestGet( std::string Type, |  | ||||||
| 								   	std::string EndPoint, |  | ||||||
| 									Types::StringPairVec & QueryData, |  | ||||||
| 									uint64_t msTimeout); |  | ||||||
| 		int Do(Poco::JSON::Object::Ptr &ResponseObject); |  | ||||||
| 	  private: |  | ||||||
| 		std::string 			Type_; |  | ||||||
| 		std::string 			EndPoint_; |  | ||||||
| 		Types::StringPairVec 	QueryData_; |  | ||||||
| 		uint64_t 				msTimeout_; |  | ||||||
| 	}; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_OPENAPIREQUEST_H |  | ||||||
| @@ -4,24 +4,21 @@ | |||||||
| 
 | 
 | ||||||
| #include "RESTAPI_AssetServer.h" | #include "RESTAPI_AssetServer.h" | ||||||
| #include "Poco/File.h" | #include "Poco/File.h" | ||||||
|  | #include "framework/RESTAPI_protocol.h" | ||||||
| #include "Daemon.h" | #include "Daemon.h" | ||||||
| #include "RESTAPI_server.h" |  | ||||||
| #include "Utils.h" |  | ||||||
| #include "RESTAPI_protocol.h" |  | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     void RESTAPI_AssetServer::DoGet() { |     void RESTAPI_AssetServer::DoGet() { | ||||||
|         Poco::File  AssetFile; |         Poco::File  AssetFile; | ||||||
| 
 | 
 | ||||||
|         if(Request->getURI().find("/favicon.ico") != std::string::npos) { |         if(Request->getURI().find("/favicon.ico") != std::string::npos) { | ||||||
|             AssetFile = RESTAPI_Server()->AssetDir() + "/favicon.ico"; |             AssetFile = Daemon()->AssetDir() + "/favicon.ico"; | ||||||
|         } else { |         } else { | ||||||
|             std::string AssetName = GetBinding(RESTAPI::Protocol::ID, ""); |             std::string AssetName = GetBinding(RESTAPI::Protocol::ID, ""); | ||||||
|             AssetFile = RESTAPI_Server()->AssetDir() + "/" + AssetName; |             AssetFile = Daemon()->AssetDir() + "/" + AssetName; | ||||||
|         } |         } | ||||||
|         if(!AssetFile.isFile()) { |         if(!AssetFile.isFile()) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         SendFile(AssetFile); |         SendFile(AssetFile); | ||||||
|     } |     } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
| #ifndef UCENTRALSEC_RESTAPI_ASSETSERVER_H | #ifndef UCENTRALSEC_RESTAPI_ASSETSERVER_H | ||||||
| #define UCENTRALSEC_RESTAPI_ASSETSERVER_H | #define UCENTRALSEC_RESTAPI_ASSETSERVER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "../framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_AssetServer : public RESTAPIHandler { |     class RESTAPI_AssetServer : public RESTAPIHandler { | ||||||
| @@ -19,7 +19,7 @@ namespace OpenWifi { | |||||||
|                                           Poco::Net::HTTPRequest::HTTP_DELETE, |                                           Poco::Net::HTTPRequest::HTTP_DELETE, | ||||||
|                                           Poco::Net::HTTPRequest::HTTP_OPTIONS}, |                                           Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||||
|                                           Server, |                                           Server, | ||||||
|                                           Internal) {} |                                           Internal, false) {} | ||||||
|         static const std::list<const char *> PathName() { return std::list<const char *>{"/wwwassets/{id}" , |         static const std::list<const char *> PathName() { return std::list<const char *>{"/wwwassets/{id}" , | ||||||
|                                                                                          "/favicon.ico"}; }; |                                                                                          "/favicon.ico"}; }; | ||||||
|         void DoGet() final; |         void DoGet() final; | ||||||
| @@ -2,14 +2,12 @@ | |||||||
| // Created by stephane bourque on 2021-06-22.
 | // Created by stephane bourque on 2021-06-22.
 | ||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_action_links.h" |  | ||||||
| #include "StorageService.h" |  | ||||||
| #include "Utils.h" |  | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
| 
 |  | ||||||
| #include "Poco/JSON/Parser.h" | #include "Poco/JSON/Parser.h" | ||||||
| #include "Poco/Net/HTMLForm.h" | #include "Poco/Net/HTMLForm.h" | ||||||
| #include "RESTAPI_server.h" | 
 | ||||||
|  | #include "RESTAPI_action_links.h" | ||||||
|  | #include "StorageService.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
| #include "Daemon.h" | #include "Daemon.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| @@ -19,11 +17,11 @@ namespace OpenWifi { | |||||||
|         auto Id = GetParameter("id",""); |         auto Id = GetParameter("id",""); | ||||||
| 
 | 
 | ||||||
|         if(Action=="password_reset") |         if(Action=="password_reset") | ||||||
|             RequestResetPassword(Id); |             return RequestResetPassword(Id); | ||||||
|         else if(Action=="email_verification") |         else if(Action=="email_verification") | ||||||
|             DoEmailVerification(Id); |             return DoEmailVerification(Id); | ||||||
|         else |         else | ||||||
|             DoReturnA404(); |             return DoReturnA404(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void RESTAPI_action_links::DoPost() { |     void RESTAPI_action_links::DoPost() { | ||||||
| @@ -39,7 +37,7 @@ namespace OpenWifi { | |||||||
| 
 | 
 | ||||||
|     void RESTAPI_action_links::RequestResetPassword(std::string &Id) { |     void RESTAPI_action_links::RequestResetPassword(std::string &Id) { | ||||||
|         Logger_.information(Poco::format("REQUEST-PASSWORD-RESET(%s): For ID=%s", Request->clientAddress().toString(), Id)); |         Logger_.information(Poco::format("REQUEST-PASSWORD-RESET(%s): For ID=%s", Request->clientAddress().toString(), Id)); | ||||||
|         Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset.html"}; |         Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset.html"}; | ||||||
|         Types::StringPairVec    FormVars{ {"UUID", Id}, |         Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                           {"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; |                                           {"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||||
|         SendHTMLFileBack(FormFile,FormVars); |         SendHTMLFileBack(FormFile,FormVars); | ||||||
| @@ -54,45 +52,41 @@ namespace OpenWifi { | |||||||
|             auto Password2 = Form.get("password1","blu"); |             auto Password2 = Form.get("password1","blu"); | ||||||
|             Id = Form.get("id",""); |             Id = Form.get("id",""); | ||||||
|             if(Password1!=Password2 || !AuthService()->ValidatePassword(Password2) || !AuthService()->ValidatePassword(Password1)) { |             if(Password1!=Password2 || !AuthService()->ValidatePassword(Password2) || !AuthService()->ValidatePassword(Password1)) { | ||||||
|                 Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_error.html"}; |                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, |                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                                   {"ERROR_TEXT", "For some reason, the passwords entered do not match or they do not comply with" |                                                   {"ERROR_TEXT", "For some reason, the passwords entered do not match or they do not comply with" | ||||||
|                                                                  " accepted password creation restrictions. Please consult our on-line help" |                                                                  " accepted password creation restrictions. Please consult our on-line help" | ||||||
|                                                                  " to look at the our password policy. If you would like to contact us, please mention" |                                                                  " to look at the our password policy. If you would like to contact us, please mention" | ||||||
|                                                                  " id(" + Id + ")"}}; |                                                                  " id(" + Id + ")"}}; | ||||||
|                 SendHTMLFileBack(FormFile,FormVars); |                 return SendHTMLFileBack(FormFile,FormVars); | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             SecurityObjects::UserInfo   UInfo; |             SecurityObjects::UserInfo   UInfo; | ||||||
|             if(!Storage()->GetUserById(Id,UInfo)) { |             if(!StorageService()->GetUserById(Id,UInfo)) { | ||||||
|                 Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_error.html"}; |                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, |                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                                   {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}}; |                                                   {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}}; | ||||||
|                 SendHTMLFileBack(FormFile,FormVars); |                 return SendHTMLFileBack(FormFile,FormVars); | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(UInfo.blackListed || UInfo.suspended) { |             if(UInfo.blackListed || UInfo.suspended) { | ||||||
|                 Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_error.html"}; |                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, |                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                                   {"ERROR_TEXT", "Please contact our system administrators. We have identified an error in your account that must be resolved first."}}; |                                                   {"ERROR_TEXT", "Please contact our system administrators. We have identified an error in your account that must be resolved first."}}; | ||||||
|                 SendHTMLFileBack(FormFile,FormVars); |                 return SendHTMLFileBack(FormFile,FormVars); | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if(!AuthService()->SetPassword(Password1,UInfo)) { |             if(!AuthService()->SetPassword(Password1,UInfo)) { | ||||||
|                 Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_error.html"}; |                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, |                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                                   {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}}; |                                                   {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}}; | ||||||
|                 SendHTMLFileBack(FormFile,FormVars); |                 return SendHTMLFileBack(FormFile,FormVars); | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
|             Storage()->UpdateUserInfo(UInfo.email,Id,UInfo); |             StorageService()->UpdateUserInfo(UInfo.email,Id,UInfo); | ||||||
|             Poco::File  FormFile{ RESTAPI_Server()->AssetDir() + "/password_reset_success.html"}; |             Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_success.html"}; | ||||||
|             Types::StringPairVec    FormVars{ {"UUID", Id}, |             Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||||
|                                               {"USERNAME", UInfo.email}, |                                               {"USERNAME", UInfo.email}, | ||||||
|                                               {"ACTION_LINK",Daemon()->GetUIURI()}}; |                                               {"ACTION_LINK",MicroService::instance().GetUIURI()}}; | ||||||
|             SendHTMLFileBack(FormFile,FormVars); |             SendHTMLFileBack(FormFile,FormVars); | ||||||
|         } else { |         } else { | ||||||
|             DoReturnA404(); |             DoReturnA404(); | ||||||
| @@ -103,29 +97,28 @@ namespace OpenWifi { | |||||||
|         SecurityObjects::UserInfo UInfo; |         SecurityObjects::UserInfo UInfo; | ||||||
| 
 | 
 | ||||||
|         Logger_.information(Poco::format("EMAIL-VERIFICATION(%s): For ID=%s", Request->clientAddress().toString(), Id)); |         Logger_.information(Poco::format("EMAIL-VERIFICATION(%s): For ID=%s", Request->clientAddress().toString(), Id)); | ||||||
|         if (!Storage()->GetUserById(Id, UInfo)) { |         if (!StorageService()->GetUserById(Id, UInfo)) { | ||||||
|             Types::StringPairVec FormVars{{"UUID",       Id}, |             Types::StringPairVec FormVars{{"UUID",       Id}, | ||||||
|                                           {"ERROR_TEXT", "This does not appear to be a valid email verification link.."}}; |                                           {"ERROR_TEXT", "This does not appear to be a valid email verification link.."}}; | ||||||
|             Poco::File FormFile{RESTAPI_Server()->AssetDir() + "/email_verification_error.html"}; |             Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_error.html"}; | ||||||
|             SendHTMLFileBack(FormFile, FormVars); |             return SendHTMLFileBack(FormFile, FormVars); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         UInfo.waitingForEmailCheck = false; |         UInfo.waitingForEmailCheck = false; | ||||||
|         UInfo.validated = true; |         UInfo.validated = true; | ||||||
|         UInfo.lastEmailCheck = std::time(nullptr); |         UInfo.lastEmailCheck = std::time(nullptr); | ||||||
|         UInfo.validationDate = std::time(nullptr); |         UInfo.validationDate = std::time(nullptr); | ||||||
|         Storage()->UpdateUserInfo(UInfo.email, Id, UInfo); |         StorageService()->UpdateUserInfo(UInfo.email, Id, UInfo); | ||||||
|         Types::StringPairVec FormVars{{"UUID",     Id}, |         Types::StringPairVec FormVars{{"UUID",     Id}, | ||||||
|                                       {"USERNAME", UInfo.email}, |                                       {"USERNAME", UInfo.email}, | ||||||
|                                       {"ACTION_LINK",Daemon()->GetUIURI()}}; |                                       {"ACTION_LINK",MicroService::instance().GetUIURI()}}; | ||||||
|         Poco::File FormFile{RESTAPI_Server()->AssetDir() + "/email_verification_success.html"}; |         Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_success.html"}; | ||||||
|         SendHTMLFileBack(FormFile, FormVars); |         SendHTMLFileBack(FormFile, FormVars); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void RESTAPI_action_links::DoReturnA404() { |     void RESTAPI_action_links::DoReturnA404() { | ||||||
|         Types::StringPairVec FormVars; |         Types::StringPairVec FormVars; | ||||||
|         Poco::File FormFile{RESTAPI_Server()->AssetDir() + "/404_error.html"}; |         Poco::File FormFile{Daemon()->AssetDir() + "/404_error.html"}; | ||||||
|         SendHTMLFileBack(FormFile, FormVars); |         SendHTMLFileBack(FormFile, FormVars); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -6,14 +6,7 @@ | |||||||
| #define UCENTRALSEC_RESTAPI_ACTION_LINKS_H | #define UCENTRALSEC_RESTAPI_ACTION_LINKS_H | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| #include "Poco/Net/PartHandler.h" |  | ||||||
| #include "Poco/Message.h" |  | ||||||
| #include "Poco/Net/MessageHeader.h" |  | ||||||
| #include "Poco/Net/NameValueCollection.h" |  | ||||||
| #include "Poco/NullStream.h" |  | ||||||
| #include "Poco/StreamCopier.h" |  | ||||||
| #include "Poco/CountingStream.h" |  | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_action_links : public RESTAPIHandler { |     class RESTAPI_action_links : public RESTAPIHandler { | ||||||
| @@ -7,10 +7,9 @@ | |||||||
| 
 | 
 | ||||||
| #include "RESTAPI_avatarHandler.h" | #include "RESTAPI_avatarHandler.h" | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "Daemon.h" |  | ||||||
| #include "Poco/Net/HTMLForm.h" | #include "Poco/Net/HTMLForm.h" | ||||||
| #include "Utils.h" | #include "framework/RESTAPI_protocol.h" | ||||||
| #include "RESTAPI_protocol.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
| @@ -32,24 +31,23 @@ namespace OpenWifi { | |||||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); |         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||||
|         SecurityObjects::UserInfo UInfo; |         SecurityObjects::UserInfo UInfo; | ||||||
| 
 | 
 | ||||||
|         if (Id.empty() || !Storage()->GetUserById(Id, UInfo)) { |         if (Id.empty() || !StorageService()->GetUserById(Id, UInfo)) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         //  if there is an avatar, just remove it...
 |         //  if there is an avatar, just remove it...
 | ||||||
|         Storage()->DeleteAvatar(UserInfo_.userinfo.email,Id); |         StorageService()->DeleteAvatar(UserInfo_.userinfo.email,Id); | ||||||
| 
 | 
 | ||||||
|         Poco::TemporaryFile TmpFile; |         Poco::TemporaryFile TmpFile; | ||||||
|         AvatarPartHandler partHandler(Id, Logger_, TmpFile); |         AvatarPartHandler partHandler(Id, Logger_, TmpFile); | ||||||
| 
 | 
 | ||||||
|         Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); |         Poco::Net::HTMLForm form(*Request, Request->stream(), partHandler); | ||||||
|         Poco::JSON::Object Answer; |         Poco::JSON::Object Answer; | ||||||
|         if (!partHandler.Name().empty() && partHandler.Length()<Daemon()->ConfigGetInt("openwifi.avatar.maxsize",2000000)) { |         if (!partHandler.Name().empty() && partHandler.Length()< MicroService::instance().ConfigGetInt("openwifi.avatar.maxsize",2000000)) { | ||||||
|             Answer.set(RESTAPI::Protocol::AVATARID, Id); |             Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||||
|             Answer.set(RESTAPI::Protocol::ERRORCODE, 0); |             Answer.set(RESTAPI::Protocol::ERRORCODE, 0); | ||||||
|             Logger_.information(Poco::format("Uploaded avatar: %s Type: %s", partHandler.Name(), partHandler.ContentType())); |             Logger_.information(Poco::format("Uploaded avatar: %s Type: %s", partHandler.Name(), partHandler.ContentType())); | ||||||
|             Storage()->SetAvatar(UserInfo_.userinfo.email, |             StorageService()->SetAvatar(UserInfo_.userinfo.email, | ||||||
|                                  Id, TmpFile, partHandler.ContentType(), partHandler.Name()); |                                  Id, TmpFile, partHandler.ContentType(), partHandler.Name()); | ||||||
|         } else { |         } else { | ||||||
|             Answer.set(RESTAPI::Protocol::AVATARID, Id); |             Answer.set(RESTAPI::Protocol::AVATARID, Id); | ||||||
| @@ -62,14 +60,12 @@ namespace OpenWifi { | |||||||
|     void RESTAPI_avatarHandler::DoGet() { |     void RESTAPI_avatarHandler::DoGet() { | ||||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); |         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||||
|         if (Id.empty()) { |         if (Id.empty()) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         Poco::TemporaryFile TempAvatar; |         Poco::TemporaryFile TempAvatar; | ||||||
|         std::string Type, Name; |         std::string Type, Name; | ||||||
|         if (!Storage()->GetAvatar(UserInfo_.userinfo.email, Id, TempAvatar, Type, Name)) { |         if (!StorageService()->GetAvatar(UserInfo_.userinfo.email, Id, TempAvatar, Type, Name)) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         SendFile(TempAvatar, Type, Name); |         SendFile(TempAvatar, Type, Name); | ||||||
|     } |     } | ||||||
| @@ -77,12 +73,10 @@ namespace OpenWifi { | |||||||
|     void RESTAPI_avatarHandler::DoDelete() { |     void RESTAPI_avatarHandler::DoDelete() { | ||||||
|         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); |         std::string Id = GetBinding(RESTAPI::Protocol::ID, ""); | ||||||
|         if (Id.empty()) { |         if (Id.empty()) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         if (!Storage()->DeleteAvatar(UserInfo_.userinfo.email, Id)) { |         if (!StorageService()->DeleteAvatar(UserInfo_.userinfo.email, Id)) { | ||||||
|             NotFound(); |             return NotFound(); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         OK(); |         OK(); | ||||||
|     } |     } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
| #define UCENTRALSEC_RESTAPI_AVATARHANDLER_H | #define UCENTRALSEC_RESTAPI_AVATARHANDLER_H | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
| @@ -8,9 +8,9 @@ | |||||||
| #include "Poco/Exception.h" | #include "Poco/Exception.h" | ||||||
| #include "Poco/JSON/Parser.h" | #include "Poco/JSON/Parser.h" | ||||||
| 
 | 
 | ||||||
| #include "Daemon.h" |  | ||||||
| #include "SMTPMailerService.h" | #include "SMTPMailerService.h" | ||||||
| #include "RESTAPI_errors.h" | #include "framework/RESTAPI_errors.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     void RESTAPI_email_handler::DoPost() { |     void RESTAPI_email_handler::DoPost() { | ||||||
| @@ -18,18 +18,20 @@ namespace OpenWifi { | |||||||
|         if (Obj->has("subject") && |         if (Obj->has("subject") && | ||||||
|             Obj->has("from") && |             Obj->has("from") && | ||||||
|             Obj->has("text") && |             Obj->has("text") && | ||||||
|             Obj->has("recipients")) { |             Obj->has("recipients") && | ||||||
|             auto   Recipients = Obj->getArray("recipients"); |             Obj->isArray("recipients")) { | ||||||
|  | 
 | ||||||
|  |             Poco::JSON::Array::Ptr Recipients = Obj->getArray("recipients"); | ||||||
|  |             auto Recipient = Recipients->get(0).toString(); | ||||||
|             MessageAttributes Attrs; |             MessageAttributes Attrs; | ||||||
|             Attrs[RECIPIENT_EMAIL] = Recipients->get(0).toString(); |             Attrs[RECIPIENT_EMAIL] = Recipient; | ||||||
|             Attrs[SUBJECT] = Obj->get("subject").toString(); |             Attrs[SUBJECT] = Obj->get("subject").toString(); | ||||||
|             Attrs[TEXT] = Obj->get("text").toString(); |             Attrs[TEXT] = Obj->get("text").toString(); | ||||||
|             if(SMTPMailerService()->SendMessage(Recipients->get(0).toString(), "password_reset.txt", Attrs)) { |             Attrs[SENDER] = Obj->get("from").toString(); | ||||||
|                 OK(); |             if(SMTPMailerService()->SendMessage(Recipient, "password_reset.txt", Attrs)) { | ||||||
|                 return; |                 return OK(); | ||||||
|             } |             } | ||||||
|             ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE); |             return ReturnStatus(Poco::Net::HTTPResponse::HTTP_SERVICE_UNAVAILABLE); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|         BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); |         BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||||
|     } |     } | ||||||
| @@ -6,7 +6,7 @@ | |||||||
| #define OWSEC_RESTAPI_EMAIL_HANDLER_H | #define OWSEC_RESTAPI_EMAIL_HANDLER_H | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_email_handler : public RESTAPIHandler { |     class RESTAPI_email_handler : public RESTAPIHandler { | ||||||
							
								
								
									
										125
									
								
								src/RESTAPI/RESTAPI_oauth2Handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								src/RESTAPI/RESTAPI_oauth2Handler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "Poco/JSON/Parser.h" | ||||||
|  |  | ||||||
|  | #include "Daemon.h" | ||||||
|  | #include "AuthService.h" | ||||||
|  | #include "RESTAPI_oauth2Handler.h" | ||||||
|  | #include "MFAServer.h" | ||||||
|  | #include "framework/RESTAPI_protocol.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  | 	void RESTAPI_oauth2Handler::DoGet() { | ||||||
|  |         if (!IsAuthorized()) { | ||||||
|  |             return UnAuthorized("Not authorized."); | ||||||
|  |         } | ||||||
|  |         bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false); | ||||||
|  |         if(GetMe) { | ||||||
|  |             Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); | ||||||
|  |             Poco::JSON::Object Me; | ||||||
|  |             UserInfo_.userinfo.to_json(Me); | ||||||
|  |             return ReturnObject(Me); | ||||||
|  |         } | ||||||
|  |         BadRequest("Ill-formed request. Please consult documentation."); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |     void RESTAPI_oauth2Handler::DoDelete() { | ||||||
|  |         if (!IsAuthorized()) { | ||||||
|  |             return UnAuthorized("Not authorized."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "..."); | ||||||
|  |         if (Token == SessionToken_) { | ||||||
|  |             AuthService()->Logout(Token); | ||||||
|  |             return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Logger_.information(Poco::format("BAD-LOGOUT(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); | ||||||
|  |         NotFound(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void RESTAPI_oauth2Handler::DoPost() { | ||||||
|  |         auto Obj = ParseStream(); | ||||||
|  |         auto userId = GetS(RESTAPI::Protocol::USERID, Obj); | ||||||
|  |         auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj); | ||||||
|  |         auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj); | ||||||
|  |  | ||||||
|  |         Poco::toLowerInPlace(userId); | ||||||
|  |  | ||||||
|  |         if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS, false)) { | ||||||
|  |             Logger_.information(Poco::format("POLICY-REQUEST(%s): Request.", Request->clientAddress().toString())); | ||||||
|  |             Poco::JSON::Object  Answer; | ||||||
|  |             Answer.set(RESTAPI::Protocol::PASSWORDPATTERN, AuthService()->PasswordValidationExpression()); | ||||||
|  |             Answer.set(RESTAPI::Protocol::ACCESSPOLICY, Daemon()->GetAccessPolicy()); | ||||||
|  |             Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, Daemon()->GetPasswordPolicy()); | ||||||
|  |             return ReturnObject(Answer); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD,false)) { | ||||||
|  |             //  Send an email to the userId | ||||||
|  |             Logger_.information(Poco::format("FORGOTTEN-PASSWORD(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||||
|  |             SecurityObjects::UserInfoAndPolicy UInfo; | ||||||
|  |             if(AuthService::SendEmailToUser(userId,AuthService::FORGOT_PASSWORD)) | ||||||
|  |                 Logger_.information(Poco::format("Send password reset link to %s",userId)); | ||||||
|  |             UInfo.webtoken.userMustChangePassword=true; | ||||||
|  |             Poco::JSON::Object ReturnObj; | ||||||
|  |             UInfo.webtoken.to_json(ReturnObj); | ||||||
|  |             return ReturnObject(ReturnObj); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) { | ||||||
|  |             Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||||
|  |             if(Obj->has("uuid")) { | ||||||
|  |                 auto uuid = Obj->get("uuid").toString(); | ||||||
|  |                 if(MFAServer().ResendCode(uuid)) | ||||||
|  |                     return OK(); | ||||||
|  |                 return UnAuthorized("Unrecognized credentials (username/password)."); | ||||||
|  |             } | ||||||
|  |             return UnAuthorized("Unrecognized credentials (username/password)."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) { | ||||||
|  |             Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||||
|  |             if(Obj->has("uuid")) { | ||||||
|  |                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||||
|  |                 if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) { | ||||||
|  |                     Poco::JSON::Object ReturnObj; | ||||||
|  |                     UInfo.webtoken.to_json(ReturnObj); | ||||||
|  |                     return ReturnObject(ReturnObj); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             return UnAuthorized("Unrecognized credentials (username/password)."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SecurityObjects::UserInfoAndPolicy UInfo; | ||||||
|  |         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo); | ||||||
|  |         if (Code==AuthService::SUCCESS) { | ||||||
|  |             Poco::JSON::Object ReturnObj; | ||||||
|  |             if(AuthService()->RequiresMFA(UInfo)) { | ||||||
|  |                 if(MFAServer().StartMFAChallenge(UInfo, ReturnObj)) { | ||||||
|  |                     return ReturnObject(ReturnObj); | ||||||
|  |                 } | ||||||
|  |                 Logger_.warning("MFA Seems ot be broken. Please fix. Disabling MFA checking for now."); | ||||||
|  |             } | ||||||
|  |             UInfo.webtoken.to_json(ReturnObj); | ||||||
|  |             return ReturnObject(ReturnObj); | ||||||
|  |         } else { | ||||||
|  |             switch(Code) { | ||||||
|  |                 case AuthService::INVALID_CREDENTIALS: return UnAuthorized("Unrecognized credentials (username/password)."); break; | ||||||
|  |                 case AuthService::PASSWORD_INVALID: return UnAuthorized("Invalid password."); break; | ||||||
|  |                 case AuthService::PASSWORD_ALREADY_USED: return UnAuthorized("Password already used previously."); break; | ||||||
|  |                 case AuthService::USERNAME_PENDING_VERIFICATION: return UnAuthorized("User access pending email verification."); break; | ||||||
|  |                 case AuthService::PASSWORD_CHANGE_REQUIRED: return UnAuthorized("Password change expected."); break; | ||||||
|  |                 default: return UnAuthorized("Unrecognized credentials (username/password)."); break; | ||||||
|  |             } | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -9,7 +9,7 @@ | |||||||
| #ifndef UCENTRAL_RESTAPI_OAUTH2HANDLER_H | #ifndef UCENTRAL_RESTAPI_OAUTH2HANDLER_H | ||||||
| #define UCENTRAL_RESTAPI_OAUTH2HANDLER_H | #define UCENTRAL_RESTAPI_OAUTH2HANDLER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 	class RESTAPI_oauth2Handler : public RESTAPIHandler { | 	class RESTAPI_oauth2Handler : public RESTAPIHandler { | ||||||
							
								
								
									
										49
									
								
								src/RESTAPI/RESTAPI_sms_handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/RESTAPI/RESTAPI_sms_handler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-09. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "RESTAPI_sms_handler.h" | ||||||
|  | #include "SMSSender.h" | ||||||
|  | #include "framework/RESTAPI_errors.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |  | ||||||
|  |     void OpenWifi::RESTAPI_sms_handler::DoPost() { | ||||||
|  |         auto Obj = ParseStream(); | ||||||
|  |  | ||||||
|  |         std::string Arg; | ||||||
|  |         if(HasParameter("validateNumber",Arg) && Arg=="true" && Obj->has("to")) { | ||||||
|  |             auto Number = Obj->get("to").toString(); | ||||||
|  |             if(SMSSender()->StartValidation(Number, UserInfo_.userinfo.email)) { | ||||||
|  |                 return OK(); | ||||||
|  |             } | ||||||
|  |             return BadRequest("SMS could not be sent to validate device, try later or change the phone number."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::string Code; | ||||||
|  |         if( HasParameter("completeValidation",Arg) && | ||||||
|  |             Arg=="true" && | ||||||
|  |             HasParameter("validationCode", Code) && | ||||||
|  |             Obj->has("to")) { | ||||||
|  |             auto Number = Obj->get("to").toString(); | ||||||
|  |             if(SMSSender()->CompleteValidation(Number, Code, UserInfo_.userinfo.email)) { | ||||||
|  |                 return OK(); | ||||||
|  |             } | ||||||
|  |             return BadRequest("Code and number could not be validated"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (Obj->has("to") && | ||||||
|  |             Obj->has("text")) { | ||||||
|  |  | ||||||
|  |             std::string PhoneNumber = Obj->get("to").toString(); | ||||||
|  |             std::string Text = Obj->get("text").toString(); | ||||||
|  |             if(SMSSender()->Send(PhoneNumber, Text)) | ||||||
|  |                 return OK(); | ||||||
|  |  | ||||||
|  |             return InternalError("SMS Message could not be sent."); | ||||||
|  |         } | ||||||
|  |         BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/RESTAPI/RESTAPI_sms_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/RESTAPI/RESTAPI_sms_handler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-09. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_RESTAPI_SMS_HANDLER_H | ||||||
|  | #define OWSEC_RESTAPI_SMS_HANDLER_H | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     class RESTAPI_sms_handler : public RESTAPIHandler { | ||||||
|  |     public: | ||||||
|  |         RESTAPI_sms_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal) | ||||||
|  |         : RESTAPIHandler(bindings, L, | ||||||
|  |                          std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST, | ||||||
|  |                                                   Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||||
|  |                                                   Server, | ||||||
|  |                                                   Internal) {} | ||||||
|  |                                                   static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/sms"};} | ||||||
|  |         void DoGet() final {}; | ||||||
|  |         void DoPost() final; | ||||||
|  |         void DoDelete() final {}; | ||||||
|  |         void DoPut() final {}; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OWSEC_RESTAPI_SMS_HANDLER_H | ||||||
| @@ -3,13 +3,12 @@ | |||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_systemEndpoints_handler.h" | #include "RESTAPI_systemEndpoints_handler.h" | ||||||
| #include "Daemon.h" | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
| #include "RESTAPI_SecurityObjects.h" |  | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
|     void RESTAPI_systemEndpoints_handler::DoGet() { |     void RESTAPI_systemEndpoints_handler::DoGet() { | ||||||
|         auto Services = Daemon()->GetServices(); |         auto Services = MicroService::instance().GetServices(); | ||||||
|         SecurityObjects::SystemEndpointList L; |         SecurityObjects::SystemEndpointList L; | ||||||
|         for(const auto &i:Services) { |         for(const auto &i:Services) { | ||||||
|             SecurityObjects::SystemEndpoint S{ |             SecurityObjects::SystemEndpoint S{ | ||||||
| @@ -5,7 +5,8 @@ | |||||||
| #ifndef UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H | #ifndef UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H | ||||||
| #define UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H | #define UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "../framework/MicroService.h" | ||||||
|  | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_systemEndpoints_handler : public RESTAPIHandler { |     class RESTAPI_systemEndpoints_handler : public RESTAPIHandler { | ||||||
|     public: |     public: | ||||||
							
								
								
									
										196
									
								
								src/RESTAPI/RESTAPI_user_handler.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/RESTAPI/RESTAPI_user_handler.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-06-21. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "RESTAPI_user_handler.h" | ||||||
|  | #include "StorageService.h" | ||||||
|  | #include "Poco/JSON/Parser.h" | ||||||
|  | #include "framework/RESTAPI_errors.h" | ||||||
|  | #include "SMSSender.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     void RESTAPI_user_handler::DoGet() { | ||||||
|  |         std::string Id = GetBinding("id", ""); | ||||||
|  |         if(Id.empty()) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Poco::toLowerInPlace(Id); | ||||||
|  |         std::string Arg; | ||||||
|  |         SecurityObjects::UserInfo   UInfo; | ||||||
|  |         if(HasParameter("byEmail",Arg) && Arg=="true") { | ||||||
|  |             if(!StorageService()->GetUserByEmail(Id,UInfo)) { | ||||||
|  |                 return NotFound(); | ||||||
|  |             } | ||||||
|  |         } else if(!StorageService()->GetUserById(Id,UInfo)) { | ||||||
|  |             return NotFound(); | ||||||
|  |         } | ||||||
|  |         Poco::JSON::Object  UserInfoObject; | ||||||
|  |         UInfo.to_json(UserInfoObject); | ||||||
|  |         ReturnObject(UserInfoObject); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RESTAPI_user_handler::DoDelete() { | ||||||
|  |         std::string Id = GetBinding("id", ""); | ||||||
|  |         if(Id.empty()) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SecurityObjects::UserInfo UInfo; | ||||||
|  |         if(!StorageService()->GetUserById(Id,UInfo)) { | ||||||
|  |             return NotFound(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(!StorageService()->DeleteUser(UserInfo_.userinfo.email,Id)) { | ||||||
|  |             return NotFound(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(AuthService()->DeleteUserFromCache(UInfo.email)) | ||||||
|  |             ; | ||||||
|  |         Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email)); | ||||||
|  |         StorageService()->RevokeAllTokens(UInfo.email); | ||||||
|  |         Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email)); | ||||||
|  |         OK(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RESTAPI_user_handler::DoPost() { | ||||||
|  |         std::string Id = GetBinding("id", ""); | ||||||
|  |         if(Id!="0") { | ||||||
|  |             return BadRequest(RESTAPI::Errors::IdMustBe0); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SecurityObjects::UserInfo   UInfo; | ||||||
|  |         RESTAPI_utils::from_request(UInfo,*Request); | ||||||
|  |  | ||||||
|  |         if(UInfo.userRole == SecurityObjects::UNKNOWN) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Poco::toLowerInPlace(UInfo.email); | ||||||
|  |         if(!Utils::ValidEMailAddress(UInfo.email)) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(!UInfo.currentPassword.empty()) { | ||||||
|  |             if(!AuthService()->ValidatePassword(UInfo.currentPassword)) { | ||||||
|  |                 return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(UInfo.name.empty()) | ||||||
|  |             UInfo.name = UInfo.email; | ||||||
|  |  | ||||||
|  |         if(!StorageService()->CreateUser(UInfo.email,UInfo)) { | ||||||
|  |             Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email)); | ||||||
|  |             return BadRequest(RESTAPI::Errors::RecordNotCreated); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(GetParameter("email_verification","false")=="true") { | ||||||
|  |             if(AuthService::VerifyEmail(UInfo)) | ||||||
|  |                 Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email)); | ||||||
|  |             StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(!StorageService()->GetUserByEmail(UInfo.email, UInfo)) { | ||||||
|  |             Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email)); | ||||||
|  |             return NotFound(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Poco::JSON::Object  UserInfoObject; | ||||||
|  |         UInfo.to_json(UserInfoObject); | ||||||
|  |  | ||||||
|  |         ReturnObject(UserInfoObject); | ||||||
|  |  | ||||||
|  |         Logger_.information(Poco::format("User '%s' has been added by '%s')",UInfo.email, UserInfo_.userinfo.email)); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RESTAPI_user_handler::DoPut() { | ||||||
|  |         std::string Id = GetBinding("id", ""); | ||||||
|  |         if(Id.empty()) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SecurityObjects::UserInfo   Existing; | ||||||
|  |         if(!StorageService()->GetUserById(Id,Existing)) { | ||||||
|  |             return NotFound(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         SecurityObjects::UserInfo   NewUser; | ||||||
|  |         auto RawObject = ParseStream(); | ||||||
|  |         if(!NewUser.from_json(RawObject)) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::InvalidJSONDocument); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // some basic validations | ||||||
|  |         if(RawObject->has("userRole") && SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::UNKNOWN) { | ||||||
|  |             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // The only valid things to change are: changePassword, name, | ||||||
|  |         AssignIfPresent(RawObject,"name", Existing.name); | ||||||
|  |         AssignIfPresent(RawObject,"description", Existing.description); | ||||||
|  |         AssignIfPresent(RawObject,"owner", Existing.owner); | ||||||
|  |         AssignIfPresent(RawObject,"location", Existing.location); | ||||||
|  |         AssignIfPresent(RawObject,"locale", Existing.locale); | ||||||
|  |         AssignIfPresent(RawObject,"changePassword", Existing.changePassword); | ||||||
|  |         AssignIfPresent(RawObject,"suspended", Existing.suspended); | ||||||
|  |         AssignIfPresent(RawObject,"blackListed", Existing.blackListed); | ||||||
|  |  | ||||||
|  |         if(RawObject->has("userRole")) | ||||||
|  |             Existing.userRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()); | ||||||
|  |         if(RawObject->has("notes")) { | ||||||
|  |             SecurityObjects::NoteInfoVec NIV; | ||||||
|  |             NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString()); | ||||||
|  |             for(auto const &i:NIV) { | ||||||
|  |                 SecurityObjects::NoteInfo   ii{.created=(uint64_t)std::time(nullptr), .createdBy=UserInfo_.userinfo.email, .note=i.note}; | ||||||
|  |                 Existing.notes.push_back(ii); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if(RawObject->has("currentPassword")) { | ||||||
|  |             if(!AuthService()->ValidatePassword(RawObject->get("currentPassword").toString())) { | ||||||
|  |                 return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||||
|  |             } | ||||||
|  |             if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),Existing)) { | ||||||
|  |                 return BadRequest(RESTAPI::Errors::PasswordRejected); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(GetParameter("email_verification","false")=="true") { | ||||||
|  |             if(AuthService::VerifyEmail(Existing)) | ||||||
|  |                 Logger_.information(Poco::format("Verification e-mail requested for %s",Existing.email)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(RawObject->has("userTypeProprietaryInfo")) { | ||||||
|  |             Existing.userTypeProprietaryInfo.mfa.enabled = NewUser.userTypeProprietaryInfo.mfa.enabled; | ||||||
|  |             if(NewUser.userTypeProprietaryInfo.mfa.method=="sms") { | ||||||
|  |                 Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; | ||||||
|  |                 auto MobileStruct = RawObject->get("userTypeProprietaryInfo"); | ||||||
|  |                 auto Info = MobileStruct.extract<Poco::JSON::Object::Ptr>(); | ||||||
|  |                 if(Info->isArray("mobiles")) { | ||||||
|  |                     Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles; | ||||||
|  |                 } | ||||||
|  |                 if(!NewUser.userTypeProprietaryInfo.mobiles.empty() && !SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)){ | ||||||
|  |                     return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||||
|  |                 } | ||||||
|  |                 if(NewUser.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mobiles.empty()) { | ||||||
|  |                     return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||||
|  |                 } | ||||||
|  |             } else if(NewUser.userTypeProprietaryInfo.mfa.method=="email") { | ||||||
|  |                 Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; | ||||||
|  |             } else { | ||||||
|  |                 if(NewUser.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mfa.method.empty()) { | ||||||
|  |                     return BadRequest(RESTAPI::Errors::BadMFAMethod); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { | ||||||
|  |             SecurityObjects::UserInfo   NewUserInfo; | ||||||
|  |             StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); | ||||||
|  |             Poco::JSON::Object  ModifiedObject; | ||||||
|  |             NewUserInfo.to_json(ModifiedObject); | ||||||
|  |             return ReturnObject(ModifiedObject); | ||||||
|  |         } | ||||||
|  |         BadRequest(RESTAPI::Errors::RecordNotUpdated); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
| #ifndef UCENTRALSEC_RESTAPI_USER_HANDLER_H | #ifndef UCENTRALSEC_RESTAPI_USER_HANDLER_H | ||||||
| #define UCENTRALSEC_RESTAPI_USER_HANDLER_H | #define UCENTRALSEC_RESTAPI_USER_HANDLER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_user_handler : public RESTAPIHandler { |     class RESTAPI_user_handler : public RESTAPIHandler { | ||||||
| @@ -4,8 +4,8 @@ | |||||||
| 
 | 
 | ||||||
| #include "RESTAPI_users_handler.h" | #include "RESTAPI_users_handler.h" | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "RESTAPI_protocol.h" | #include "framework/RESTAPI_protocol.h" | ||||||
| #include "Utils.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     void RESTAPI_users_handler::DoGet() { |     void RESTAPI_users_handler::DoGet() { | ||||||
| @@ -15,7 +15,7 @@ namespace OpenWifi { | |||||||
|         if(QB_.Select.empty()) { |         if(QB_.Select.empty()) { | ||||||
|             Poco::JSON::Array ArrayObj; |             Poco::JSON::Array ArrayObj; | ||||||
|             Poco::JSON::Object Answer; |             Poco::JSON::Object Answer; | ||||||
|             if (Storage()->GetUsers(QB_.Offset, QB_.Limit, Users)) { |             if (StorageService()->GetUsers(QB_.Offset, QB_.Limit, Users)) { | ||||||
|                 for (const auto &i : Users) { |                 for (const auto &i : Users) { | ||||||
|                     Poco::JSON::Object Obj; |                     Poco::JSON::Object Obj; | ||||||
|                     if (IdOnly) { |                     if (IdOnly) { | ||||||
| @@ -27,14 +27,13 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|                 Answer.set(RESTAPI::Protocol::USERS, ArrayObj); |                 Answer.set(RESTAPI::Protocol::USERS, ArrayObj); | ||||||
|             } |             } | ||||||
|             ReturnObject(Answer); |             return ReturnObject(Answer); | ||||||
|             return; |  | ||||||
|         } else { |         } else { | ||||||
|             Types::StringVec IDs = Utils::Split(QB_.Select); |             Types::StringVec IDs = Utils::Split(QB_.Select); | ||||||
|             Poco::JSON::Array ArrayObj; |             Poco::JSON::Array ArrayObj; | ||||||
|             for(auto &i:IDs) { |             for(auto &i:IDs) { | ||||||
|                 SecurityObjects::UserInfo   UInfo; |                 SecurityObjects::UserInfo   UInfo; | ||||||
|                 if(Storage()->GetUserById(i,UInfo)) { |                 if(StorageService()->GetUserById(i,UInfo)) { | ||||||
|                     Poco::JSON::Object Obj; |                     Poco::JSON::Object Obj; | ||||||
|                     if (IdOnly) { |                     if (IdOnly) { | ||||||
|                         ArrayObj.add(UInfo.Id); |                         ArrayObj.add(UInfo.Id); | ||||||
| @@ -46,8 +45,7 @@ namespace OpenWifi { | |||||||
|             } |             } | ||||||
|             Poco::JSON::Object RetObj; |             Poco::JSON::Object RetObj; | ||||||
|             RetObj.set(RESTAPI::Protocol::USERS, ArrayObj); |             RetObj.set(RESTAPI::Protocol::USERS, ArrayObj); | ||||||
|             ReturnObject(RetObj); |             return ReturnObject(RetObj); | ||||||
|             return; |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
| #ifndef UCENTRALSEC_RESTAPI_USERS_HANDLER_H | #ifndef UCENTRALSEC_RESTAPI_USERS_HANDLER_H | ||||||
| #define UCENTRALSEC_RESTAPI_USERS_HANDLER_H | #define UCENTRALSEC_RESTAPI_USERS_HANDLER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_users_handler : public RESTAPIHandler { |     class RESTAPI_users_handler : public RESTAPIHandler { | ||||||
| @@ -3,9 +3,7 @@ | |||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_validateToken_handler.h" | #include "RESTAPI_validateToken_handler.h" | ||||||
| #include "Daemon.h" |  | ||||||
| #include "AuthService.h" | #include "AuthService.h" | ||||||
| #include "Utils.h" |  | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     void RESTAPI_validateToken_handler::DoGet() { |     void RESTAPI_validateToken_handler::DoGet() { | ||||||
| @@ -18,11 +16,10 @@ namespace OpenWifi { | |||||||
|                 if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo)) { |                 if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo)) { | ||||||
|                     Poco::JSON::Object Obj; |                     Poco::JSON::Object Obj; | ||||||
|                     SecObj.to_json(Obj); |                     SecObj.to_json(Obj); | ||||||
|                     ReturnObject(Obj); |                     return ReturnObject(Obj); | ||||||
|                     return; |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         NotFound(); |         return NotFound(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -5,7 +5,7 @@ | |||||||
| #ifndef UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H | #ifndef UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H | ||||||
| #define UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H | #define UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H | ||||||
| 
 | 
 | ||||||
| #include "RESTAPI_handler.h" | #include "framework/MicroService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     class RESTAPI_validateToken_handler : public RESTAPIHandler { |     class RESTAPI_validateToken_handler : public RESTAPIHandler { | ||||||
| @@ -1,5 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-09-15. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include "RESTAPI_GenericServer.h" |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-09-15. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef OWPROV_RESTAPI_GENERICSERVER_H |  | ||||||
| #define OWPROV_RESTAPI_GENERICSERVER_H |  | ||||||
|  |  | ||||||
| #include <vector> |  | ||||||
| #include <string> |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "Poco/StringTokenizer.h" |  | ||||||
| #include "Poco/Net/HTTPRequest.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_GenericServer { |  | ||||||
|     public: |  | ||||||
|  |  | ||||||
|         enum { |  | ||||||
|             LOG_GET=0, |  | ||||||
|             LOG_DELETE, |  | ||||||
|             LOG_PUT, |  | ||||||
|             LOG_POST |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         void inline SetFlags(bool External, const std::string &Methods) { |  | ||||||
|             Poco::StringTokenizer   Tokens(Methods,","); |  | ||||||
|             auto Offset = (External ? 0 : 4); |  | ||||||
|             for(const auto &i:Tokens) { |  | ||||||
|                 if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_DELETE)==0) |  | ||||||
|                     LogFlags_[Offset+LOG_DELETE]=true; |  | ||||||
|                 else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_PUT)==0) |  | ||||||
|                     LogFlags_[Offset+LOG_PUT]=true; |  | ||||||
|                 else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_POST)==0) |  | ||||||
|                     LogFlags_[Offset+LOG_POST]=true; |  | ||||||
|                 else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_GET)==0) |  | ||||||
|                     LogFlags_[Offset+LOG_GET]=true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         inline void InitLogging() { |  | ||||||
|             std::string Public = Daemon()->ConfigGetString("apilogging.public.methods","PUT,POST,DELETE"); |  | ||||||
|             SetFlags(true, Public); |  | ||||||
|             std::string Private = Daemon()->ConfigGetString("apilogging.private.methods","PUT,POST,DELETE"); |  | ||||||
|             SetFlags(false, Private); |  | ||||||
|  |  | ||||||
|             std::string PublicBadTokens = Daemon()->ConfigGetString("apilogging.public.badtokens.methods",""); |  | ||||||
|             LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0); |  | ||||||
|             std::string PrivateBadTokens = Daemon()->ConfigGetString("apilogging.private.badtokens.methods",""); |  | ||||||
|             LogBadTokens_[1] = (Poco::icompare(PrivateBadTokens,"true")==0); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [[nodiscard]] inline bool LogIt(const std::string &Method, bool External) const { |  | ||||||
|             auto Offset = (External ? 0 : 4); |  | ||||||
|             if(Method == Poco::Net::HTTPRequest::HTTP_GET) |  | ||||||
|                 return LogFlags_[Offset+LOG_GET]; |  | ||||||
|             if(Method == Poco::Net::HTTPRequest::HTTP_POST) |  | ||||||
|                 return LogFlags_[Offset+LOG_POST]; |  | ||||||
|             if(Method == Poco::Net::HTTPRequest::HTTP_PUT) |  | ||||||
|                 return LogFlags_[Offset+LOG_PUT]; |  | ||||||
|             if(Method == Poco::Net::HTTPRequest::HTTP_DELETE) |  | ||||||
|                 return LogFlags_[Offset+LOG_DELETE]; |  | ||||||
|             return false; |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         [[nodiscard]] inline bool LogBadTokens(bool External) const { |  | ||||||
|             return LogBadTokens_[ (External ? 0 : 1) ]; |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|     private: |  | ||||||
|         std::array<bool,8>       LogFlags_{false}; |  | ||||||
|         std::array<bool,2>       LogBadTokens_{false}; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #endif //OWPROV_RESTAPI_GENERICSERVER_H |  | ||||||
| @@ -1,80 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-06-29. |  | ||||||
| // |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #include "Poco/URI.h" |  | ||||||
|  |  | ||||||
| #include "RESTAPI_system_command.h" |  | ||||||
| #include "RESTAPI_user_handler.h" |  | ||||||
| #include "RESTAPI_users_handler.h" |  | ||||||
| #include "RESTAPI_action_links.h" |  | ||||||
| #include "RESTAPI_validateToken_handler.h" |  | ||||||
| #include "RESTAPI_InternalServer.h" |  | ||||||
|  |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_InternalServer *RESTAPI_InternalServer::instance_ = nullptr; |  | ||||||
|  |  | ||||||
|     RESTAPI_InternalServer::RESTAPI_InternalServer() noexcept: |  | ||||||
|         SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi") |  | ||||||
|     { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     int RESTAPI_InternalServer::Start() { |  | ||||||
|         Logger_.information("Starting."); |  | ||||||
|         Server_.InitLogging(); |  | ||||||
|  |  | ||||||
|         for(const auto & Svr: ConfigServersList_) { |  | ||||||
|             Logger_.information(Poco::format("Starting: %s:%s Keyfile:%s CertFile: %s", Svr.Address(), std::to_string(Svr.Port()), |  | ||||||
|                                              Svr.KeyFile(),Svr.CertFile())); |  | ||||||
|  |  | ||||||
|             auto Sock{Svr.CreateSecureSocket(Logger_)}; |  | ||||||
|  |  | ||||||
|             Svr.LogCert(Logger_); |  | ||||||
|             if(!Svr.RootCA().empty()) |  | ||||||
|                 Svr.LogCas(Logger_); |  | ||||||
|  |  | ||||||
|             auto Params = new Poco::Net::HTTPServerParams; |  | ||||||
|             Params->setMaxThreads(50); |  | ||||||
|             Params->setMaxQueued(200); |  | ||||||
|             Params->setKeepAlive(true); |  | ||||||
|  |  | ||||||
|             auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new InternalRequestHandlerFactory(Server_), Pool_, Sock, Params); |  | ||||||
|             NewServer->start(); |  | ||||||
|             RESTServers_.push_back(std::move(NewServer)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_InternalServer::Stop() { |  | ||||||
|         Logger_.information("Stopping "); |  | ||||||
|         for( const auto & svr : RESTServers_ ) |  | ||||||
|             svr->stop(); |  | ||||||
|         RESTServers_.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_InternalServer::reinitialize(Poco::Util::Application &self) { |  | ||||||
|         Daemon()->LoadConfigurationFile(); |  | ||||||
|         Logger_.information("Reinitializing."); |  | ||||||
|         Stop(); |  | ||||||
|         Start(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Poco::Net::HTTPRequestHandler *InternalRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) { |  | ||||||
|         Poco::URI uri(Request.getURI()); |  | ||||||
|         const auto & Path = uri.getPath(); |  | ||||||
|         RESTAPIHandler::BindingMap Bindings; |  | ||||||
|         return RESTAPI_Router_I< |  | ||||||
|                 RESTAPI_users_handler, |  | ||||||
|                 RESTAPI_user_handler, |  | ||||||
|                 RESTAPI_system_command, |  | ||||||
|                 RESTAPI_action_links, |  | ||||||
|                 RESTAPI_validateToken_handler |  | ||||||
|         >(Path,Bindings,Logger_, Server_); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| } |  | ||||||
| @@ -1,57 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-06-29. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALSEC_RESTAPI_INTERNALSERVER_H |  | ||||||
| #define UCENTRALSEC_RESTAPI_INTERNALSERVER_H |  | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
| #include "Poco/Net/HTTPServer.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandler.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandlerFactory.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Net/NetException.h" |  | ||||||
| #include "RESTAPI_GenericServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_InternalServer : public SubSystemServer { |  | ||||||
|         public: |  | ||||||
|             RESTAPI_InternalServer() noexcept; |  | ||||||
|  |  | ||||||
|             static RESTAPI_InternalServer *instance() { |  | ||||||
|                 if (instance_ == nullptr) { |  | ||||||
|                     instance_ = new RESTAPI_InternalServer; |  | ||||||
|                 } |  | ||||||
|                 return instance_; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             int Start() override; |  | ||||||
|             void Stop() override; |  | ||||||
|             void reinitialize(Poco::Util::Application &self) override; |  | ||||||
|  |  | ||||||
|         private: |  | ||||||
|             static RESTAPI_InternalServer *instance_; |  | ||||||
|             std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; |  | ||||||
|             Poco::ThreadPool	    Pool_; |  | ||||||
|             RESTAPI_GenericServer   Server_; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); }; |  | ||||||
|  |  | ||||||
|     class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { |  | ||||||
|         public: |  | ||||||
|         explicit InternalRequestHandlerFactory(RESTAPI_GenericServer & Server) : |  | ||||||
|                     Logger_(RESTAPI_InternalServer()->Logger()), |  | ||||||
|                     Server_(Server){} |  | ||||||
|  |  | ||||||
|             Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override; |  | ||||||
|         private: |  | ||||||
|             Poco::Logger    & Logger_; |  | ||||||
|             RESTAPI_GenericServer & Server_; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } //   namespace |  | ||||||
|  |  | ||||||
| #endif //UCENTRALSEC_RESTAPI_INTERNALSERVER_H |  | ||||||
| @@ -1,479 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include <cctype> |  | ||||||
| #include <algorithm> |  | ||||||
| #include <functional> |  | ||||||
| #include <iostream> |  | ||||||
| #include <iterator> |  | ||||||
| #include <future> |  | ||||||
| #include <chrono> |  | ||||||
|  |  | ||||||
| #include "Poco/URI.h" |  | ||||||
| #include "Poco/Net/OAuth20Credentials.h" |  | ||||||
|  |  | ||||||
| #include "RESTAPI_errors.h" |  | ||||||
|  |  | ||||||
| #ifdef	TIP_SECURITY_SERVICE |  | ||||||
| #include "AuthService.h" |  | ||||||
| #else |  | ||||||
| #include "AuthClient.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include "RESTAPI_handler.h" |  | ||||||
| #include "RESTAPI_protocol.h" |  | ||||||
| #include "Utils.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     void RESTAPIHandler::handleRequest(Poco::Net::HTTPServerRequest &RequestIn, |  | ||||||
|                        Poco::Net::HTTPServerResponse &ResponseIn) { |  | ||||||
| 		try { |  | ||||||
| 			Request = &RequestIn; |  | ||||||
| 			Response = &ResponseIn; |  | ||||||
|  |  | ||||||
| 			if (!ContinueProcessing()) |  | ||||||
| 				return; |  | ||||||
|  |  | ||||||
| 			if (AlwaysAuthorize_ && !IsAuthorized()) |  | ||||||
| 				return; |  | ||||||
|  |  | ||||||
| 			ParseParameters(); |  | ||||||
| 			if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET) |  | ||||||
| 				DoGet(); |  | ||||||
| 			else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST) |  | ||||||
| 				DoPost(); |  | ||||||
| 			else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) |  | ||||||
| 				DoDelete(); |  | ||||||
| 			else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) |  | ||||||
| 				DoPut(); |  | ||||||
| 			else |  | ||||||
| 				BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); |  | ||||||
| 			return; |  | ||||||
| 		} catch (const Poco::Exception &E) { |  | ||||||
| 			Logger_.log(E); |  | ||||||
| 			BadRequest(RESTAPI::Errors::InternalError); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     const Poco::JSON::Object::Ptr &RESTAPIHandler::ParseStream() { |  | ||||||
|         return IncomingParser_.parse(Request->stream()).extract<Poco::JSON::Object::Ptr>(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::ParseBindings(const std::string & Request, const std::list<const char *> & EndPoints, BindingMap &bindings) { |  | ||||||
| 		bindings.clear(); |  | ||||||
| 		std::vector<std::string> PathItems = Utils::Split(Request, '/'); |  | ||||||
|  |  | ||||||
| 		for(const auto &EndPoint:EndPoints) { |  | ||||||
| 			std::vector<std::string> ParamItems = Utils::Split(EndPoint, '/'); |  | ||||||
| 			if (PathItems.size() != ParamItems.size()) |  | ||||||
| 				continue; |  | ||||||
|  |  | ||||||
| 			bool Matched = true; |  | ||||||
| 			for (auto i = 0; i != PathItems.size() && Matched; i++) { |  | ||||||
| 				if (PathItems[i] != ParamItems[i]) { |  | ||||||
| 					if (ParamItems[i][0] == '{') { |  | ||||||
| 						auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2); |  | ||||||
| 						bindings[Poco::toLower(ParamName)] = PathItems[i]; |  | ||||||
| 					} else { |  | ||||||
| 						Matched = false; |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			if(Matched) |  | ||||||
| 				return true; |  | ||||||
| 		} |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::PrintBindings() { |  | ||||||
| 		for (const auto &[key, value] : Bindings_) |  | ||||||
| 			std::cout << "Key = " << key << "  Value= " << value << std::endl; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::ParseParameters() { |  | ||||||
| 		Poco::URI uri(Request->getURI()); |  | ||||||
| 		Parameters_ = uri.getQueryParameters(); |  | ||||||
| 		InitQueryBlock(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static bool is_number(const std::string &s) { |  | ||||||
| 		return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static bool is_bool(const std::string &s) { |  | ||||||
| 		if (s == "true" || s == "false") |  | ||||||
| 			return true; |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t RESTAPIHandler::GetParameter(const std::string &Name, const uint64_t Default) { |  | ||||||
|         auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; }); |  | ||||||
|         if(Hint==Parameters_.end() || !is_number(Hint->second)) |  | ||||||
|             return Default; |  | ||||||
|         return std::stoull(Hint->second); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::GetBoolParameter(const std::string &Name, bool Default) { |  | ||||||
|         auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; }); |  | ||||||
|         if(Hint==end(Parameters_) || !is_bool(Hint->second)) |  | ||||||
|             return Default; |  | ||||||
| 		return Hint->second=="true"; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string RESTAPIHandler::GetParameter(const std::string &Name, const std::string &Default) { |  | ||||||
|         auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; }); |  | ||||||
|         if(Hint==end(Parameters_)) |  | ||||||
|             return Default; |  | ||||||
|         return Hint->second; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::HasParameter(const std::string &Name, std::string &Value) { |  | ||||||
|         auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; }); |  | ||||||
|         if(Hint==end(Parameters_)) |  | ||||||
|             return false; |  | ||||||
|         Value = Hint->second; |  | ||||||
|         return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::HasParameter(const std::string &Name, uint64_t & Value) { |  | ||||||
|         auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[Name](const std::pair<std::string,std::string> &S){ return S.first==Name; }); |  | ||||||
|         if(Hint==end(Parameters_)) |  | ||||||
|             return false; |  | ||||||
|         Value = std::stoull(Hint->second); |  | ||||||
|         return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const std::string &RESTAPIHandler::GetBinding(const std::string &Name, const std::string &Default) { |  | ||||||
| 		auto E = Bindings_.find(Poco::toLower(Name)); |  | ||||||
| 		if (E == Bindings_.end()) |  | ||||||
| 			return Default; |  | ||||||
|  |  | ||||||
| 		return E->second; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static std::string MakeList(const std::vector<std::string> &L) { |  | ||||||
| 		std::string Return; |  | ||||||
| 		for (const auto &i : L) |  | ||||||
| 			if (Return.empty()) |  | ||||||
| 				Return = i; |  | ||||||
| 			else |  | ||||||
| 				Return += ", " + i; |  | ||||||
|  |  | ||||||
| 		return Return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value) { |  | ||||||
| 	    if(O->has(Field)) { |  | ||||||
| 	        Value = O->get(Field).toString(); |  | ||||||
| 	        return true; |  | ||||||
| 	    } |  | ||||||
| 	    return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value) { |  | ||||||
| 	    if(O->has(Field)) { |  | ||||||
| 	        Value = O->get(Field); |  | ||||||
| 	        return true; |  | ||||||
| 	    } |  | ||||||
| 	    return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::AddCORS() { |  | ||||||
| 		auto Origin = Request->find("Origin"); |  | ||||||
| 		if (Origin != Request->end()) { |  | ||||||
| 			Response->set("Access-Control-Allow-Origin", Origin->second); |  | ||||||
| 			Response->set("Vary", "Origin"); |  | ||||||
| 		} else { |  | ||||||
| 			Response->set("Access-Control-Allow-Origin", "*"); |  | ||||||
| 		} |  | ||||||
| 		Response->set("Access-Control-Allow-Headers", "*"); |  | ||||||
| 		Response->set("Access-Control-Allow-Methods", MakeList(Methods_)); |  | ||||||
| 		Response->set("Access-Control-Max-Age", "86400"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::SetCommonHeaders(bool CloseConnection) { |  | ||||||
| 		Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1); |  | ||||||
| 		Response->setChunkedTransferEncoding(true); |  | ||||||
| 		Response->setContentType("application/json"); |  | ||||||
| 		if(CloseConnection) { |  | ||||||
| 			Response->set("Connection", "close"); |  | ||||||
| 			Response->setKeepAlive(false); |  | ||||||
| 		} else { |  | ||||||
| 			Response->setKeepAlive(true); |  | ||||||
| 			Response->set("Connection", "Keep-Alive"); |  | ||||||
| 			Response->set("Keep-Alive", "timeout=5, max=1000"); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::ProcessOptions() { |  | ||||||
| 		AddCORS(); |  | ||||||
| 		SetCommonHeaders(); |  | ||||||
| 		Response->setContentLength(0); |  | ||||||
| 		Response->set("Access-Control-Allow-Credentials", "true"); |  | ||||||
| 		Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK); |  | ||||||
| 		Response->set("Vary", "Origin, Access-Control-Request-Headers, Access-Control-Request-Method"); |  | ||||||
| 		Response->send(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::PrepareResponse( Poco::Net::HTTPResponse::HTTPStatus Status, |  | ||||||
| 										 bool CloseConnection) { |  | ||||||
| 		Response->setStatus(Status); |  | ||||||
| 		AddCORS(); |  | ||||||
| 		SetCommonHeaders(CloseConnection); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::BadRequest(const std::string & Reason) { |  | ||||||
| 		PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST); |  | ||||||
| 		Poco::JSON::Object	ErrorObject; |  | ||||||
| 		ErrorObject.set("ErrorCode",400); |  | ||||||
| 		ErrorObject.set("ErrorDetails",Request->getMethod()); |  | ||||||
| 		ErrorObject.set("ErrorDescription",Reason.empty() ? "Command is missing parameters or wrong values." : Reason) ; |  | ||||||
| 		std::ostream &Answer = Response->send(); |  | ||||||
| 		Poco::JSON::Stringifier::stringify(ErrorObject, Answer); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::InternalError(const std::string & Reason) { |  | ||||||
|         PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR); |  | ||||||
|         Poco::JSON::Object	ErrorObject; |  | ||||||
|         ErrorObject.set("ErrorCode",500); |  | ||||||
|         ErrorObject.set("ErrorDetails",Request->getMethod()); |  | ||||||
|         ErrorObject.set("ErrorDescription",Reason.empty() ? "Please try later or review the data submitted." : Reason) ; |  | ||||||
|         std::ostream &Answer = Response->send(); |  | ||||||
|         Poco::JSON::Stringifier::stringify(ErrorObject, Answer); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::UnAuthorized(const std::string & Reason) { |  | ||||||
| 		PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); |  | ||||||
| 		Poco::JSON::Object	ErrorObject; |  | ||||||
| 		ErrorObject.set("ErrorCode",403); |  | ||||||
| 		ErrorObject.set("ErrorDetails",Request->getMethod()); |  | ||||||
| 		ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ; |  | ||||||
| 		std::ostream &Answer = Response->send(); |  | ||||||
| 		Poco::JSON::Stringifier::stringify(ErrorObject, Answer); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::NotFound() { |  | ||||||
| 		PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND); |  | ||||||
| 		Poco::JSON::Object	ErrorObject; |  | ||||||
| 		ErrorObject.set("ErrorCode",404); |  | ||||||
| 		ErrorObject.set("ErrorDetails",Request->getMethod()); |  | ||||||
| 		ErrorObject.set("ErrorDescription","This resource does not exist."); |  | ||||||
| 		std::ostream &Answer = Response->send(); |  | ||||||
| 		Poco::JSON::Stringifier::stringify(ErrorObject, Answer); |  | ||||||
| 		Logger_.debug(Poco::format("RES-NOTFOUND: User='%s' Method='%s' Path='%s", |  | ||||||
|                                    Utils::FormatIPv6(Request->clientAddress().toString()), |  | ||||||
|                                    Request->getMethod(), |  | ||||||
|                                    Request->getURI())); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::OK() { |  | ||||||
| 		PrepareResponse(); |  | ||||||
| 		if(	Request->getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE || |  | ||||||
| 			Request->getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) { |  | ||||||
| 			Response->send(); |  | ||||||
| 		} else { |  | ||||||
| 			Poco::JSON::Object ErrorObject; |  | ||||||
| 			ErrorObject.set("Code", 0); |  | ||||||
| 			ErrorObject.set("Operation", Request->getMethod()); |  | ||||||
| 			ErrorObject.set("Details", "Command completed."); |  | ||||||
| 			std::ostream &Answer = Response->send(); |  | ||||||
| 			Poco::JSON::Stringifier::stringify(ErrorObject, Answer); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::SendFile(Poco::File & File, const std::string & UUID) { |  | ||||||
| 		Response->set("Content-Type","application/octet-stream"); |  | ||||||
| 		Response->set("Content-Disposition", "attachment; filename=" + UUID ); |  | ||||||
| 		Response->set("Content-Transfer-Encoding","binary"); |  | ||||||
| 		Response->set("Accept-Ranges", "bytes"); |  | ||||||
| 		Response->set("Cache-Control", "private"); |  | ||||||
| 		Response->set("Pragma", "private"); |  | ||||||
| 		Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); |  | ||||||
| 		Response->set("Content-Length", std::to_string(File.getSize())); |  | ||||||
| 		AddCORS(); |  | ||||||
| 		Response->sendFile(File.path(),"application/octet-stream"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void RESTAPIHandler::SendFile(Poco::File & File) { |  | ||||||
|         Poco::Path  P(File.path()); |  | ||||||
|         auto MT = Utils::FindMediaType(File); |  | ||||||
|         if(MT.Encoding==Utils::BINARY) { |  | ||||||
|             Response->set("Content-Transfer-Encoding","binary"); |  | ||||||
|             Response->set("Accept-Ranges", "bytes"); |  | ||||||
|         } |  | ||||||
|         Response->set("Cache-Control", "private"); |  | ||||||
|         Response->set("Pragma", "private"); |  | ||||||
|         Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); |  | ||||||
|         AddCORS(); |  | ||||||
|         Response->sendFile(File.path(),MT.ContentType); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPIHandler::SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name) { |  | ||||||
|         auto MT = Utils::FindMediaType(Name); |  | ||||||
|         if(MT.Encoding==Utils::BINARY) { |  | ||||||
|             Response->set("Content-Transfer-Encoding","binary"); |  | ||||||
|             Response->set("Accept-Ranges", "bytes"); |  | ||||||
|         } |  | ||||||
|         Response->set("Content-Disposition", "attachment; filename=" + Name ); |  | ||||||
|         Response->set("Accept-Ranges", "bytes"); |  | ||||||
|         Response->set("Cache-Control", "private"); |  | ||||||
|         Response->set("Pragma", "private"); |  | ||||||
|         Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); |  | ||||||
|         AddCORS(); |  | ||||||
|         Response->sendFile(TempAvatar.path(),MT.ContentType); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void RESTAPIHandler::SendHTMLFileBack(Poco::File & File, |  | ||||||
|                           const Types::StringPairVec & FormVars) { |  | ||||||
|         Response->set("Pragma", "private"); |  | ||||||
|         Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); |  | ||||||
|         Response->set("Content-Length", std::to_string(File.getSize())); |  | ||||||
|         AddCORS(); |  | ||||||
|         auto FormContent = Utils::LoadFile(File.path()); |  | ||||||
|         Utils::ReplaceVariables(FormContent, FormVars); |  | ||||||
|         Response->setChunkedTransferEncoding(true); |  | ||||||
|         Response->setContentType("text/html"); |  | ||||||
|         std::ostream& ostr = Response->send(); |  | ||||||
|         ostr << FormContent; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void RESTAPIHandler::ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection) { |  | ||||||
| 		PrepareResponse(Status, CloseConnection); |  | ||||||
| 		if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) { |  | ||||||
| 			Response->setContentLength(0); |  | ||||||
| 			Response->erase("Content-Type"); |  | ||||||
| 			Response->setChunkedTransferEncoding(false); |  | ||||||
| 		} |  | ||||||
| 		Response->send(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::ContinueProcessing() { |  | ||||||
| 		if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) { |  | ||||||
| 			ProcessOptions(); |  | ||||||
| 			return false; |  | ||||||
| 		} else if (std::find(Methods_.begin(), Methods_.end(), Request->getMethod()) == Methods_.end()) { |  | ||||||
| 			BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::IsAuthorized() { |  | ||||||
| 	    if(Internal_) { |  | ||||||
| 	        auto Allowed = Daemon()->IsValidAPIKEY(*Request); |  | ||||||
| 	        if(!Allowed) { |  | ||||||
| 	            if(Server_.LogBadTokens(false)) { |  | ||||||
| 	                Logger_.debug(Poco::format("I-REQ-DENIED(%s): Method='%s' Path='%s", |  | ||||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), |  | ||||||
|                                                Request->getMethod(), Request->getURI())); |  | ||||||
| 	            } |  | ||||||
| 	        } else { |  | ||||||
| 	            auto Id = Request->get("X-INTERNAL-NAME", "unknown"); |  | ||||||
| 	            if(Server_.LogIt(Request->getMethod(),true)) { |  | ||||||
| 	                Logger_.debug(Poco::format("I-REQ-ALLOWED(%s): User='%s' Method='%s' Path='%s", |  | ||||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), Id, |  | ||||||
|                                                Request->getMethod(), Request->getURI())); |  | ||||||
| 	            } |  | ||||||
| 	        } |  | ||||||
|             return Allowed; |  | ||||||
| 	    } else { |  | ||||||
|             if (SessionToken_.empty()) { |  | ||||||
|                 try { |  | ||||||
|                     Poco::Net::OAuth20Credentials Auth(*Request); |  | ||||||
|                     if (Auth.getScheme() == "Bearer") { |  | ||||||
|                         SessionToken_ = Auth.getBearerToken(); |  | ||||||
|                     } |  | ||||||
|                 } catch (const Poco::Exception &E) { |  | ||||||
|                     Logger_.log(E); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| #ifdef    TIP_SECURITY_SERVICE |  | ||||||
|             if (AuthService()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { |  | ||||||
| #else |  | ||||||
|             if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { |  | ||||||
| #endif |  | ||||||
|                 if(Server_.LogIt(Request->getMethod(),true)) { |  | ||||||
|                     Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s", |  | ||||||
|                          Utils::FormatIPv6(Request->clientAddress().toString()), |  | ||||||
|                          UserInfo_.userinfo.email, |  | ||||||
|                          Request->clientAddress().toString(), |  | ||||||
|                          Request->getMethod(), |  | ||||||
|                          Request->getURI())); |  | ||||||
|                 } |  | ||||||
|                 return true; |  | ||||||
|             } else { |  | ||||||
|                 if(Server_.LogBadTokens(true)) { |  | ||||||
|                     Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s", |  | ||||||
|                          Utils::FormatIPv6(Request->clientAddress().toString()), |  | ||||||
|                          Request->getMethod(), Request->getURI())); |  | ||||||
|                 } |  | ||||||
|                 UnAuthorized(); |  | ||||||
|             } |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::ReturnObject(Poco::JSON::Object &Object) { |  | ||||||
| 		PrepareResponse(); |  | ||||||
| 		std::ostream &Answer = Response->send(); |  | ||||||
| 		Poco::JSON::Stringifier::stringify(Object, Answer); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPIHandler::ReturnCountOnly(uint64_t Count) { |  | ||||||
| 	    Poco::JSON::Object  Answer; |  | ||||||
| 	    Answer.set("count", Count); |  | ||||||
|         ReturnObject(Answer); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool RESTAPIHandler::InitQueryBlock() { |  | ||||||
| 	    if(QueryBlockInitialized_) |  | ||||||
| 	        return true; |  | ||||||
| 	    QueryBlockInitialized_=true; |  | ||||||
| 		QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, ""); |  | ||||||
| 		QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0); |  | ||||||
| 		QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0); |  | ||||||
| 		QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 1); |  | ||||||
| 		QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); |  | ||||||
| 		QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); |  | ||||||
| 		QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, ""); |  | ||||||
| 		QB_.Lifetime = GetBoolParameter(RESTAPI::Protocol::LIFETIME,false); |  | ||||||
| 		QB_.LogType = GetParameter(RESTAPI::Protocol::LOGTYPE,0); |  | ||||||
| 		QB_.LastOnly = GetBoolParameter(RESTAPI::Protocol::LASTONLY,false); |  | ||||||
| 		QB_.Newest = GetBoolParameter(RESTAPI::Protocol::NEWEST,false); |  | ||||||
| 		QB_.CountOnly = GetBoolParameter(RESTAPI::Protocol::COUNTONLY,false); |  | ||||||
|  |  | ||||||
| 		if(QB_.Offset<1) |  | ||||||
| 		    QB_.Offset=1; |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] uint64_t RESTAPIHandler::Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default){ |  | ||||||
| 		if(Obj->has(Parameter)) |  | ||||||
| 			return Obj->get(Parameter); |  | ||||||
| 		return Default; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string RESTAPIHandler::GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default){ |  | ||||||
| 		if(Obj->has(Parameter)) |  | ||||||
| 			return Obj->get(Parameter).toString(); |  | ||||||
| 		return Default; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool RESTAPIHandler::GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default){ |  | ||||||
| 		if(Obj->has(Parameter)) |  | ||||||
| 			return Obj->get(Parameter).toString()=="true"; |  | ||||||
| 		return Default; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] uint64_t RESTAPIHandler::GetWhen(const Poco::JSON::Object::Ptr &Obj) { |  | ||||||
| 		return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,233 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRAL_RESTAPI_HANDLER_H |  | ||||||
| #define UCENTRAL_RESTAPI_HANDLER_H |  | ||||||
|  |  | ||||||
| #include "Poco/URI.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandler.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandlerFactory.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Net/HTTPServerResponse.h" |  | ||||||
| #include "Poco/Net/NetException.h" |  | ||||||
| #include "Poco/Net/PartHandler.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Logger.h" |  | ||||||
| #include "Poco/File.h" |  | ||||||
| #include "Poco/TemporaryFile.h" |  | ||||||
| #include "Poco/JSON/Object.h" |  | ||||||
| #include "Poco/CountingStream.h" |  | ||||||
| #include "Poco/NullStream.h" |  | ||||||
|  |  | ||||||
| #include "RESTAPI_SecurityObjects.h" |  | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
| #include "RESTAPI_GenericServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_PartHandler: public Poco::Net::PartHandler |  | ||||||
|     { |  | ||||||
|     public: |  | ||||||
|         RESTAPI_PartHandler(): |  | ||||||
|                 _length(0) |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override |  | ||||||
|         { |  | ||||||
|             _type = header.get("Content-Type", "(unspecified)"); |  | ||||||
|             if (header.has("Content-Disposition")) |  | ||||||
|             { |  | ||||||
|                 std::string disp; |  | ||||||
|                 Poco::Net::NameValueCollection params; |  | ||||||
|                 Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params); |  | ||||||
|                 _name = params.get("name", "(unnamed)"); |  | ||||||
|                 _fileName = params.get("filename", "(unnamed)"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             Poco::CountingInputStream istr(stream); |  | ||||||
|             Poco::NullOutputStream ostr; |  | ||||||
|             Poco::StreamCopier::copyStream(istr, ostr); |  | ||||||
|             _length = (int)istr.chars(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [[nodiscard]] int length() const |  | ||||||
|         { |  | ||||||
|             return _length; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [[nodiscard]] const std::string& name() const |  | ||||||
|         { |  | ||||||
|             return _name; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [[nodiscard]] const std::string& fileName() const |  | ||||||
|         { |  | ||||||
|             return _fileName; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [[nodiscard]] const std::string& contentType() const |  | ||||||
|         { |  | ||||||
|             return _type; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     private: |  | ||||||
|         int _length; |  | ||||||
|         std::string _type; |  | ||||||
|         std::string _name; |  | ||||||
|         std::string _fileName; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { |  | ||||||
| 	  public: |  | ||||||
| 		struct QueryBlock { |  | ||||||
| 			uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ; |  | ||||||
| 			std::string SerialNumber, Filter, Select; |  | ||||||
| 			bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false; |  | ||||||
| 		}; |  | ||||||
|  |  | ||||||
| 		typedef std::map<std::string, std::string> BindingMap; |  | ||||||
|  |  | ||||||
| 		RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods, RESTAPI_GenericServer & Server, bool Internal=false, bool AlwaysAuthorize=true) |  | ||||||
| 		: Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Server_(Server), Internal_(Internal), AlwaysAuthorize_(AlwaysAuthorize) {} |  | ||||||
|  |  | ||||||
| 		static bool ParseBindings(const std::string & Request, const std::list<const char *> & EndPoints, BindingMap &Keys); |  | ||||||
| 		void PrintBindings(); |  | ||||||
| 		void ParseParameters(); |  | ||||||
|  |  | ||||||
| 		void AddCORS(); |  | ||||||
| 	 	void SetCommonHeaders(bool CloseConnection=false); |  | ||||||
| 		void ProcessOptions(); |  | ||||||
| 		void |  | ||||||
| 		PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK, |  | ||||||
| 						bool CloseConnection = false); |  | ||||||
| 		bool ContinueProcessing(); |  | ||||||
| 		bool IsAuthorized(); |  | ||||||
|  |  | ||||||
| 		uint64_t GetParameter(const std::string &Name, uint64_t Default); |  | ||||||
| 		std::string GetParameter(const std::string &Name, const std::string &Default); |  | ||||||
| 		bool GetBoolParameter(const std::string &Name, bool Default); |  | ||||||
|  |  | ||||||
| 		void BadRequest(const std::string &Reason ); |  | ||||||
| 		void InternalError(const std::string &Reason = ""); |  | ||||||
| 		void UnAuthorized(const std::string &Reason = ""); |  | ||||||
| 		void ReturnObject(Poco::JSON::Object &Object); |  | ||||||
| 		void NotFound(); |  | ||||||
| 		void OK(); |  | ||||||
| 		void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, |  | ||||||
| 						  bool CloseConnection=false); |  | ||||||
| 		void SendFile(Poco::File & File, const std::string & UUID); |  | ||||||
| 		void SendHTMLFileBack(Poco::File & File, |  | ||||||
|                               const Types::StringPairVec & FormVars); |  | ||||||
|         void SendFile(Poco::TemporaryFile &TempAvatar, const std::string &Type, const std::string & Name); |  | ||||||
|  |  | ||||||
|         void SendFile(Poco::File & File); |  | ||||||
|  |  | ||||||
|         const std::string &GetBinding(const std::string &Name, const std::string &Default); |  | ||||||
| 		bool InitQueryBlock(); |  | ||||||
|  |  | ||||||
| 		void ReturnCountOnly(uint64_t Count); |  | ||||||
|  |  | ||||||
| 		[[nodiscard]] static uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0); |  | ||||||
| 		[[nodiscard]] static std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default=""); |  | ||||||
| 		[[nodiscard]] static bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false); |  | ||||||
| 		[[nodiscard]] static uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj); |  | ||||||
| 		bool HasParameter(const std::string &QueryParameter, std::string &Value); |  | ||||||
| 		bool HasParameter(const std::string &QueryParameter, uint64_t & Value); |  | ||||||
|  |  | ||||||
| 		static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value); |  | ||||||
| 		static bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value); |  | ||||||
|  |  | ||||||
| 		template<typename T> void ReturnObject(const char *Name, const std::vector<T> & Objects) { |  | ||||||
| 		    Poco::JSON::Object  Answer; |  | ||||||
| 		    RESTAPI_utils::field_to_json(Answer,Name,Objects); |  | ||||||
|             ReturnObject(Answer); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Poco::Logger & Logger() { return Logger_; } |  | ||||||
|  |  | ||||||
| 		void handleRequest(Poco::Net::HTTPServerRequest &request, |  | ||||||
|                            Poco::Net::HTTPServerResponse &response) final; |  | ||||||
|  |  | ||||||
| 		virtual void DoGet() = 0 ; |  | ||||||
| 		virtual void DoDelete() = 0 ; |  | ||||||
| 		virtual void DoPost() = 0 ; |  | ||||||
| 		virtual void DoPut() = 0 ; |  | ||||||
|  |  | ||||||
| 		const Poco::JSON::Object::Ptr & ParseStream(); |  | ||||||
|  |  | ||||||
| 	  protected: |  | ||||||
| 		BindingMap 					Bindings_; |  | ||||||
| 		Poco::URI::QueryParameters 	Parameters_; |  | ||||||
| 		Poco::Logger 				&Logger_; |  | ||||||
| 		std::string 				SessionToken_; |  | ||||||
| 		SecurityObjects::UserInfoAndPolicy 	UserInfo_; |  | ||||||
| 		std::vector<std::string> 	Methods_; |  | ||||||
| 		QueryBlock					QB_; |  | ||||||
| 		bool                        Internal_=false; |  | ||||||
| 		bool                        QueryBlockInitialized_=false; |  | ||||||
| 		Poco::Net::HTTPServerRequest    *Request= nullptr; |  | ||||||
| 		Poco::Net::HTTPServerResponse   *Response= nullptr; |  | ||||||
| 		bool                        AlwaysAuthorize_=true; |  | ||||||
| 		Poco::JSON::Parser          IncomingParser_; |  | ||||||
| 		RESTAPI_GenericServer       & Server_; |  | ||||||
| 	}; |  | ||||||
|  |  | ||||||
| 	class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { |  | ||||||
| 	  public: |  | ||||||
| 		RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server) |  | ||||||
| 			: RESTAPIHandler(bindings, L, std::vector<std::string>{}, Server) {} |  | ||||||
|         inline void DoGet() override {}; |  | ||||||
| 		inline void DoPost() override {}; |  | ||||||
| 		inline void DoPut() override {}; |  | ||||||
| 		inline void DoDelete() override {}; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| 	template<class T> |  | ||||||
| 	constexpr auto test_has_PathName_method(T*) |  | ||||||
| 	-> decltype(  T::PathName() , std::true_type{} ) |  | ||||||
| 	{ |  | ||||||
| 		return std::true_type{}; |  | ||||||
| 	} |  | ||||||
| 	constexpr auto test_has_PathName_method(...) -> std::false_type |  | ||||||
| 	{ |  | ||||||
| 		return std::false_type{}; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<typename T, typename... Args> |  | ||||||
| 	RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) { |  | ||||||
| 		static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); |  | ||||||
| 		if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { |  | ||||||
| 			return new T(Bindings, Logger, Server, false); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if constexpr (sizeof...(Args) == 0) { |  | ||||||
| 			return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); |  | ||||||
| 		} else { |  | ||||||
| 			return RESTAPI_Router<Args...>(RequestedPath, Bindings, Logger, Server); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     template<typename T, typename... Args> |  | ||||||
|     RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger, RESTAPI_GenericServer & Server) { |  | ||||||
|         static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method."); |  | ||||||
|         if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) { |  | ||||||
|             return new T(Bindings, Logger, Server, true); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if constexpr (sizeof...(Args) == 0) { |  | ||||||
|             return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server); |  | ||||||
|         } else { |  | ||||||
|             return RESTAPI_Router_I<Args...>(RequestedPath, Bindings, Logger, Server); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif //UCENTRAL_RESTAPI_HANDLER_H |  | ||||||
| @@ -1,100 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include "Poco/JSON/Parser.h" |  | ||||||
|  |  | ||||||
| #include "AuthService.h" |  | ||||||
| #include "RESTAPI_oauth2Handler.h" |  | ||||||
| #include "RESTAPI_protocol.h" |  | ||||||
| #include "RESTAPI_server.h" |  | ||||||
|  |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| 	void RESTAPI_oauth2Handler::DoGet() { |  | ||||||
|         if (!IsAuthorized()) { |  | ||||||
|             UnAuthorized("Not authorized."); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false); |  | ||||||
|         if(GetMe) { |  | ||||||
|             Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); |  | ||||||
|             Poco::JSON::Object Me; |  | ||||||
|             UserInfo_.userinfo.to_json(Me); |  | ||||||
|             ReturnObject(Me); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         BadRequest("Ill-formed request. Please consult documentation."); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void RESTAPI_oauth2Handler::DoDelete() { |  | ||||||
|         if (!IsAuthorized()) { |  | ||||||
|             UnAuthorized("Not authorized."); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "..."); |  | ||||||
|         if (Token == SessionToken_) { |  | ||||||
|             AuthService()->Logout(Token); |  | ||||||
|             ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true); |  | ||||||
|         } else { |  | ||||||
|             Logger_.information(Poco::format("BAD-LOGOUT(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); |  | ||||||
|             NotFound(); |  | ||||||
|         } |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPI_oauth2Handler::DoPost() { |  | ||||||
|         auto Obj = ParseStream(); |  | ||||||
|         auto userId = GetS(RESTAPI::Protocol::USERID, Obj); |  | ||||||
|         auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj); |  | ||||||
|         auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj); |  | ||||||
|         Poco::toLowerInPlace(userId); |  | ||||||
|  |  | ||||||
|         if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS, false)) { |  | ||||||
|             Logger_.information(Poco::format("POLICY-REQUEST(%s): Request.", Request->clientAddress().toString())); |  | ||||||
|             Poco::JSON::Object  Answer; |  | ||||||
|             Answer.set(RESTAPI::Protocol::PASSWORDPATTERN, AuthService()->PasswordValidationExpression()); |  | ||||||
|             Answer.set(RESTAPI::Protocol::ACCESSPOLICY, RESTAPI_Server()->GetAccessPolicy()); |  | ||||||
|             Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, RESTAPI_Server()->GetPasswordPolicy()); |  | ||||||
|             ReturnObject(Answer); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD,false)) { |  | ||||||
|             //  Send an email to the userId |  | ||||||
|             Logger_.information(Poco::format("FORGOTTEN-PASSWORD(%s): Request for %s", Request->clientAddress().toString(), userId)); |  | ||||||
|             SecurityObjects::UserInfoAndPolicy UInfo; |  | ||||||
|             if(AuthService::SendEmailToUser(userId,AuthService::FORGOT_PASSWORD)) |  | ||||||
|                 Logger_.information(Poco::format("Send password reset link to %s",userId)); |  | ||||||
|             UInfo.webtoken.userMustChangePassword=true; |  | ||||||
|             Poco::JSON::Object ReturnObj; |  | ||||||
|             UInfo.webtoken.to_json(ReturnObj); |  | ||||||
|             ReturnObject(ReturnObj); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfoAndPolicy UInfo; |  | ||||||
|  |  | ||||||
|         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo); |  | ||||||
|         if (Code==AuthService::SUCCESS) { |  | ||||||
|             Poco::JSON::Object ReturnObj; |  | ||||||
|             UInfo.webtoken.to_json(ReturnObj); |  | ||||||
|             ReturnObject(ReturnObj); |  | ||||||
|             return; |  | ||||||
|         } else { |  | ||||||
|             switch(Code) { |  | ||||||
|                 case AuthService::INVALID_CREDENTIALS: UnAuthorized("Unrecognized credentials (username/password)."); break; |  | ||||||
|                 case AuthService::PASSWORD_INVALID: UnAuthorized("Invalid password."); break; |  | ||||||
|                 case AuthService::PASSWORD_ALREADY_USED: UnAuthorized("Password already used previously."); break; |  | ||||||
|                 case AuthService::USERNAME_PENDING_VERIFICATION: UnAuthorized("User access pending email verification."); break; |  | ||||||
|                 case AuthService::PASSWORD_CHANGE_REQUIRED: UnAuthorized("Password change expected."); break; |  | ||||||
|                 default: UnAuthorized("Unrecognized credentials (username/password)."); break; |  | ||||||
|             } |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,92 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include <memory> |  | ||||||
|  |  | ||||||
| #include "Poco/URI.h" |  | ||||||
|  |  | ||||||
| #include "RESTAPI_server.h" |  | ||||||
| #include "RESTAPI_oauth2Handler.h" |  | ||||||
| #include "RESTAPI_system_command.h" |  | ||||||
| #include "RESTAPI_user_handler.h" |  | ||||||
| #include "RESTAPI_users_handler.h" |  | ||||||
| #include "RESTAPI_action_links.h" |  | ||||||
| #include "RESTAPI_systemEndpoints_handler.h" |  | ||||||
| #include "RESTAPI_AssetServer.h" |  | ||||||
| #include "RESTAPI_avatarHandler.h" |  | ||||||
| #include "RESTAPI_email_handler.h" |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_Server *RESTAPI_Server::instance_ = nullptr; |  | ||||||
|  |  | ||||||
|     int RESTAPI_Server::Start() { |  | ||||||
|         Logger_.information("Starting."); |  | ||||||
|         Server_.InitLogging(); |  | ||||||
|  |  | ||||||
|         AsserDir_ = Daemon()->ConfigPath("openwifi.restapi.wwwassets"); |  | ||||||
|         AccessPolicy_ = Daemon()->ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); |  | ||||||
|         PasswordPolicy_ = Daemon()->ConfigGetString("openwifi.document.policy.password", "/wwwassets/possword_policy.html"); |  | ||||||
|  |  | ||||||
|         for(const auto & Svr: ConfigServersList_) { |  | ||||||
| 			Logger_.information(Poco::format("Starting: %s:%s Keyfile:%s CertFile: %s", Svr.Address(), std::to_string(Svr.Port()), |  | ||||||
| 											 Svr.KeyFile(),Svr.CertFile())); |  | ||||||
|  |  | ||||||
|             auto Sock{Svr.CreateSecureSocket(Logger_)}; |  | ||||||
|  |  | ||||||
| 			Svr.LogCert(Logger_); |  | ||||||
| 			if(!Svr.RootCA().empty()) |  | ||||||
| 				Svr.LogCas(Logger_); |  | ||||||
|  |  | ||||||
|             auto Params = new Poco::Net::HTTPServerParams; |  | ||||||
|             Params->setMaxThreads(50); |  | ||||||
|             Params->setMaxQueued(200); |  | ||||||
| 			Params->setKeepAlive(true); |  | ||||||
|  |  | ||||||
|             auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new RequestHandlerFactory(Server_), Pool_, Sock, Params); |  | ||||||
|             NewServer->start(); |  | ||||||
|             RESTServers_.push_back(std::move(NewServer)); |  | ||||||
|         } |  | ||||||
|         return 0; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     Poco::Net::HTTPRequestHandler *RequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest & Request) { |  | ||||||
|         Poco::URI uri(Request.getURI()); |  | ||||||
|         const auto & Path = uri.getPath(); |  | ||||||
|         RESTAPIHandler::BindingMap Bindings; |  | ||||||
|         return RESTAPI_Router< |  | ||||||
|                 RESTAPI_oauth2Handler, |  | ||||||
|                 RESTAPI_users_handler, |  | ||||||
|                 RESTAPI_user_handler, |  | ||||||
|                 RESTAPI_system_command, |  | ||||||
|                 RESTAPI_AssetServer, |  | ||||||
|                 RESTAPI_systemEndpoints_handler, |  | ||||||
|                 RESTAPI_action_links, |  | ||||||
|                 RESTAPI_avatarHandler, |  | ||||||
|                 RESTAPI_email_handler |  | ||||||
|                 >(Path,Bindings,Logger_,Server_); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_Server::Stop() { |  | ||||||
|         Logger_.information("Stopping "); |  | ||||||
|         for( const auto & svr : RESTServers_ ) |  | ||||||
|             svr->stop(); |  | ||||||
|         RESTServers_.clear(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_Server::reinitialize(Poco::Util::Application &self) { |  | ||||||
|         Daemon()->LoadConfigurationFile(); |  | ||||||
|         Logger_.information("Reinitializing."); |  | ||||||
|         Stop(); |  | ||||||
|         Start(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
| }  // namespace |  | ||||||
| @@ -1,71 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRAL_UCENTRALRESTAPISERVER_H |  | ||||||
| #define UCENTRAL_UCENTRALRESTAPISERVER_H |  | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
| #include "Poco/Net/HTTPServer.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandler.h" |  | ||||||
| #include "Poco/Net/HTTPRequestHandlerFactory.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "Poco/Net/NetException.h" |  | ||||||
| #include "RESTAPI_GenericServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|  |  | ||||||
|     class RESTAPI_Server : public SubSystemServer { |  | ||||||
|  |  | ||||||
|     public: |  | ||||||
|         static RESTAPI_Server *instance() { |  | ||||||
|             if (instance_ == nullptr) { |  | ||||||
|                 instance_ = new RESTAPI_Server; |  | ||||||
|             } |  | ||||||
|             return instance_; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         int Start() override; |  | ||||||
|         void Stop() override; |  | ||||||
|         void reinitialize(Poco::Util::Application &self) override; |  | ||||||
|  |  | ||||||
|         inline const std::string & AssetDir() { return AsserDir_; } |  | ||||||
|         inline const std::string & GetPasswordPolicy() const { return PasswordPolicy_; } |  | ||||||
|         inline const std::string & GetAccessPolicy() const { return AccessPolicy_; } |  | ||||||
|     private: |  | ||||||
| 		static RESTAPI_Server *instance_; |  | ||||||
|         std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; |  | ||||||
| 		Poco::ThreadPool	Pool_; |  | ||||||
| 		std::string         AsserDir_; |  | ||||||
| 		std::string         PasswordPolicy_; |  | ||||||
| 		std::string         AccessPolicy_; |  | ||||||
| 		RESTAPI_GenericServer   Server_; |  | ||||||
|  |  | ||||||
|         RESTAPI_Server() noexcept: |  | ||||||
|             SubSystemServer("RESTAPIServer", "REST-SRV", "openwifi.restapi") |  | ||||||
|         { |  | ||||||
|         } |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     inline RESTAPI_Server * RESTAPI_Server() { return RESTAPI_Server::instance(); }; |  | ||||||
|  |  | ||||||
|     class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { |  | ||||||
|         public: |  | ||||||
|         RequestHandlerFactory(RESTAPI_GenericServer &Server) : |  | ||||||
|                 Logger_(RESTAPI_Server()->Logger()), |  | ||||||
|                 Server_(Server){} |  | ||||||
|  |  | ||||||
|             Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &request) override; |  | ||||||
|         private: |  | ||||||
|             Poco::Logger    & Logger_; |  | ||||||
|             RESTAPI_GenericServer   &Server_; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } //   namespace |  | ||||||
|  |  | ||||||
| #endif //UCENTRAL_UCENTRALRESTAPISERVER_H |  | ||||||
| @@ -1,146 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
| #include "RESTAPI_system_command.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Exception.h" |  | ||||||
| #include "Poco/JSON/Parser.h" |  | ||||||
| #include "Poco/DateTime.h" |  | ||||||
| #include "Poco/DateTimeFormat.h" |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
| #include "RESTAPI_protocol.h" |  | ||||||
| #include "RESTAPI_errors.h" |  | ||||||
| #include <thread> |  | ||||||
| #include <chrono> |  | ||||||
|  |  | ||||||
| using namespace std::chrono_literals; |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| 	void RESTAPI_system_command::DoPost() { |  | ||||||
| 		auto Obj = ParseStream(); |  | ||||||
| 		if (Obj->has(RESTAPI::Protocol::COMMAND)) { |  | ||||||
| 			auto Command = Poco::toLower(Obj->get(RESTAPI::Protocol::COMMAND).toString()); |  | ||||||
| 			if (Command == RESTAPI::Protocol::SETLOGLEVEL) { |  | ||||||
| 				if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && |  | ||||||
| 					Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { |  | ||||||
| 					auto ParametersBlock = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); |  | ||||||
| 					for (const auto &i : *ParametersBlock) { |  | ||||||
| 						Poco::JSON::Parser pp; |  | ||||||
| 						auto InnerObj = pp.parse(i).extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 						if (InnerObj->has(RESTAPI::Protocol::TAG) && |  | ||||||
| 							InnerObj->has(RESTAPI::Protocol::VALUE)) { |  | ||||||
| 							auto Name = GetS(RESTAPI::Protocol::TAG, InnerObj); |  | ||||||
| 							auto Value = GetS(RESTAPI::Protocol::VALUE, InnerObj); |  | ||||||
| 							Daemon()->SetSubsystemLogLevel(Name, Value); |  | ||||||
| 							Logger_.information( |  | ||||||
| 								Poco::format("Setting log level for %s at %s", Name, Value)); |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					OK(); |  | ||||||
| 					return; |  | ||||||
| 				} |  | ||||||
| 			} else if (Command == RESTAPI::Protocol::GETLOGLEVELS) { |  | ||||||
| 				auto CurrentLogLevels = Daemon()->GetLogLevels(); |  | ||||||
| 				Poco::JSON::Object Result; |  | ||||||
| 				Poco::JSON::Array Array; |  | ||||||
| 				for (auto &[Name, Level] : CurrentLogLevels) { |  | ||||||
| 					Poco::JSON::Object Pair; |  | ||||||
| 					Pair.set(RESTAPI::Protocol::TAG, Name); |  | ||||||
| 					Pair.set(RESTAPI::Protocol::VALUE, Level); |  | ||||||
| 					Array.add(Pair); |  | ||||||
| 				} |  | ||||||
| 				Result.set(RESTAPI::Protocol::TAGLIST, Array); |  | ||||||
| 				ReturnObject(Result); |  | ||||||
| 				return; |  | ||||||
| 			} else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) { |  | ||||||
| 				Poco::JSON::Object Result; |  | ||||||
| 				Poco::JSON::Array LevelNamesArray; |  | ||||||
| 				const Types::StringVec &LevelNames = Daemon()->GetLogLevelNames(); |  | ||||||
| 				for (const auto &i : LevelNames) |  | ||||||
| 					LevelNamesArray.add(i); |  | ||||||
| 				Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); |  | ||||||
| 				ReturnObject(Result); |  | ||||||
| 				return; |  | ||||||
| 			} else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) { |  | ||||||
| 				Poco::JSON::Object Result; |  | ||||||
| 				Poco::JSON::Array LevelNamesArray; |  | ||||||
| 				const Types::StringVec &SubSystemNames = Daemon()->GetSubSystems(); |  | ||||||
| 				for (const auto &i : SubSystemNames) |  | ||||||
| 					LevelNamesArray.add(i); |  | ||||||
| 				Result.set(RESTAPI::Protocol::LIST, LevelNamesArray); |  | ||||||
| 				ReturnObject(Result); |  | ||||||
| 				return; |  | ||||||
| 			} else if (Command == RESTAPI::Protocol::STATS) { |  | ||||||
|  |  | ||||||
| 			} else if (Command == RESTAPI::Protocol::RELOAD) { |  | ||||||
| 				if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) && |  | ||||||
| 					Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) { |  | ||||||
| 					auto SubSystems = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS); |  | ||||||
| 					std::vector<std::string> Names; |  | ||||||
| 					for (const auto &i : *SubSystems) |  | ||||||
| 						Names.push_back(i.toString()); |  | ||||||
| 						std::thread	ReloadThread([Names](){ |  | ||||||
| 						std::this_thread::sleep_for(10000ms); |  | ||||||
| 						for(const auto &i:Names) { |  | ||||||
| 						    if(i=="daemon") |  | ||||||
| 						        Daemon()->Reload(); |  | ||||||
| 						    else |  | ||||||
|     							Daemon()->Reload(i); |  | ||||||
| 						} |  | ||||||
| 					 }); |  | ||||||
| 					ReloadThread.detach(); |  | ||||||
| 				} |  | ||||||
| 				OK(); |  | ||||||
| 				return; |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			BadRequest(RESTAPI::Errors::InvalidCommand); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		BadRequest(RESTAPI::Errors::MissingOrInvalidParameters); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void RESTAPI_system_command::DoGet() { |  | ||||||
| 		std::string Arg; |  | ||||||
| 		if(HasParameter("command",Arg) && Arg=="info") { |  | ||||||
| 			Poco::JSON::Object Answer; |  | ||||||
| 			Answer.set(RESTAPI::Protocol::VERSION, Daemon()->Version()); |  | ||||||
| 			Answer.set(RESTAPI::Protocol::UPTIME, Daemon()->uptime().totalSeconds()); |  | ||||||
| 			Answer.set(RESTAPI::Protocol::START, Daemon()->startTime().epochTime()); |  | ||||||
| 			Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName()); |  | ||||||
| 			Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount()); |  | ||||||
| 			Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName()); |  | ||||||
|  |  | ||||||
| 			Poco::JSON::Array   Certificates; |  | ||||||
| 			auto SubSystems = Daemon()->GetFullSubSystems(); |  | ||||||
| 			std::set<std::string>   CertNames; |  | ||||||
|  |  | ||||||
| 			for(const auto &i:SubSystems) { |  | ||||||
| 			    auto Hosts=i->HostSize(); |  | ||||||
| 			    for(uint64_t j=0;j<Hosts;++j) { |  | ||||||
| 			        auto CertFileName = i->Host(j).CertFile(); |  | ||||||
| 			        if(!CertFileName.empty()) { |  | ||||||
| 			            auto InsertResult = CertNames.insert(CertFileName); |  | ||||||
| 			            if(InsertResult.second) { |  | ||||||
| 			                Poco::JSON::Object  Inner; |  | ||||||
| 			                Inner.set("filename", CertFileName); |  | ||||||
| 			                Poco::Crypto::X509Certificate   C(CertFileName); |  | ||||||
| 			                auto ExpiresOn = C.expiresOn(); |  | ||||||
| 			                Inner.set("expiresOn",ExpiresOn.timestamp().epochTime()); |  | ||||||
| 			                Certificates.add(Inner); |  | ||||||
| 			            } |  | ||||||
| 			        } |  | ||||||
| 			    } |  | ||||||
| 			} |  | ||||||
| 			Answer.set("certificates", Certificates); |  | ||||||
| 			ReturnObject(Answer); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
| 		BadRequest(RESTAPI::Errors::InvalidCommand); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,32 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H |  | ||||||
| #define UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H |  | ||||||
|  |  | ||||||
| #include "RESTAPI_handler.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| class RESTAPI_system_command : public RESTAPIHandler { |  | ||||||
|   public: |  | ||||||
|     RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, bool Internal) |  | ||||||
| 		: RESTAPIHandler(bindings, L, |  | ||||||
| 						 std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST, |  | ||||||
| 														  Poco::Net::HTTPRequest::HTTP_GET, |  | ||||||
| 														  Poco::Net::HTTPRequest::HTTP_OPTIONS}, |  | ||||||
| 														  Server, |  | ||||||
| 						 Internal) {} |  | ||||||
| 	static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/system"};} |  | ||||||
|  |  | ||||||
| 	void DoGet() final; |  | ||||||
| 	void DoPost() final; |  | ||||||
| 	void DoPut() final {}; |  | ||||||
| 	void DoDelete() final {}; |  | ||||||
| 	}; |  | ||||||
| } |  | ||||||
| #endif // UCENTRALGW_RESTAPI_SYSTEM_COMMAND_H |  | ||||||
| @@ -1,186 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-06-21. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include "RESTAPI_user_handler.h" |  | ||||||
| #include "StorageService.h" |  | ||||||
| #include "Poco/JSON/Parser.h" |  | ||||||
| #include "Utils.h" |  | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
| #include "RESTAPI_errors.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
|     void RESTAPI_user_handler::DoGet() { |  | ||||||
|         std::string Id = GetBinding("id", ""); |  | ||||||
|         if(Id.empty()) { |  | ||||||
|             BadRequest(RESTAPI::Errors::MissingUserID); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfo   UInfo; |  | ||||||
|         if(!Storage()->GetUserById(Id,UInfo)) { |  | ||||||
|             NotFound(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         Poco::JSON::Object  UserInfoObject; |  | ||||||
|         UInfo.to_json(UserInfoObject); |  | ||||||
|         ReturnObject(UserInfoObject); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_user_handler::DoDelete() { |  | ||||||
|         std::string Id = GetBinding("id", ""); |  | ||||||
|         if(Id.empty()) { |  | ||||||
|             BadRequest(RESTAPI::Errors::MissingUserID); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfo UInfo; |  | ||||||
|         if(!Storage()->GetUserById(Id,UInfo)) { |  | ||||||
|             NotFound(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!Storage()->DeleteUser(UserInfo_.userinfo.email,Id)) { |  | ||||||
|             NotFound(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(AuthService()->DeleteUserFromCache(UInfo.email)) |  | ||||||
|             ; |  | ||||||
|         Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email)); |  | ||||||
|         Storage()->RevokeAllTokens(UInfo.email); |  | ||||||
|         Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email)); |  | ||||||
|         OK(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_user_handler::DoPost() { |  | ||||||
|         std::string Id = GetBinding("id", ""); |  | ||||||
|         if(Id!="0") { |  | ||||||
|             BadRequest(RESTAPI::Errors::IdMustBe0); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfo   UInfo; |  | ||||||
|         RESTAPI_utils::from_request(UInfo,*Request); |  | ||||||
|  |  | ||||||
|         if(UInfo.userRole == SecurityObjects::UNKNOWN) { |  | ||||||
|             BadRequest(RESTAPI::Errors::InvalidUserRole); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Poco::toLowerInPlace(UInfo.email); |  | ||||||
|         if(!Utils::ValidEMailAddress(UInfo.email)) { |  | ||||||
|             BadRequest(RESTAPI::Errors::InvalidEmailAddress); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!UInfo.currentPassword.empty()) { |  | ||||||
|             if(!AuthService()->ValidatePassword(UInfo.currentPassword)) { |  | ||||||
|                 BadRequest(RESTAPI::Errors::InvalidPassword); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(UInfo.name.empty()) |  | ||||||
|             UInfo.name = UInfo.email; |  | ||||||
|  |  | ||||||
|         if(!Storage()->CreateUser(UInfo.email,UInfo)) { |  | ||||||
|             Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email)); |  | ||||||
|             BadRequest(RESTAPI::Errors::RecordNotCreated); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(GetParameter("email_verification","false")=="true") { |  | ||||||
|             if(AuthService::VerifyEmail(UInfo)) |  | ||||||
|                 Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email)); |  | ||||||
|             Storage()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(!Storage()->GetUserByEmail(UInfo.email, UInfo)) { |  | ||||||
|             Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email)); |  | ||||||
|             NotFound(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Poco::JSON::Object  UserInfoObject; |  | ||||||
|         UInfo.to_json(UserInfoObject); |  | ||||||
|  |  | ||||||
|         ReturnObject(UserInfoObject); |  | ||||||
|  |  | ||||||
|         Logger_.information(Poco::format("User '%s' has been added by '%s')",UInfo.email, UserInfo_.userinfo.email)); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     void RESTAPI_user_handler::DoPut() { |  | ||||||
|         std::string Id = GetBinding("id", ""); |  | ||||||
|         if(Id.empty()) { |  | ||||||
|             BadRequest(RESTAPI::Errors::MissingUserID); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         SecurityObjects::UserInfo   LocalObject; |  | ||||||
|         if(!Storage()->GetUserById(Id,LocalObject)) { |  | ||||||
|             NotFound(); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // some basic validations |  | ||||||
|         auto RawObject = ParseStream(); |  | ||||||
|         if(RawObject->has("userRole") && SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::UNKNOWN) { |  | ||||||
|             BadRequest(RESTAPI::Errors::InvalidUserRole); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // The only valid things to change are: changePassword, name, |  | ||||||
|         if(RawObject->has("name")) |  | ||||||
|             LocalObject.name = RawObject->get("name").toString(); |  | ||||||
|         if(RawObject->has("description")) |  | ||||||
|             LocalObject.description = RawObject->get("description").toString(); |  | ||||||
|         if(RawObject->has("avatar")) |  | ||||||
|             LocalObject.avatar = RawObject->get("avatar").toString(); |  | ||||||
|         if(RawObject->has("changePassword")) |  | ||||||
|             LocalObject.changePassword = RawObject->get("changePassword").toString()=="true"; |  | ||||||
|         if(RawObject->has("owner")) |  | ||||||
|             LocalObject.owner = RawObject->get("owner").toString(); |  | ||||||
|         if(RawObject->has("location")) |  | ||||||
|             LocalObject.location = RawObject->get("location").toString(); |  | ||||||
|         if(RawObject->has("locale")) |  | ||||||
|             LocalObject.locale = RawObject->get("locale").toString(); |  | ||||||
|         if(RawObject->has("userRole")) |  | ||||||
|             LocalObject.userRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()); |  | ||||||
|         if(RawObject->has("suspended")) |  | ||||||
|             LocalObject.suspended = RawObject->get("suspended").toString()=="true"; |  | ||||||
|         if(RawObject->has("blackListed")) |  | ||||||
|             LocalObject.blackListed = RawObject->get("blackListed").toString()=="true"; |  | ||||||
|         if(RawObject->has("notes")) { |  | ||||||
|             SecurityObjects::NoteInfoVec NIV; |  | ||||||
|             NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString()); |  | ||||||
|             for(auto const &i:NIV) { |  | ||||||
|                 SecurityObjects::NoteInfo   ii{.created=(uint64_t)std::time(nullptr), .createdBy=UserInfo_.userinfo.email, .note=i.note}; |  | ||||||
|                 LocalObject.notes.push_back(ii); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if(RawObject->has("currentPassword")) { |  | ||||||
|             if(!AuthService()->ValidatePassword(RawObject->get("currentPassword").toString())) { |  | ||||||
|                 BadRequest(RESTAPI::Errors::InvalidPassword); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|             if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),LocalObject)) { |  | ||||||
|                 BadRequest(RESTAPI::Errors::PasswordRejected); |  | ||||||
|                 return; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(GetParameter("email_verification","false")=="true") { |  | ||||||
|             if(AuthService::VerifyEmail(LocalObject)) |  | ||||||
|                 Logger_.information(Poco::format("Verification e-mail requested for %s",LocalObject.email)); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if(Storage()->UpdateUserInfo(UserInfo_.userinfo.email,Id,LocalObject)) { |  | ||||||
|             Poco::JSON::Object  ModifiedObject; |  | ||||||
|             LocalObject.to_json(ModifiedObject); |  | ||||||
|             ReturnObject(ModifiedObject); |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         BadRequest(RESTAPI::Errors::RecordNotUpdated); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-07-05. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi::RESTAPI_utils { |  | ||||||
|  |  | ||||||
| 	void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr) { |  | ||||||
| 		std::string D = ObjStr.empty() ? "{}" : ObjStr; |  | ||||||
| 		Poco::JSON::Parser P; |  | ||||||
| 		Poco::Dynamic::Var result = P.parse(D); |  | ||||||
| 		const auto &DetailsObj = result.extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 		Obj.set(ObjName, DetailsObj); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| @@ -1,216 +0,0 @@ | |||||||
| // |  | ||||||
| // Created by stephane bourque on 2021-07-05. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_RESTAPI_UTILS_H |  | ||||||
| #define UCENTRALGW_RESTAPI_UTILS_H |  | ||||||
| #include <functional> |  | ||||||
|  |  | ||||||
| #include "Poco/JSON/Object.h" |  | ||||||
| #include "Poco/JSON/Parser.h" |  | ||||||
| #include "Poco/Net/HTTPServerRequest.h" |  | ||||||
| #include "OpenWifiTypes.h" |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi::RESTAPI_utils { |  | ||||||
|  |  | ||||||
| 	void EmbedDocument(const std::string & ObjName, Poco::JSON::Object & Obj, const std::string &ObjStr); |  | ||||||
|  |  | ||||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, bool V) { |  | ||||||
| 		Obj.set(Field,V); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::string & S) { |  | ||||||
| 		Obj.set(Field,S); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const char * S) { |  | ||||||
| 		Obj.set(Field,S); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, uint64_t V) { |  | ||||||
| 		Obj.set(Field,V); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::StringVec &V) { |  | ||||||
| 		Poco::JSON::Array	A; |  | ||||||
| 		for(const auto &i:V) |  | ||||||
| 			A.add(i); |  | ||||||
| 		Obj.set(Field,A); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     inline void field_to_json(Poco::JSON::Object &Obj, const char *Field, const Types::CountedMap &M) { |  | ||||||
|         Poco::JSON::Array	A; |  | ||||||
|         for(const auto &[Key,Value]:M) { |  | ||||||
|             Poco::JSON::Object  O; |  | ||||||
|             O.set("tag",Key); |  | ||||||
|             O.set("value", Value); |  | ||||||
|             A.add(O); |  | ||||||
|         } |  | ||||||
|         Obj.set(Field,A); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     template<typename T> void field_to_json(Poco::JSON::Object &Obj, |  | ||||||
| 					   						const char *Field, |  | ||||||
| 					   						const T &V, |  | ||||||
| 											std::function<std::string(const T &)> F) { |  | ||||||
| 		Obj.set(Field, F(V)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<typename T> bool field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, T & V, |  | ||||||
| 											std::function<T(const std::string &)> F) { |  | ||||||
| 		if(Obj->has(Field)) |  | ||||||
| 			V = F(Obj->get(Field).toString()); |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, std::string &S) { |  | ||||||
| 		if(Obj->has(Field)) |  | ||||||
| 			S = Obj->get(Field).toString(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, uint64_t &V) { |  | ||||||
| 		if(Obj->has(Field)) |  | ||||||
| 			V = Obj->get(Field); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, bool &V) { |  | ||||||
| 		if(Obj->has(Field)) |  | ||||||
| 			V = (Obj->get(Field).toString() == "true"); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline void field_from_json(Poco::JSON::Object::Ptr Obj, const char *Field, Types::StringVec &V) { |  | ||||||
| 		if(Obj->isArray(Field)) { |  | ||||||
| 			V.clear(); |  | ||||||
| 			Poco::JSON::Array::Ptr A = Obj->getArray(Field); |  | ||||||
| 			for(const auto &i:*A) { |  | ||||||
| 				V.push_back(i.toString()); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> void field_to_json(Poco::JSON::Object &Obj, const char *Field, const std::vector<T> &Value) { |  | ||||||
| 		Poco::JSON::Array Arr; |  | ||||||
| 		for(const auto &i:Value) { |  | ||||||
| 			Poco::JSON::Object	AO; |  | ||||||
| 			i.to_json(AO); |  | ||||||
| 			Arr.add(AO); |  | ||||||
| 		} |  | ||||||
| 		Obj.set(Field, Arr); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, std::vector<T> &Value) { |  | ||||||
| 		if(Obj->isArray(Field)) { |  | ||||||
| 			Poco::JSON::Array::Ptr	Arr = Obj->getArray(Field); |  | ||||||
| 			for(auto &i:*Arr) { |  | ||||||
| 				auto InnerObj = i.extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 				T	NewItem; |  | ||||||
| 				NewItem.from_json(InnerObj); |  | ||||||
| 				Value.push_back(NewItem); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> void field_from_json(const Poco::JSON::Object::Ptr &Obj, const char *Field, T &Value) { |  | ||||||
| 		if(Obj->isObject(Field)) { |  | ||||||
| 			Poco::JSON::Object::Ptr	A = Obj->getObject(Field); |  | ||||||
| 			Value.from_json(A); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline std::string to_string(const Types::StringVec & ObjectArray) { |  | ||||||
| 		Poco::JSON::Array OutputArr; |  | ||||||
| 		if(ObjectArray.empty()) |  | ||||||
| 			return "[]"; |  | ||||||
| 		for(auto const &i:ObjectArray) { |  | ||||||
| 			OutputArr.add(i); |  | ||||||
| 		} |  | ||||||
| 		std::ostringstream OS; |  | ||||||
| 		Poco::JSON::Stringifier::condense(OutputArr,OS); |  | ||||||
| 		return OS.str(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> std::string to_string(const std::vector<T> & ObjectArray) { |  | ||||||
| 		Poco::JSON::Array OutputArr; |  | ||||||
| 		if(ObjectArray.empty()) |  | ||||||
| 			return "[]"; |  | ||||||
| 		for(auto const &i:ObjectArray) { |  | ||||||
| 			Poco::JSON::Object O; |  | ||||||
| 			i.to_json(O); |  | ||||||
| 			OutputArr.add(O); |  | ||||||
| 		} |  | ||||||
| 		std::ostringstream OS; |  | ||||||
| 		Poco::JSON::Stringifier::condense(OutputArr,OS); |  | ||||||
| 		return OS.str(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> std::string to_string(const T & Object) { |  | ||||||
| 		Poco::JSON::Object OutputObj; |  | ||||||
| 		Object.to_json(OutputObj); |  | ||||||
| 		std::ostringstream OS; |  | ||||||
| 		Poco::JSON::Stringifier::condense(OutputObj,OS); |  | ||||||
| 		return OS.str(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     inline Types::StringVec to_object_array(const std::string & ObjectString) { |  | ||||||
|  |  | ||||||
|         Types::StringVec 	Result; |  | ||||||
|         if(ObjectString.empty()) |  | ||||||
|             return Result; |  | ||||||
|  |  | ||||||
|         try { |  | ||||||
|             Poco::JSON::Parser P; |  | ||||||
|             auto Object = P.parse(ObjectString).template extract<Poco::JSON::Array::Ptr>(); |  | ||||||
|             for (auto const i : *Object) { |  | ||||||
|                 Result.push_back(i.toString()); |  | ||||||
|             } |  | ||||||
|         } catch (...) { |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|         return Result; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     template<class T> std::vector<T> to_object_array(const std::string & ObjectString) { |  | ||||||
|  |  | ||||||
| 		std::vector<T>	Result; |  | ||||||
| 		if(ObjectString.empty()) |  | ||||||
| 			return Result; |  | ||||||
|  |  | ||||||
| 		try { |  | ||||||
| 			Poco::JSON::Parser P; |  | ||||||
| 			auto Object = P.parse(ObjectString).template extract<Poco::JSON::Array::Ptr>(); |  | ||||||
| 			for (auto const i : *Object) { |  | ||||||
| 				auto InnerObject = i.template extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 				T Obj; |  | ||||||
| 				Obj.from_json(InnerObject); |  | ||||||
| 				Result.push_back(Obj); |  | ||||||
| 			} |  | ||||||
| 		} catch (...) { |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	template<class T> T to_object(const std::string & ObjectString) { |  | ||||||
| 		T	Result; |  | ||||||
|  |  | ||||||
| 		if(ObjectString.empty()) |  | ||||||
| 			return Result; |  | ||||||
|  |  | ||||||
| 		Poco::JSON::Parser	P; |  | ||||||
| 		auto Object = P.parse(ObjectString).template extract<Poco::JSON::Object::Ptr>(); |  | ||||||
| 		Result.from_json(Object); |  | ||||||
|  |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     template<class T> bool from_request(T & Obj, Poco::Net::HTTPServerRequest &Request) { |  | ||||||
|         Poco::JSON::Parser IncomingParser; |  | ||||||
|         auto RawObject = IncomingParser.parse(Request.stream()).extract<Poco::JSON::Object::Ptr>(); |  | ||||||
|         Obj.from_json(RawObject); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif // UCENTRALGW_RESTAPI_UTILS_H |  | ||||||
							
								
								
									
										248
									
								
								src/RESTObjects/RESTAPI_FMSObjects.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										248
									
								
								src/RESTObjects/RESTAPI_FMSObjects.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,248 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-07-12. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "RESTAPI_FMSObjects.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | using OpenWifi::RESTAPI_utils::field_to_json; | ||||||
|  | using OpenWifi::RESTAPI_utils::field_from_json; | ||||||
|  |  | ||||||
|  | namespace OpenWifi::FMSObjects { | ||||||
|  |  | ||||||
|  |     void Firmware::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |         field_to_json(Obj, "release", release); | ||||||
|  |         field_to_json(Obj, "deviceType", deviceType); | ||||||
|  |         field_to_json(Obj, "description", description); | ||||||
|  |         field_to_json(Obj, "revision", revision); | ||||||
|  |         field_to_json(Obj, "uri", uri); | ||||||
|  |         field_to_json(Obj, "image", image); | ||||||
|  |         field_to_json(Obj, "imageDate", imageDate); | ||||||
|  |         field_to_json(Obj, "size", size); | ||||||
|  |         field_to_json(Obj, "downloadCount", downloadCount); | ||||||
|  |         field_to_json(Obj, "firmwareHash", firmwareHash); | ||||||
|  |         field_to_json(Obj, "owner", owner); | ||||||
|  |         field_to_json(Obj, "location", location); | ||||||
|  |         field_to_json(Obj, "uploader", uploader); | ||||||
|  |         field_to_json(Obj, "digest", digest); | ||||||
|  |         field_to_json(Obj, "latest", latest); | ||||||
|  |         field_to_json(Obj, "notes", notes); | ||||||
|  |         field_to_json(Obj, "created", created); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     bool Firmware::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             field_from_json(Obj, "release", release); | ||||||
|  |             field_from_json(Obj, "deviceType", deviceType); | ||||||
|  |             field_from_json(Obj, "description", description); | ||||||
|  |             field_from_json(Obj, "revision", revision); | ||||||
|  |             field_from_json(Obj, "uri", uri); | ||||||
|  |             field_from_json(Obj, "image", image); | ||||||
|  |             field_from_json(Obj, "imageDate", imageDate); | ||||||
|  |             field_from_json(Obj, "size", size); | ||||||
|  |             field_from_json(Obj, "downloadCount", downloadCount); | ||||||
|  |             field_from_json(Obj, "firmwareHash", firmwareHash); | ||||||
|  |             field_from_json(Obj, "owner", owner); | ||||||
|  |             field_from_json(Obj, "location", location); | ||||||
|  |             field_from_json(Obj, "uploader", uploader); | ||||||
|  |             field_from_json(Obj, "digest", digest); | ||||||
|  |             field_from_json(Obj, "latest", latest); | ||||||
|  |             field_from_json(Obj, "notes", notes); | ||||||
|  |             field_from_json(Obj, "created", created); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void FirmwareList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"firmwares",firmwares); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool FirmwareList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj, "firmwares", firmwares); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceType::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |         field_to_json(Obj, "deviceType", deviceType); | ||||||
|  |         field_to_json(Obj, "manufacturer", manufacturer); | ||||||
|  |         field_to_json(Obj, "model", model); | ||||||
|  |         field_to_json(Obj, "policy", policy); | ||||||
|  |         field_to_json(Obj, "notes", notes); | ||||||
|  |         field_to_json(Obj, "lastUpdate", lastUpdate); | ||||||
|  |         field_to_json(Obj, "created", created); | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceType::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             field_from_json(Obj, "deviceType", deviceType); | ||||||
|  |             field_from_json(Obj, "manufacturer", manufacturer); | ||||||
|  |             field_from_json(Obj, "model", model); | ||||||
|  |             field_from_json(Obj, "policy", policy); | ||||||
|  |             field_from_json(Obj, "notes", notes); | ||||||
|  |             field_from_json(Obj, "lastUpdate", lastUpdate); | ||||||
|  |             field_from_json(Obj, "created", created); | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceTypeList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"deviceTypes", deviceTypes); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceTypeList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"deviceTypes", deviceTypes); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RevisionHistoryEntry::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "id", id); | ||||||
|  |         field_to_json(Obj, "serialNumber", serialNumber); | ||||||
|  |         field_to_json(Obj, "fromRelease", fromRelease); | ||||||
|  |         field_to_json(Obj, "toRelease", toRelease); | ||||||
|  |         field_to_json(Obj, "commandUUID", commandUUID); | ||||||
|  |         field_to_json(Obj, "revisionId", revisionId); | ||||||
|  |         field_to_json(Obj, "upgraded", upgraded); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool RevisionHistoryEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj, "id", id); | ||||||
|  |             field_from_json(Obj, "serialNumber", serialNumber); | ||||||
|  |             field_from_json(Obj, "fromRelease", fromRelease); | ||||||
|  |             field_from_json(Obj, "toRelease", toRelease); | ||||||
|  |             field_from_json(Obj, "commandUUID", commandUUID); | ||||||
|  |             field_from_json(Obj, "revisionId", revisionId); | ||||||
|  |             field_from_json(Obj, "upgraded", upgraded); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void RevisionHistoryEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"deviceTypes", history); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool RevisionHistoryEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"deviceTypes", history); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void FirmwareAgeDetails::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj,"latestId", latestId); | ||||||
|  |         field_to_json(Obj,"image", image); | ||||||
|  |         field_to_json(Obj,"imageDate", imageDate); | ||||||
|  |         field_to_json(Obj,"revision", revision); | ||||||
|  |         field_to_json(Obj,"uri", uri); | ||||||
|  |         field_to_json(Obj,"age", age); | ||||||
|  |         field_to_json(Obj,"latest",latest); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool FirmwareAgeDetails::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"latestId", latestId); | ||||||
|  |             field_from_json(Obj,"image", image); | ||||||
|  |             field_from_json(Obj,"imageDate", imageDate); | ||||||
|  |             field_from_json(Obj,"revision", revision); | ||||||
|  |             field_from_json(Obj,"uri", uri); | ||||||
|  |             field_from_json(Obj,"age", age); | ||||||
|  |             field_from_json(Obj,"latest", latest); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceConnectionInformation::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "serialNumber", serialNumber); | ||||||
|  |         field_to_json(Obj, "revision", revision); | ||||||
|  |         field_to_json(Obj, "deviceType", deviceType); | ||||||
|  |         field_to_json(Obj, "endPoint", endPoint); | ||||||
|  |         field_to_json(Obj, "lastUpdate", lastUpdate); | ||||||
|  |         field_to_json(Obj, "status", status); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceConnectionInformation::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj, "serialNumber", serialNumber); | ||||||
|  |             field_from_json(Obj, "revision", revision); | ||||||
|  |             field_from_json(Obj, "deviceType", deviceType); | ||||||
|  |             field_from_json(Obj, "endPoint", endPoint); | ||||||
|  |             field_from_json(Obj, "lastUpdate", lastUpdate); | ||||||
|  |             field_from_json(Obj, "status", status); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceReport::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "ouis",OUI_); | ||||||
|  |         field_to_json(Obj, "revisions", Revisions_); | ||||||
|  |         field_to_json(Obj, "deviceTypes", DeviceTypes_); | ||||||
|  |         field_to_json(Obj, "status", Status_); | ||||||
|  |         field_to_json(Obj, "endPoints", EndPoints_); | ||||||
|  |         field_to_json(Obj, "usingLatest", UsingLatest_); | ||||||
|  |         field_to_json(Obj, "unknownFirmwares", UnknownFirmwares_); | ||||||
|  |         field_to_json(Obj,"snapshot",snapshot); | ||||||
|  |         field_to_json(Obj,"numberOfDevices",numberOfDevices); | ||||||
|  |         field_to_json(Obj, "totalSecondsOld", totalSecondsOld_); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceReport::reset() { | ||||||
|  |         OUI_.clear(); | ||||||
|  |         Revisions_.clear(); | ||||||
|  |         DeviceTypes_.clear(); | ||||||
|  |         Status_.clear(); | ||||||
|  |         EndPoints_.clear(); | ||||||
|  |         UsingLatest_.clear(); | ||||||
|  |         UnknownFirmwares_.clear(); | ||||||
|  |         totalSecondsOld_.clear(); | ||||||
|  |         numberOfDevices = 0 ; | ||||||
|  |         snapshot = std::time(nullptr); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceReport::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										133
									
								
								src/RESTObjects/RESTAPI_FMSObjects.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/RESTObjects/RESTAPI_FMSObjects.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-07-12. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  |  | ||||||
|  | #ifndef UCENTRALFMS_RESTAPI_FMSOBJECTS_H | ||||||
|  | #define UCENTRALFMS_RESTAPI_FMSOBJECTS_H | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "RESTAPI_SecurityObjects.h" | ||||||
|  | #include "framework/OpenWifiTypes.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi::FMSObjects { | ||||||
|  |  | ||||||
|  |     struct Firmware { | ||||||
|  |         std::string     id; | ||||||
|  |         std::string     release; | ||||||
|  |         std::string     deviceType; | ||||||
|  |         std::string     description; | ||||||
|  |         std::string     revision; | ||||||
|  |         std::string     uri; | ||||||
|  |         std::string     image; | ||||||
|  |         uint64_t        imageDate=0; | ||||||
|  |         uint64_t        size=0; | ||||||
|  |         uint64_t        downloadCount=0; | ||||||
|  |         std::string     firmwareHash; | ||||||
|  |         std::string     owner; | ||||||
|  |         std::string     location; | ||||||
|  |         std::string     uploader; | ||||||
|  |         std::string     digest; | ||||||
|  |         bool            latest=false; | ||||||
|  |         SecurityObjects::NoteInfoVec    notes; | ||||||
|  |         uint64_t        created=0; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Firmware>    FirmwareVec; | ||||||
|  |  | ||||||
|  |     struct FirmwareList { | ||||||
|  |         FirmwareVec  firmwares; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct DeviceType { | ||||||
|  |         std::string id; | ||||||
|  |         std::string deviceType; | ||||||
|  |         std::string manufacturer; | ||||||
|  |         std::string model; | ||||||
|  |         std::string policy; | ||||||
|  |         SecurityObjects::NoteInfoVec notes; | ||||||
|  |         uint64_t lastUpdate=0; | ||||||
|  |         uint64_t created=0; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<DeviceType> DeviceTypeVec; | ||||||
|  |  | ||||||
|  |     struct DeviceTypeList { | ||||||
|  |         DeviceTypeVec   deviceTypes; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct RevisionHistoryEntry { | ||||||
|  |         std::string id; | ||||||
|  |         std::string serialNumber; | ||||||
|  |         std::string fromRelease; | ||||||
|  |         std::string toRelease; | ||||||
|  |         std::string commandUUID; | ||||||
|  |         std::string revisionId; | ||||||
|  |         uint64_t    upgraded; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<RevisionHistoryEntry>   RevisionHistoryEntryVec; | ||||||
|  |  | ||||||
|  |     struct RevisionHistoryEntryList { | ||||||
|  |         RevisionHistoryEntryVec history; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct FirmwareAgeDetails { | ||||||
|  |         std::string latestId; | ||||||
|  |         std::string image; | ||||||
|  |         uint64_t imageDate; | ||||||
|  |         std::string revision; | ||||||
|  |         std::string uri; | ||||||
|  |         uint64_t age=0; | ||||||
|  |         bool latest=true; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct DeviceConnectionInformation { | ||||||
|  |         std::string serialNumber; | ||||||
|  |         std::string revision; | ||||||
|  |         std::string deviceType; | ||||||
|  |         std::string endPoint; | ||||||
|  |         uint64_t    lastUpdate; | ||||||
|  |         std::string status; | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct DeviceReport { | ||||||
|  |         uint64_t               snapshot=0; | ||||||
|  |         uint64_t               numberOfDevices=0; | ||||||
|  |         Types::CountedMap      OUI_; | ||||||
|  |         Types::CountedMap      Revisions_; | ||||||
|  |         Types::CountedMap      DeviceTypes_; | ||||||
|  |         Types::CountedMap      Status_; | ||||||
|  |         Types::CountedMap      EndPoints_; | ||||||
|  |         Types::CountedMap      UsingLatest_; | ||||||
|  |         Types::CountedMap      UnknownFirmwares_; | ||||||
|  |         Types::CountedMap      totalSecondsOld_; | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         void reset(); | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif //UCENTRALFMS_RESTAPI_FMSOBJECTS_H | ||||||
							
								
								
									
										263
									
								
								src/RESTObjects/RESTAPI_GWobjects.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/RESTObjects/RESTAPI_GWobjects.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "Poco/JSON/Parser.h" | ||||||
|  | #include "Poco/JSON/Stringifier.h" | ||||||
|  |  | ||||||
|  | #include "Daemon.h" | ||||||
|  | #ifdef	TIP_GATEWAY_SERVICE | ||||||
|  | #include "DeviceRegistry.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "RESTAPI_GWobjects.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | using OpenWifi::RESTAPI_utils::field_to_json; | ||||||
|  | using OpenWifi::RESTAPI_utils::field_from_json; | ||||||
|  | using OpenWifi::RESTAPI_utils::EmbedDocument; | ||||||
|  |  | ||||||
|  | namespace OpenWifi::GWObjects { | ||||||
|  |  | ||||||
|  | 	void Device::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		field_to_json(Obj,"serialNumber", SerialNumber); | ||||||
|  | #ifdef TIP_GATEWAY_SERVICE | ||||||
|  | 		field_to_json(Obj,"deviceType", Daemon::instance()->IdentifyDevice(Compatible)); | ||||||
|  | #endif | ||||||
|  | 		field_to_json(Obj,"macAddress", MACAddress); | ||||||
|  | 		field_to_json(Obj,"manufacturer", Manufacturer); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 		EmbedDocument("configuration", Obj, Configuration); | ||||||
|  | 		field_to_json(Obj,"notes", Notes); | ||||||
|  | 		field_to_json(Obj,"createdTimestamp", CreationTimestamp); | ||||||
|  | 		field_to_json(Obj,"lastConfigurationChange", LastConfigurationChange); | ||||||
|  | 		field_to_json(Obj,"lastConfigurationDownload", LastConfigurationDownload); | ||||||
|  | 		field_to_json(Obj,"lastFWUpdate", LastFWUpdate); | ||||||
|  | 		field_to_json(Obj,"owner", Owner); | ||||||
|  | 		field_to_json(Obj,"location", Location); | ||||||
|  | 		field_to_json(Obj,"venue", Venue); | ||||||
|  | 		field_to_json(Obj,"firmware", Firmware); | ||||||
|  | 		field_to_json(Obj,"compatible", Compatible); | ||||||
|  | 		field_to_json(Obj,"fwUpdatePolicy", FWUpdatePolicy); | ||||||
|  | 		field_to_json(Obj,"devicePassword", DevicePassword); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Device::to_json_with_status(Poco::JSON::Object &Obj) const { | ||||||
|  | 		to_json(Obj); | ||||||
|  |  | ||||||
|  | #ifdef TIP_GATEWAY_SERVICE | ||||||
|  | 		ConnectionState ConState; | ||||||
|  |  | ||||||
|  | 		if (DeviceRegistry()->GetState(SerialNumber, ConState)) { | ||||||
|  | 			ConState.to_json(Obj); | ||||||
|  | 		} else { | ||||||
|  | 			field_to_json(Obj,"ipAddress", ""); | ||||||
|  | 			field_to_json(Obj,"txBytes", (uint64_t) 0); | ||||||
|  | 			field_to_json(Obj,"rxBytes", (uint64_t )0); | ||||||
|  | 			field_to_json(Obj,"messageCount", (uint64_t )0); | ||||||
|  | 			field_to_json(Obj,"connected", false); | ||||||
|  | 			field_to_json(Obj,"lastContact", ""); | ||||||
|  | 			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); | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bool Device::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 		try { | ||||||
|  | 			field_from_json(Obj,"serialNumber",SerialNumber); | ||||||
|  | 			field_from_json(Obj,"deviceType",DeviceType); | ||||||
|  | 			field_from_json(Obj,"macAddress",MACAddress); | ||||||
|  | 			field_from_json(Obj,"configuration",Configuration); | ||||||
|  | 			field_from_json(Obj,"notes",Notes); | ||||||
|  | 			field_from_json(Obj,"manufacturer",Manufacturer); | ||||||
|  | 			field_from_json(Obj,"owner",Owner); | ||||||
|  | 			field_from_json(Obj,"location",Location); | ||||||
|  | 			field_from_json(Obj,"venue",Venue); | ||||||
|  | 			field_from_json(Obj,"compatible",Compatible); | ||||||
|  | 			return true; | ||||||
|  | 		} catch (const Poco::Exception &E) { | ||||||
|  | 		} | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Device::Print() const { | ||||||
|  | 		std::cout << "Device: " << SerialNumber << " DeviceType:" << DeviceType << " MACAddress:" << MACAddress << " Manufacturer:" | ||||||
|  | 				  << Manufacturer << " " << Configuration << std::endl; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Statistics::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("data", Obj, Data); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 		field_to_json(Obj,"recorded", Recorded); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Capabilities::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("capabilities", Obj, Capabilities); | ||||||
|  | 		field_to_json(Obj,"firstUpdate", FirstUpdate); | ||||||
|  | 		field_to_json(Obj,"lastUpdate", LastUpdate); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void DeviceLog::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("data", Obj, Data); | ||||||
|  | 		field_to_json(Obj,"log", Log); | ||||||
|  | 		field_to_json(Obj,"severity", Severity); | ||||||
|  | 		field_to_json(Obj,"recorded", Recorded); | ||||||
|  | 		field_to_json(Obj,"logType", LogType); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void HealthCheck::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("values", Obj, Data); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 		field_to_json(Obj,"sanity", Sanity); | ||||||
|  | 		field_to_json(Obj,"recorded", Recorded); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("configuration", Obj, Configuration); | ||||||
|  | 		field_to_json(Obj,"name", Name); | ||||||
|  | 		field_to_json(Obj,"modelIds", Models); | ||||||
|  | 		field_to_json(Obj,"description", Description); | ||||||
|  | 		field_to_json(Obj,"created", Created); | ||||||
|  | 		field_to_json(Obj,"lastModified", LastModified); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void CommandDetails::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		EmbedDocument("details", Obj, Details); | ||||||
|  | 		EmbedDocument("results", Obj, Results); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 		field_to_json(Obj,"serialNumber", SerialNumber); | ||||||
|  | 		field_to_json(Obj,"command", Command); | ||||||
|  | 		field_to_json(Obj,"errorText", ErrorText); | ||||||
|  | 		field_to_json(Obj,"submittedBy", SubmittedBy); | ||||||
|  | 		field_to_json(Obj,"status", Status); | ||||||
|  | 		field_to_json(Obj,"submitted", Submitted); | ||||||
|  | 		field_to_json(Obj,"executed", Executed); | ||||||
|  | 		field_to_json(Obj,"completed", Completed); | ||||||
|  | 		field_to_json(Obj,"when", RunAt); | ||||||
|  | 		field_to_json(Obj,"errorCode", ErrorCode); | ||||||
|  | 		field_to_json(Obj,"custom", Custom); | ||||||
|  | 		field_to_json(Obj,"waitingForFile", WaitingForFile); | ||||||
|  | 		field_to_json(Obj,"attachFile", AttachDate); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 		try { | ||||||
|  | 			field_from_json(Obj,"name",Name); | ||||||
|  | 			field_from_json(Obj,"configuration",Configuration); | ||||||
|  | 			field_from_json(Obj,"modelIds",Models); | ||||||
|  | 			field_from_json(Obj,"description",Description); | ||||||
|  | 			return true; | ||||||
|  | 		} catch (const Poco::Exception &E) { | ||||||
|  | 		} | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void BlackListedDevice::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		field_to_json(Obj,"serialNumber", serialNumber); | ||||||
|  | 		field_to_json(Obj,"author", author); | ||||||
|  | 		field_to_json(Obj,"reason", reason); | ||||||
|  | 		field_to_json(Obj,"created", created); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	bool BlackListedDevice::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 		try { | ||||||
|  | 			field_from_json(Obj,"serialNumber",serialNumber); | ||||||
|  | 			field_from_json(Obj,"author",author); | ||||||
|  | 			field_from_json(Obj,"reason",reason); | ||||||
|  | 			field_from_json(Obj,"created",created); | ||||||
|  | 			return true; | ||||||
|  | 		} catch (const Poco::Exception &E) { | ||||||
|  | 		} | ||||||
|  | 		return false; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void ConnectionState::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		field_to_json(Obj,"serialNumber", SerialNumber); | ||||||
|  | 		field_to_json(Obj,"ipAddress", Address); | ||||||
|  | 		field_to_json(Obj,"txBytes", TX); | ||||||
|  | 		field_to_json(Obj,"rxBytes", RX); | ||||||
|  | 		field_to_json(Obj,"messageCount", MessageCount); | ||||||
|  | 		field_to_json(Obj,"UUID", UUID); | ||||||
|  | 		field_to_json(Obj,"connected", Connected); | ||||||
|  | 		field_to_json(Obj,"firmware", Firmware); | ||||||
|  | 		field_to_json(Obj,"lastContact", LastContact); | ||||||
|  | 		field_to_json(Obj,"associations_2G", Associations_2G); | ||||||
|  | 		field_to_json(Obj,"associations_5G", Associations_5G); | ||||||
|  |  | ||||||
|  | 		switch(VerifiedCertificate) { | ||||||
|  | 			case NO_CERTIFICATE: | ||||||
|  | 				field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; | ||||||
|  | 			case VALID_CERTIFICATE: | ||||||
|  | 				field_to_json(Obj,"verifiedCertificate", "VALID_CERTIFICATE"); break; | ||||||
|  | 			case MISMATCH_SERIAL: | ||||||
|  | 				field_to_json(Obj,"verifiedCertificate", "MISMATCH_SERIAL"); break; | ||||||
|  | 			case VERIFIED: | ||||||
|  | 				field_to_json(Obj,"verifiedCertificate", "VERIFIED"); break; | ||||||
|  | 			default: | ||||||
|  | 				field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		field_to_json(Obj,"serialNumber", SerialNumber); | ||||||
|  | 		field_to_json(Obj,"server", Server); | ||||||
|  | 		field_to_json(Obj,"port", Port); | ||||||
|  | 		field_to_json(Obj,"token",Token); | ||||||
|  | 		field_to_json(Obj,"timeout", TimeOut); | ||||||
|  | 		field_to_json(Obj,"connectionId",ConnectionId); | ||||||
|  | 		field_to_json(Obj,"commandUUID",CommandUUID); | ||||||
|  | 		field_to_json(Obj,"started", Started); | ||||||
|  | 		field_to_json(Obj,"viewport",ViewPort); | ||||||
|  | 		field_to_json(Obj,"password",DevicePassword); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Dashboard::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 		field_to_json(Obj,"commands",commands); | ||||||
|  | 		field_to_json(Obj,"upTimes",upTimes); | ||||||
|  | 		field_to_json(Obj,"memoryUsed",memoryUsed); | ||||||
|  | 		field_to_json(Obj,"load1",load1); | ||||||
|  | 		field_to_json(Obj,"load5",load5); | ||||||
|  | 		field_to_json(Obj,"load15",load15); | ||||||
|  | 		field_to_json(Obj,"vendors",vendors); | ||||||
|  | 		field_to_json(Obj,"status",status); | ||||||
|  | 		field_to_json(Obj,"deviceType",deviceType); | ||||||
|  | 		field_to_json(Obj,"healths",healths); | ||||||
|  | 		field_to_json(Obj,"certificates",certificates); | ||||||
|  | 		field_to_json(Obj,"lastContact",lastContact); | ||||||
|  | 		field_to_json(Obj,"associations",associations); | ||||||
|  | 		field_to_json(Obj,"snapshot",snapshot); | ||||||
|  | 		field_to_json(Obj,"numberOfDevices",numberOfDevices); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void Dashboard::reset()  { | ||||||
|  | 		commands.clear(); | ||||||
|  | 		upTimes.clear(); | ||||||
|  | 		memoryUsed.clear(); | ||||||
|  | 		load1.clear(); | ||||||
|  | 		load5.clear(); | ||||||
|  | 		load15.clear(); | ||||||
|  | 		vendors.clear(); | ||||||
|  | 		status.clear(); | ||||||
|  | 		deviceType.clear(); | ||||||
|  | 		healths.clear(); | ||||||
|  | 		certificates.clear(); | ||||||
|  | 		lastContact.clear(); | ||||||
|  | 		associations.clear(); | ||||||
|  | 		numberOfDevices = 0 ; | ||||||
|  | 		snapshot = std::time(nullptr); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{ | ||||||
|  | 		field_to_json(Obj,"deviceType", deviceType); | ||||||
|  | 		field_to_json(Obj,"capabilities", capabilities); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										195
									
								
								src/RESTObjects/RESTAPI_GWobjects.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								src/RESTObjects/RESTAPI_GWobjects.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,195 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef UCENTRAL_RESTAPI_OBJECTS_H | ||||||
|  | #define UCENTRAL_RESTAPI_OBJECTS_H | ||||||
|  |  | ||||||
|  | #include "Poco/JSON/Object.h" | ||||||
|  | #include "RESTAPI_SecurityObjects.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi::GWObjects { | ||||||
|  |  | ||||||
|  | 	enum CertificateValidation { | ||||||
|  | 		NO_CERTIFICATE, | ||||||
|  | 		VALID_CERTIFICATE, | ||||||
|  | 		MISMATCH_SERIAL, | ||||||
|  | 		VERIFIED | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct ConnectionState { | ||||||
|  | 		uint64_t MessageCount = 0 ; | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		std::string Address; | ||||||
|  | 		uint64_t UUID = 0 ; | ||||||
|  | 		uint64_t PendingUUID = 0 ; | ||||||
|  | 		uint64_t TX = 0, RX = 0; | ||||||
|  | 		uint64_t Associations_2G=0; | ||||||
|  | 		uint64_t Associations_5G=0; | ||||||
|  | 		bool Connected = false; | ||||||
|  | 		uint64_t LastContact=0; | ||||||
|  | 		std::string Firmware; | ||||||
|  | 		CertificateValidation VerifiedCertificate = NO_CERTIFICATE; | ||||||
|  | 		std::string Compatible; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct Device { | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		std::string DeviceType; | ||||||
|  | 		std::string MACAddress; | ||||||
|  | 		std::string Manufacturer; | ||||||
|  | 		std::string Configuration; | ||||||
|  | 		SecurityObjects::NoteInfoVec 	Notes; | ||||||
|  | 		std::string Owner; | ||||||
|  | 		std::string Location; | ||||||
|  | 		std::string Firmware; | ||||||
|  | 		std::string Compatible; | ||||||
|  | 		std::string FWUpdatePolicy; | ||||||
|  | 		uint64_t UUID; | ||||||
|  | 		uint64_t CreationTimestamp; | ||||||
|  | 		uint64_t LastConfigurationChange; | ||||||
|  | 		uint64_t LastConfigurationDownload; | ||||||
|  | 		uint64_t LastFWUpdate; | ||||||
|  | 		std::string Venue; | ||||||
|  | 		std::string DevicePassword; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 		void to_json_with_status(Poco::JSON::Object &Obj) const; | ||||||
|  | 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 		void Print() const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct Statistics { | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		uint64_t 	UUID; | ||||||
|  | 		std::string Data; | ||||||
|  | 		uint64_t 	Recorded; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct HealthCheck { | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		uint64_t 	UUID; | ||||||
|  | 		std::string Data; | ||||||
|  | 		uint64_t 	Recorded; | ||||||
|  | 		uint64_t 	Sanity; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct Capabilities { | ||||||
|  | 		std::string Capabilities; | ||||||
|  | 		uint64_t 	FirstUpdate; | ||||||
|  | 		uint64_t 	LastUpdate; | ||||||
|  | 		void 		to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct DeviceLog { | ||||||
|  | 		enum Level { | ||||||
|  | 			LOG_EMERG = 0,	 /* system is unusable */ | ||||||
|  | 			LOG_ALERT = 1,	 /* action must be taken immediately */ | ||||||
|  | 			LOG_CRIT = 2,	 /* critical conditions */ | ||||||
|  | 			LOG_ERR = 3,	 /* error conditions */ | ||||||
|  | 			LOG_WARNING = 4, /* warning conditions */ | ||||||
|  | 			LOG_NOTICE = 5,	 /* normal but significant condition */ | ||||||
|  | 			LOG_INFO = 6,	 /* informational */ | ||||||
|  | 			LOG_DEBUG = 7	 /* debug-level messages */ | ||||||
|  | 		}; | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		std::string Log; | ||||||
|  | 		std::string Data; | ||||||
|  | 		uint64_t 	Severity; | ||||||
|  | 		uint64_t 	Recorded; | ||||||
|  | 		uint64_t 	LogType; | ||||||
|  | 		uint64_t 	UUID; | ||||||
|  | 		void 		to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct DefaultConfiguration { | ||||||
|  | 		std::string Name; | ||||||
|  | 		std::string Configuration; | ||||||
|  | 		std::string Models; | ||||||
|  | 		std::string Description; | ||||||
|  | 		uint64_t 	Created; | ||||||
|  | 		uint64_t 	LastModified; | ||||||
|  | 		void 		to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 		bool 		from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct CommandDetails { | ||||||
|  | 		std::string UUID; | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		std::string Command; | ||||||
|  | 		std::string Status; | ||||||
|  | 		std::string SubmittedBy; | ||||||
|  | 		std::string Results; | ||||||
|  | 		std::string Details; | ||||||
|  | 		std::string ErrorText; | ||||||
|  | 		uint64_t Submitted = time(nullptr); | ||||||
|  | 		uint64_t Executed = 0; | ||||||
|  | 		uint64_t Completed = 0 ; | ||||||
|  | 		uint64_t RunAt = 0 ; | ||||||
|  | 		uint64_t ErrorCode = 0 ; | ||||||
|  | 		uint64_t Custom = 0 ; | ||||||
|  | 		uint64_t WaitingForFile = 0 ; | ||||||
|  | 		uint64_t AttachDate = 0 ; | ||||||
|  | 		uint64_t AttachSize = 0 ; | ||||||
|  | 		std::string AttachType; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct BlackListedDevice { | ||||||
|  | 		std::string serialNumber; | ||||||
|  | 		std::string reason; | ||||||
|  | 		std::string author; | ||||||
|  | 		uint64_t created; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct RttySessionDetails { | ||||||
|  | 		std::string SerialNumber; | ||||||
|  | 		std::string Server; | ||||||
|  | 		uint64_t 	Port; | ||||||
|  | 		std::string Token; | ||||||
|  | 		uint64_t 	TimeOut; | ||||||
|  | 		std::string ConnectionId; | ||||||
|  | 		uint64_t 	Started; | ||||||
|  | 		std::string CommandUUID; | ||||||
|  | 		uint64_t 	ViewPort; | ||||||
|  | 		std::string DevicePassword; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct Dashboard { | ||||||
|  | 		uint64_t 		  snapshot; | ||||||
|  | 		uint64_t 		  numberOfDevices; | ||||||
|  | 		Types::CountedMap commands; | ||||||
|  | 		Types::CountedMap upTimes; | ||||||
|  | 		Types::CountedMap memoryUsed; | ||||||
|  | 		Types::CountedMap load1; | ||||||
|  | 		Types::CountedMap load5; | ||||||
|  | 		Types::CountedMap load15; | ||||||
|  | 		Types::CountedMap vendors; | ||||||
|  | 		Types::CountedMap status; | ||||||
|  | 		Types::CountedMap deviceType; | ||||||
|  | 		Types::CountedMap healths; | ||||||
|  | 		Types::CountedMap certificates; | ||||||
|  | 		Types::CountedMap lastContact; | ||||||
|  | 		Types::CountedMap associations; | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 		void reset(); | ||||||
|  | 	}; | ||||||
|  |  | ||||||
|  | 	struct CapabilitiesModel { | ||||||
|  | 		std::string deviceType; | ||||||
|  | 		std::string capabilities; | ||||||
|  |  | ||||||
|  | 		void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	}; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //UCENTRAL_RESTAPI_OBJECTS_H | ||||||
							
								
								
									
										450
									
								
								src/RESTObjects/RESTAPI_ProvObjects.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										450
									
								
								src/RESTObjects/RESTAPI_ProvObjects.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,450 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "RESTAPI_ProvObjects.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi::ProvObjects { | ||||||
|  |  | ||||||
|  |     void ObjectInfo::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"id",id); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"name",name); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"description",description); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"created",created); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"modified",modified); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"notes",notes); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj,"tags",tags); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ObjectInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"id",id); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"name",name); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"description",description); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"created",created); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"modified",modified); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"notes",notes); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj,"tags",tags); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ManagementPolicyEntry::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"users",users); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"resources",resources); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"access",access); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"policy",policy); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ManagementPolicyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"users",users); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"resources",resources); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"access",access); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"policy",policy); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ManagementPolicy::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "inUse", inUse); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "entity", entity); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ManagementPolicy::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj, "entries", entries); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj, "inUse", inUse); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj, "entity", entity); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Entity::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"venues",venues); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"children",children); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"contacts",contacts); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"locations",locations); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"devices",devices); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool Entity::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"venues",venues); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"children",children); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"contacts",contacts); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"locations",locations); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"devices",devices); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DiGraphEntry::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"child",child); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DiGraphEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"child",child); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Venue::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"children",children); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"devices",devices); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"topology",topology); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"design",design); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"contact",contact); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"location",location); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool Venue::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"children",children); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"devices",devices); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"topology",topology); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"design",design); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"contact",contact); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"location",location); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void UserInfoDigest::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"id",id); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"entity",loginId); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"children",userType); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool UserInfoDigest::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"id",id); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",loginId); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"children",userType); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ManagementRole::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"users",users); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ManagementRole::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"users",users); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Location::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"type",OpenWifi::ProvObjects::to_string(type)); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"buildingName",buildingName); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"addressLines",addressLines); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"city",city); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"state",state); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"postal",postal); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"country",country); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"phones",phones); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"geoCode",geoCode); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool Location::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             std::string tmp_type; | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"type", tmp_type); | ||||||
|  |             type = location_from_string(tmp_type); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"buildingName",buildingName); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"addressLines",addressLines); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"city",city); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"state",state); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"postal",postal); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"country",country); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"phones",phones); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Contact::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"type", to_string(type)); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"title",title); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"salutation",salutation); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"firstname",firstname); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"lastname",lastname); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"initials",initials); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"visual",visual); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"phones",phones); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"primaryEmail",primaryEmail); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"secondaryEmail",secondaryEmail); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"accessPIN",accessPIN); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool Contact::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             std::string tmp_type; | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"type", tmp_type); | ||||||
|  |             type = contact_from_string(tmp_type); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"title",title); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"salutation",salutation); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"firstname",firstname); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"lastname",lastname); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"initials",initials); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"visual",visual); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"phones",phones); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"primaryEmail",primaryEmail); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"secondaryEmail",secondaryEmail); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"accessPIN",accessPIN); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void InventoryTag::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "serialNumber", serialNumber); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "venue", venue); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "entity", entity); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "subscriber", subscriber); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "deviceType", deviceType); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "qrCode", qrCode); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "geoCode", geoCode); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "location", location); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "contact", contact); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"serialNumber",serialNumber); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"venue",venue); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"subscriber",subscriber); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"deviceType",deviceType); | ||||||
|  |             RESTAPI_utils::field_from_json(Obj, "qrCode", qrCode); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"location",location); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"contact",contact); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"name", name); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"description", description); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"weight", weight); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"configuration", configuration); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceConfigurationElement::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"name",name); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"description",description); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"weight",weight); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"configuration",configuration); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void DeviceConfiguration::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         info.to_json(Obj); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"deviceTypes",deviceTypes); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"configuration",configuration); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"variables",variables); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||||
|  |         RESTAPI_utils::field_to_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool DeviceConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             info.from_json(Obj); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"deviceTypes",deviceTypes); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"configuration",configuration); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"variables",variables); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void Report::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "snapshot", snapShot); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "devices", tenants); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     void Report::reset() { | ||||||
|  |         tenants.clear(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ExpandedUseEntry::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "uuid", uuid); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "name", name); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "description", description); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ExpandedUseEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"uuid",uuid); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"name",name); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"description",description); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ExpandedUseEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "type", type); | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ExpandedUseEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"type",type); | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entries",entries); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void ExpandedUseEntryMapList::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool ExpandedUseEntryMapList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||||
|  |         try { | ||||||
|  |             RESTAPI_utils::field_from_json( Obj,"entries",entries); | ||||||
|  |             return true; | ||||||
|  |         } catch(...) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||||
|  |         if(O->has("name")) | ||||||
|  |             I.name = O->get("name").toString(); | ||||||
|  |         if(O->has("description")) | ||||||
|  |             I.description = O->get("description").toString(); | ||||||
|  |         SecurityObjects::MergeNotes(O,U,I.notes); | ||||||
|  |         I.modified = std::time(nullptr); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | }; | ||||||
							
								
								
									
										324
									
								
								src/RESTObjects/RESTAPI_ProvObjects.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										324
									
								
								src/RESTObjects/RESTAPI_ProvObjects.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,324 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef OWPROV_RESTAPI_PROVOBJECTS_H | ||||||
|  | #define OWPROV_RESTAPI_PROVOBJECTS_H | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include "RESTAPI_SecurityObjects.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi::ProvObjects { | ||||||
|  |  | ||||||
|  |     struct ObjectInfo { | ||||||
|  |         Types::UUID_t   id; | ||||||
|  |         std::string     name; | ||||||
|  |         std::string     description; | ||||||
|  |         SecurityObjects::NoteInfoVec notes; | ||||||
|  |         uint64_t        created=0; | ||||||
|  |         uint64_t        modified=0; | ||||||
|  |         Types::TagList  tags; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ManagementPolicyEntry { | ||||||
|  |         Types::UUIDvec_t users; | ||||||
|  |         Types::UUIDvec_t resources; | ||||||
|  |         Types::StringVec access; | ||||||
|  |         std::string policy; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ManagementPolicy { | ||||||
|  |         ObjectInfo          info; | ||||||
|  |         std::vector<ManagementPolicyEntry>  entries; | ||||||
|  |         Types::StringVec    inUse; | ||||||
|  |         Types::UUID_t       entity; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<ManagementPolicy>      ManagementPolicyVec; | ||||||
|  |  | ||||||
|  |     struct Entity { | ||||||
|  |         ObjectInfo              info; | ||||||
|  |         Types::UUID_t           parent; | ||||||
|  |         Types::UUIDvec_t        children; | ||||||
|  |         Types::UUIDvec_t        venues; | ||||||
|  |         Types::UUIDvec_t        contacts;       // all contacts associated in this entity | ||||||
|  |         Types::UUIDvec_t        locations;      // all locations associated in this entity | ||||||
|  |         Types::UUID_t           managementPolicy; | ||||||
|  |         Types::UUIDvec_t        deviceConfiguration; | ||||||
|  |         Types::UUIDvec_t        devices; | ||||||
|  |         std::string             rrm; | ||||||
|  |         Types::StringVec        sourceIP; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Entity>      EntityVec; | ||||||
|  |  | ||||||
|  |     struct DiGraphEntry { | ||||||
|  |         Types::UUID_t parent; | ||||||
|  |         Types::UUID_t child; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     typedef std::vector<DiGraphEntry>   DiGraph; | ||||||
|  |  | ||||||
|  |     struct Venue { | ||||||
|  |         ObjectInfo          info; | ||||||
|  |         Types::UUID_t       entity; | ||||||
|  |         Types::UUID_t       parent; | ||||||
|  |         Types::UUIDvec_t    children; | ||||||
|  |         Types::UUID_t       managementPolicy; | ||||||
|  |         Types::UUIDvec_t    devices; | ||||||
|  |         DiGraph             topology; | ||||||
|  |         std::string         design; | ||||||
|  |         Types::UUIDvec_t    deviceConfiguration; | ||||||
|  |         std::string         contact; | ||||||
|  |         std::string         location; | ||||||
|  |         std::string         rrm; | ||||||
|  |         Types::StringVec    sourceIP; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Venue>      VenueVec; | ||||||
|  |  | ||||||
|  |     struct UserInfoDigest { | ||||||
|  |         std::string     id; | ||||||
|  |         std::string     loginId; | ||||||
|  |         std::string     userType; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ManagementRole { | ||||||
|  |         ObjectInfo          info; | ||||||
|  |         Types::UUID_t       managementPolicy; | ||||||
|  |         Types::UUIDvec_t    users; | ||||||
|  |         Types::StringVec    inUse; | ||||||
|  |         Types::UUID_t       entity; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<ManagementRole>      ManagementRoleVec; | ||||||
|  |  | ||||||
|  |     enum LocationType { | ||||||
|  |         LT_SERVICE, LT_EQUIPMENT, LT_AUTO, LT_MANUAL, | ||||||
|  |         LT_SPECIAL, LT_UNKNOWN, LT_CORPORATE | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     inline std::string to_string(LocationType L) { | ||||||
|  |         switch(L) { | ||||||
|  |             case LT_SERVICE: return "SERVICE"; | ||||||
|  |             case LT_EQUIPMENT: return "EQUIPMENT"; | ||||||
|  |             case LT_AUTO: return "AUTO"; | ||||||
|  |             case LT_MANUAL: return "MANUAL"; | ||||||
|  |             case LT_SPECIAL: return "SPECIAL"; | ||||||
|  |             case LT_UNKNOWN: return "UNKNOWN"; | ||||||
|  |             case LT_CORPORATE: return "CORPORATE"; | ||||||
|  |             default: return "UNKNOWN"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline LocationType location_from_string(const std::string &S) { | ||||||
|  |         if(!Poco::icompare(S,"SERVICE")) | ||||||
|  |             return LT_SERVICE; | ||||||
|  |         else if(!Poco::icompare(S,"EQUIPMENT")) | ||||||
|  |             return LT_EQUIPMENT; | ||||||
|  |         else if(!Poco::icompare(S,"AUTO")) | ||||||
|  |             return LT_AUTO; | ||||||
|  |         else if(!Poco::icompare(S,"MANUAL")) | ||||||
|  |             return LT_MANUAL; | ||||||
|  |         else if(!Poco::icompare(S,"SPECIAL")) | ||||||
|  |             return LT_SPECIAL; | ||||||
|  |         else if(!Poco::icompare(S,"UNKNOWN")) | ||||||
|  |             return LT_UNKNOWN; | ||||||
|  |         else if(!Poco::icompare(S,"CORPORATE")) | ||||||
|  |             return LT_CORPORATE; | ||||||
|  |         return LT_UNKNOWN; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Location { | ||||||
|  |         ObjectInfo          info; | ||||||
|  |         LocationType        type; | ||||||
|  |         std::string         buildingName; | ||||||
|  |         Types::StringVec    addressLines; | ||||||
|  |         std::string         city; | ||||||
|  |         std::string         state; | ||||||
|  |         std::string         postal; | ||||||
|  |         std::string         country; | ||||||
|  |         Types::StringVec    phones; | ||||||
|  |         Types::StringVec    mobiles; | ||||||
|  |         std::string         geoCode; | ||||||
|  |         Types::StringVec    inUse; | ||||||
|  |         Types::UUID_t       entity; | ||||||
|  |         Types::UUID_t       managementPolicy; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Location>      LocationVec; | ||||||
|  |  | ||||||
|  |     enum ContactType { | ||||||
|  |         CT_SUBSCRIBER, CT_USER, CT_INSTALLER, CT_CSR, CT_MANAGER, | ||||||
|  |         CT_BUSINESSOWNER, CT_TECHNICIAN, CT_CORPORATE, CT_UNKNOWN | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     inline std::string to_string(ContactType L) { | ||||||
|  |         switch(L) { | ||||||
|  |             case CT_SUBSCRIBER: return "SUBSCRIBER"; | ||||||
|  |             case CT_USER: return "USER"; | ||||||
|  |             case CT_INSTALLER: return "INSTALLER"; | ||||||
|  |             case CT_CSR: return "CSR"; | ||||||
|  |             case CT_MANAGER: return "MANAGER"; | ||||||
|  |             case CT_BUSINESSOWNER: return "BUSINESSOWNER"; | ||||||
|  |             case CT_TECHNICIAN: return "TECHNICIAN"; | ||||||
|  |             case CT_CORPORATE: return "CORPORATE"; | ||||||
|  |             case CT_UNKNOWN: return "UNKNOWN"; | ||||||
|  |             default: return "UNKNOWN"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline ContactType contact_from_string(const std::string &S) { | ||||||
|  |         if(!Poco::icompare(S,"SUBSCRIBER")) | ||||||
|  |             return CT_SUBSCRIBER; | ||||||
|  |         else if(!Poco::icompare(S,"USER")) | ||||||
|  |             return CT_USER; | ||||||
|  |         else if(!Poco::icompare(S,"INSTALLER")) | ||||||
|  |             return CT_INSTALLER; | ||||||
|  |         else if(!Poco::icompare(S,"CSR")) | ||||||
|  |             return CT_CSR; | ||||||
|  |         else if(!Poco::icompare(S,"BUSINESSOWNER")) | ||||||
|  |             return CT_BUSINESSOWNER; | ||||||
|  |         else if(!Poco::icompare(S,"TECHNICIAN")) | ||||||
|  |             return CT_TECHNICIAN; | ||||||
|  |         else if(!Poco::icompare(S,"CORPORATE")) | ||||||
|  |             return CT_CORPORATE; | ||||||
|  |         else if(!Poco::icompare(S,"UNKNOWN")) | ||||||
|  |             return CT_UNKNOWN; | ||||||
|  |         return CT_UNKNOWN; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     struct Contact { | ||||||
|  |         ObjectInfo  info; | ||||||
|  |         ContactType type=CT_USER; | ||||||
|  |         std::string title; | ||||||
|  |         std::string salutation; | ||||||
|  |         std::string firstname; | ||||||
|  |         std::string lastname; | ||||||
|  |         std::string initials; | ||||||
|  |         std::string visual; | ||||||
|  |         Types::StringVec mobiles; | ||||||
|  |         Types::StringVec phones; | ||||||
|  |         std::string primaryEmail; | ||||||
|  |         std::string secondaryEmail; | ||||||
|  |         std::string accessPIN; | ||||||
|  |         Types::StringVec inUse; | ||||||
|  |         Types::UUID_t   entity; | ||||||
|  |         Types::UUID_t   managementPolicy; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Contact>      ContactVec; | ||||||
|  |  | ||||||
|  |     struct DeviceConfigurationElement { | ||||||
|  |         std::string name; | ||||||
|  |         std::string description; | ||||||
|  |         uint64_t    weight; | ||||||
|  |         std::string configuration; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<DeviceConfigurationElement> DeviceConfigurationElementVec; | ||||||
|  |  | ||||||
|  |     struct DeviceConfiguration { | ||||||
|  |     ObjectInfo                          info; | ||||||
|  |         Types::UUID_t                   managementPolicy; | ||||||
|  |         Types::StringVec                deviceTypes; | ||||||
|  |         DeviceConfigurationElementVec   configuration; | ||||||
|  |         Types::StringVec                inUse; | ||||||
|  |         Types::StringPairVec            variables; | ||||||
|  |         std::string                     rrm; | ||||||
|  |         std::string                     firmwareUpgrade; | ||||||
|  |         bool                            firmwareRCOnly=false; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<DeviceConfiguration>      DeviceConfigurationVec; | ||||||
|  |  | ||||||
|  |     struct InventoryTag { | ||||||
|  |         ObjectInfo      info; | ||||||
|  |         std::string     serialNumber; | ||||||
|  |         std::string     venue; | ||||||
|  |         std::string     entity; | ||||||
|  |         std::string     subscriber; | ||||||
|  |         std::string     deviceType; | ||||||
|  |         std::string     qrCode; | ||||||
|  |         std::string     geoCode; | ||||||
|  |         std::string     location; | ||||||
|  |         std::string     contact; | ||||||
|  |         std::string     deviceConfiguration; | ||||||
|  |         std::string     rrm; | ||||||
|  |         Types::UUID_t   managementPolicy; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<InventoryTag>      InventoryTagVec; | ||||||
|  |  | ||||||
|  |     struct Report { | ||||||
|  |         uint64_t            snapShot=0; | ||||||
|  |         Types::CountedMap   tenants; | ||||||
|  |  | ||||||
|  |         void        reset(); | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ExpandedUseEntry { | ||||||
|  |         std::string uuid; | ||||||
|  |         std::string name; | ||||||
|  |         std::string description; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ExpandedUseEntryList { | ||||||
|  |         std::string                     type; | ||||||
|  |         std::vector<ExpandedUseEntry>   entries; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct ExpandedUseEntryMapList { | ||||||
|  |         std::vector<ExpandedUseEntryList>    entries; | ||||||
|  |  | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif //OWPROV_RESTAPI_PROVOBJECTS_H | ||||||
| @@ -9,8 +9,8 @@ | |||||||
| #include "Poco/JSON/Parser.h" | #include "Poco/JSON/Parser.h" | ||||||
| #include "Poco/JSON/Stringifier.h" | #include "Poco/JSON/Stringifier.h" | ||||||
| 
 | 
 | ||||||
|  | #include "framework/MicroService.h" | ||||||
| #include "RESTAPI_SecurityObjects.h" | #include "RESTAPI_SecurityObjects.h" | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
| 
 | 
 | ||||||
| using OpenWifi::RESTAPI_utils::field_to_json; | using OpenWifi::RESTAPI_utils::field_to_json; | ||||||
| using OpenWifi::RESTAPI_utils::field_from_json; | using OpenWifi::RESTAPI_utils::field_from_json; | ||||||
| @@ -58,21 +58,28 @@ namespace OpenWifi::SecurityObjects { | |||||||
|             return CSR; |             return CSR; | ||||||
|         else if (!Poco::icompare(U, "system")) |         else if (!Poco::icompare(U, "system")) | ||||||
|             return SYSTEM; |             return SYSTEM; | ||||||
|         else if (!Poco::icompare(U, "special")) |         else if (!Poco::icompare(U, "installer")) | ||||||
|             return SPECIAL; |             return INSTALLER; | ||||||
|  |         else if (!Poco::icompare(U, "noc")) | ||||||
|  |             return NOC; | ||||||
|  |         else if (!Poco::icompare(U, "accounting")) | ||||||
|  |             return ACCOUNTING; | ||||||
|         return UNKNOWN; |         return UNKNOWN; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string UserTypeToString(USER_ROLE U) { |     std::string UserTypeToString(USER_ROLE U) { | ||||||
|         switch(U) { |         switch(U) { | ||||||
|             case UNKNOWN: return "unknown"; |  | ||||||
|             case ROOT: return "root"; |             case ROOT: return "root"; | ||||||
|  |             case ADMIN: return "admin"; | ||||||
|             case SUBSCRIBER: return "subscriber"; |             case SUBSCRIBER: return "subscriber"; | ||||||
|             case CSR: return "csr"; |             case CSR: return "csr"; | ||||||
|             case SYSTEM: return "system"; |             case SYSTEM: return "system"; | ||||||
|             case SPECIAL: return "special"; |             case INSTALLER: return "installer"; | ||||||
|             case ADMIN: return "admin"; |             case NOC: return "noc"; | ||||||
|             default: return "unknown"; |             case ACCOUNTING: return "accounting"; | ||||||
|  |             case UNKNOWN: | ||||||
|  |             default: | ||||||
|  |                 return "unknown"; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @@ -125,6 +132,94 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	void MobilePhoneNumber::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 	    field_to_json(Obj,"number", number); | ||||||
|  | 	    field_to_json(Obj,"verified", verified); | ||||||
|  | 	    field_to_json(Obj,"primary", primary); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool MobilePhoneNumber::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 	    try { | ||||||
|  | 	        field_from_json(Obj,"number",number); | ||||||
|  | 	        field_from_json(Obj,"verified",verified); | ||||||
|  | 	        field_from_json(Obj,"primary",primary); | ||||||
|  | 	        return true; | ||||||
|  | 	    } catch (...) { | ||||||
|  | 
 | ||||||
|  | 	    } | ||||||
|  | 	    return false; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	void MfaAuthInfo::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 	    field_to_json(Obj,"enabled", enabled); | ||||||
|  | 	    field_to_json(Obj,"method", method); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool MfaAuthInfo::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 	    try { | ||||||
|  | 	        field_from_json(Obj,"enabled",enabled); | ||||||
|  | 	        field_from_json(Obj,"method",method); | ||||||
|  | 	        return true; | ||||||
|  | 	    } catch (...) { | ||||||
|  | 
 | ||||||
|  | 	    } | ||||||
|  | 	    return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void UserLoginLoginExtensions::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  | 	    field_to_json(Obj, "mobiles", mobiles); | ||||||
|  | 	    field_to_json(Obj, "mfa", mfa); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bool UserLoginLoginExtensions::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 	    try { | ||||||
|  | 	        field_from_json(Obj,"mobiles",mobiles); | ||||||
|  | 	        field_from_json(Obj,"mfa",mfa); | ||||||
|  | 	        return true; | ||||||
|  | 	    } catch (...) { | ||||||
|  | 
 | ||||||
|  | 	    } | ||||||
|  | 	    return false; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  |     void MFAChallengeRequest::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "uuid", uuid); | ||||||
|  |         field_to_json(Obj, "question", question); | ||||||
|  |         field_to_json(Obj, "created", created); | ||||||
|  |         field_to_json(Obj, "method", method); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool MFAChallengeRequest::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  | 	    try { | ||||||
|  | 	        field_from_json(Obj,"uuid",uuid); | ||||||
|  | 	        field_from_json(Obj,"question",question); | ||||||
|  | 	        field_from_json(Obj,"created",created); | ||||||
|  | 	        field_from_json(Obj,"method",method); | ||||||
|  | 	        return true; | ||||||
|  | 	    } catch (...) { | ||||||
|  | 
 | ||||||
|  | 	    } | ||||||
|  | 	    return false; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  |     void MFAChallengeResponse::to_json(Poco::JSON::Object &Obj) const { | ||||||
|  |         field_to_json(Obj, "uuid", uuid); | ||||||
|  |         field_to_json(Obj, "answer", answer); | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool MFAChallengeResponse::from_json(Poco::JSON::Object::Ptr Obj) { | ||||||
|  |         try { | ||||||
|  |             field_from_json(Obj,"uuid",uuid); | ||||||
|  |             field_from_json(Obj,"answer",answer); | ||||||
|  |             return true; | ||||||
|  |         } catch (...) { | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     void UserInfo::to_json(Poco::JSON::Object &Obj) const { |     void UserInfo::to_json(Poco::JSON::Object &Obj) const { | ||||||
| 		field_to_json(Obj,"Id",Id); | 		field_to_json(Obj,"Id",Id); | ||||||
| 		field_to_json(Obj,"name",name); | 		field_to_json(Obj,"name",name); | ||||||
| @@ -303,20 +398,31 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|     bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes) { |     bool MergeNotes(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes) { | ||||||
| 	    try { | 	    try { | ||||||
| 	        SecurityObjects::NoteInfoVec NIV; | 	        if(Obj->has("notes") && Obj->isArray("notes")) { | ||||||
| 	        NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get("notes").toString()); | 	            SecurityObjects::NoteInfoVec NIV; | ||||||
| 	        for(auto const &i:NIV) { | 	            NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get("notes").toString()); | ||||||
| 	            SecurityObjects::NoteInfo   ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note}; | 	            for(auto const &i:NIV) { | ||||||
| 	            Notes.push_back(ii); | 	                SecurityObjects::NoteInfo   ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note}; | ||||||
|  | 	                Notes.push_back(ii); | ||||||
|  | 	            } | ||||||
| 	        } | 	        } | ||||||
|  | 	        return true; | ||||||
| 	    } catch(...) { | 	    } catch(...) { | ||||||
| 
 | 
 | ||||||
| 	    } | 	    } | ||||||
| 	    return false; | 	    return false; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes) { | ||||||
|  | 	    for(auto const &i:NewNotes) { | ||||||
|  | 	        SecurityObjects::NoteInfo   ii{.created=(uint64_t)std::time(nullptr), .createdBy=UInfo.email, .note=i.note}; | ||||||
|  | 	        ExistingNotes.push_back(ii); | ||||||
|  | 	    } | ||||||
|  |         return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	void ProfileAction::to_json(Poco::JSON::Object &Obj) const { | 	void ProfileAction::to_json(Poco::JSON::Object &Obj) const { | ||||||
| 		field_to_json(Obj,"resource", resource); | 		field_to_json(Obj,"resource", resource); | ||||||
| 		field_to_json<ResourceAccessType>(Obj,"access", access, ResourceAccessTypeToString); | 		field_to_json<ResourceAccessType>(Obj,"access", access, ResourceAccessTypeToString); | ||||||
| @@ -10,7 +10,7 @@ | |||||||
| #define UCENTRAL_RESTAPI_SECURITYOBJECTS_H | #define UCENTRAL_RESTAPI_SECURITYOBJECTS_H | ||||||
| 
 | 
 | ||||||
| #include "Poco/JSON/Object.h" | #include "Poco/JSON/Object.h" | ||||||
| #include "OpenWifiTypes.h" | #include "../framework/OpenWifiTypes.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi::SecurityObjects { | namespace OpenWifi::SecurityObjects { | ||||||
| 
 | 
 | ||||||
| @@ -42,7 +42,7 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 	}; | 	}; | ||||||
| 
 | 
 | ||||||
|     enum USER_ROLE { |     enum USER_ROLE { | ||||||
|         UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, SPECIAL |         UNKNOWN, ROOT, ADMIN, SUBSCRIBER, CSR, SYSTEM, INSTALLER, NOC, ACCOUNTING | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     USER_ROLE UserTypeFromString(const std::string &U); |     USER_ROLE UserTypeFromString(const std::string &U); | ||||||
| @@ -57,6 +57,49 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 	}; | 	}; | ||||||
| 	typedef std::vector<NoteInfo>	NoteInfoVec; | 	typedef std::vector<NoteInfo>	NoteInfoVec; | ||||||
| 
 | 
 | ||||||
|  | 	struct MobilePhoneNumber { | ||||||
|  | 	    std::string number; | ||||||
|  | 	    bool verified; | ||||||
|  | 	    bool primary; | ||||||
|  | 
 | ||||||
|  | 	    void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct MfaAuthInfo { | ||||||
|  | 	    bool enabled; | ||||||
|  | 	    std::string method; | ||||||
|  | 
 | ||||||
|  | 	    void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct UserLoginLoginExtensions { | ||||||
|  | 	    std::vector<MobilePhoneNumber>  mobiles; | ||||||
|  | 	    struct MfaAuthInfo mfa; | ||||||
|  | 
 | ||||||
|  | 	    void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	struct MFAChallengeRequest { | ||||||
|  | 	    std::string uuid; | ||||||
|  | 	    std::string question; | ||||||
|  | 	    std::string method; | ||||||
|  | 	    uint64_t    created; | ||||||
|  | 
 | ||||||
|  | 	    void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  | 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  |     struct MFAChallengeResponse { | ||||||
|  |         std::string uuid; | ||||||
|  |         std::string answer; | ||||||
|  | 
 | ||||||
|  |         void to_json(Poco::JSON::Object &Obj) const; | ||||||
|  |         bool from_json(Poco::JSON::Object::Ptr Obj); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
| 	struct UserInfo { | 	struct UserInfo { | ||||||
|         std::string Id; |         std::string Id; | ||||||
| 		std::string name; | 		std::string name; | ||||||
| @@ -81,7 +124,7 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 		bool suspended = false; | 		bool suspended = false; | ||||||
| 		bool blackListed = false; | 		bool blackListed = false; | ||||||
|         USER_ROLE userRole; |         USER_ROLE userRole; | ||||||
| 		std::string userTypeProprietaryInfo; |         UserLoginLoginExtensions userTypeProprietaryInfo; | ||||||
| 		std::string securityPolicy; | 		std::string securityPolicy; | ||||||
| 		uint64_t securityPolicyChange = 0 ; | 		uint64_t securityPolicyChange = 0 ; | ||||||
| 		std::string currentPassword; | 		std::string currentPassword; | ||||||
| @@ -94,7 +137,9 @@ namespace OpenWifi::SecurityObjects { | |||||||
| 	}; | 	}; | ||||||
| 	typedef std::vector<UserInfo>   UserInfoVec; | 	typedef std::vector<UserInfo>   UserInfoVec; | ||||||
| 
 | 
 | ||||||
| 	bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes); | 	// bool append_from_json(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes);
 | ||||||
|  | 	bool MergeNotes(Poco::JSON::Object::Ptr Obj, const UserInfo &UInfo, NoteInfoVec & Notes); | ||||||
|  | 	bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes); | ||||||
| 
 | 
 | ||||||
| 	struct InternalServiceInfo { | 	struct InternalServiceInfo { | ||||||
| 		std::string privateURI; | 		std::string privateURI; | ||||||
							
								
								
									
										82
									
								
								src/SMSSender.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/SMSSender.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-09. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include <aws/sns/SNSClient.h> | ||||||
|  | #include <aws/sns/model/PublishRequest.h> | ||||||
|  | #include <aws/sns/model/PublishResult.h> | ||||||
|  | #include <aws/sns/model/GetSMSAttributesRequest.h> | ||||||
|  |  | ||||||
|  | #include "MFAServer.h" | ||||||
|  | #include "SMS_provider_aws.h" | ||||||
|  | #include "SMS_provider_twilio.h" | ||||||
|  | #include "SMSSender.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     class SMSSender * SMSSender::instance_ = nullptr; | ||||||
|  |  | ||||||
|  |     int SMSSender::Start() { | ||||||
|  |         Provider_ = MicroService::instance().ConfigGetString("sms.provider","aws"); | ||||||
|  |         if(Provider_=="aws") { | ||||||
|  |             ProviderImpl_ = std::make_unique<SMS_provider_aws>(Logger_); | ||||||
|  |         } else if(Provider_=="twilio") { | ||||||
|  |             ProviderImpl_ = std::make_unique<SMS_provider_twilio>(Logger_); | ||||||
|  |         } | ||||||
|  |         Enabled_ = ProviderImpl_->Initialize(); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void SMSSender::Stop() { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     void SMSSender::CleanCache() { | ||||||
|  |         uint64_t Now=std::time(nullptr); | ||||||
|  |         for(auto i=begin(Cache_);i!=end(Cache_);) { | ||||||
|  |             if((Now-i->Created)>300) | ||||||
|  |                 i = Cache_.erase(i); | ||||||
|  |             else | ||||||
|  |                 ++i; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMSSender::StartValidation(const std::string &Number, const std::string &UserName) { | ||||||
|  |         std::lock_guard     G(Mutex_); | ||||||
|  |         CleanCache(); | ||||||
|  |         uint64_t Now=std::time(nullptr); | ||||||
|  |         auto Challenge = MFAServer::MakeChallenge(); | ||||||
|  |         Cache_.emplace_back(SMSValidationCacheEntry{.Number=Number, .Code=Challenge, .UserName=UserName, .Created=Now}); | ||||||
|  |         std::string Message = "Please enter the following code on your login screen: " + Challenge; | ||||||
|  |         return ProviderImpl_->Send(Number, Message); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMSSender::IsNumberValid(const std::string &Number, const std::string &UserName) { | ||||||
|  |         std::lock_guard     G(Mutex_); | ||||||
|  |  | ||||||
|  |         for(const auto &i:Cache_) { | ||||||
|  |             if(i.Number==Number && i.UserName==UserName) | ||||||
|  |                 return i.Validated; | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMSSender::CompleteValidation(const std::string &Number, const std::string &Code, const std::string &UserName) { | ||||||
|  |         std::lock_guard     G(Mutex_); | ||||||
|  |  | ||||||
|  |         for(auto &i:Cache_) { | ||||||
|  |             if(i.Code==Code && i.Number==Number && i.UserName==UserName) { | ||||||
|  |                 i.Validated=true; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMSSender::Send(const std::string &PhoneNumber, const std::string &Message) { | ||||||
|  |         if(!Enabled_) { | ||||||
|  |             Logger_.information("SMS has not been enabled. Messages cannot be sent."); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         return ProviderImpl_->Send(PhoneNumber,Message); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								src/SMSSender.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/SMSSender.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-09. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_SMSSENDER_H | ||||||
|  | #define OWSEC_SMSSENDER_H | ||||||
|  |  | ||||||
|  | #include <aws/core/Aws.h> | ||||||
|  | #include <aws/s3/S3Client.h> | ||||||
|  | #include <aws/core/auth/AWSCredentials.h> | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  | #include "SMS_provider.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |  | ||||||
|  |     struct SMSValidationCacheEntry { | ||||||
|  |         std::string Number; | ||||||
|  |         std::string Code; | ||||||
|  |         std::string UserName; | ||||||
|  |         uint64_t    Created; | ||||||
|  |         bool        Validated=false; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     class SMSSender : public SubSystemServer { | ||||||
|  |         public: | ||||||
|  |             static SMSSender *instance() { | ||||||
|  |                 if (instance_ == nullptr) { | ||||||
|  |                     instance_ = new SMSSender; | ||||||
|  |                 } | ||||||
|  |                 return instance_; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             int  Start() final; | ||||||
|  |             void Stop() final; | ||||||
|  |             bool Enabled() const { return Enabled_; } | ||||||
|  |             bool StartValidation(const std::string &Number, const std::string &UserName); | ||||||
|  |             bool CompleteValidation(const std::string &Number, const std::string &Code, const std::string &UserName); | ||||||
|  |             bool IsNumberValid(const std::string &Number, const std::string &UserName); | ||||||
|  |             [[nodiscard]] bool Send(const std::string &PhoneNumber, const std::string &Message); | ||||||
|  |         private: | ||||||
|  |             static SMSSender * instance_; | ||||||
|  |             std::string         Provider_; | ||||||
|  |             bool                Enabled_=false; | ||||||
|  |             std::vector<SMSValidationCacheEntry>    Cache_; | ||||||
|  |             std::unique_ptr<SMS_provider>           ProviderImpl_; | ||||||
|  |  | ||||||
|  |             SMSSender() noexcept: | ||||||
|  |                 SubSystemServer("SMSSender", "SMS-SVR", "smssender.aws") | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             bool SendAWS(const std::string &PhoneNumber, const std::string &Message); | ||||||
|  |             void CleanCache(); | ||||||
|  |  | ||||||
|  |     }; | ||||||
|  |     inline SMSSender * SMSSender() { return SMSSender::instance(); } | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif //OWSEC_SMSSENDER_H | ||||||
							
								
								
									
										5
									
								
								src/SMS_provider.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/SMS_provider.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "SMS_provider.h" | ||||||
							
								
								
									
										24
									
								
								src/SMS_provider.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/SMS_provider.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_SMS_PROVIDER_H | ||||||
|  | #define OWSEC_SMS_PROVIDER_H | ||||||
|  |  | ||||||
|  | #include "Poco/Logger.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     class SMS_provider { | ||||||
|  |     public: | ||||||
|  |         virtual bool Initialize() = 0 ; | ||||||
|  |         virtual bool Start() = 0 ; | ||||||
|  |         virtual bool Stop() = 0 ; | ||||||
|  |         virtual bool Running() = 0 ; | ||||||
|  |         virtual bool Send(const std::string &Number, const std::string &Message) = 0; | ||||||
|  |         virtual ~SMS_provider() {}; | ||||||
|  |     private: | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif //OWSEC_SMS_PROVIDER_H | ||||||
							
								
								
									
										61
									
								
								src/SMS_provider_aws.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/SMS_provider_aws.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <aws/sns/SNSClient.h> | ||||||
|  | #include <aws/sns/model/PublishRequest.h> | ||||||
|  | #include <aws/sns/model/PublishResult.h> | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  | #include "SMS_provider_aws.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     bool SMS_provider_aws::Initialize() { | ||||||
|  |         SecretKey_ = MicroService::instance().ConfigGetString("smssender.aws.secretkey",""); | ||||||
|  |         AccessKey_ = MicroService::instance().ConfigGetString("smssender.aws.accesskey",""); | ||||||
|  |         Region_ = MicroService::instance().ConfigGetString("smssender.aws.region",""); | ||||||
|  |  | ||||||
|  |         if(SecretKey_.empty() || AccessKey_.empty() || Region_.empty()) { | ||||||
|  |             Logger_.debug("SMSSender is disabled. Please provide key, secret, and region."); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         Running_=true; | ||||||
|  |         AwsConfig_.region = Region_; | ||||||
|  |         AwsCreds_.SetAWSAccessKeyId(AccessKey_.c_str()); | ||||||
|  |         AwsCreds_.SetAWSSecretKey(SecretKey_.c_str()); | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_aws::Start() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_aws::Stop() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_aws::Running() { | ||||||
|  |         return Running_; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_aws::Send(const std::string &PhoneNumber, const std::string &Message) { | ||||||
|  |         if(!Running_) | ||||||
|  |             return false; | ||||||
|  |  | ||||||
|  |         Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_); | ||||||
|  |         Aws::SNS::Model::PublishRequest psms_req; | ||||||
|  |         psms_req.SetMessage(Message.c_str()); | ||||||
|  |         psms_req.SetPhoneNumber(PhoneNumber.c_str()); | ||||||
|  |  | ||||||
|  |         auto psms_out = sns.Publish(psms_req); | ||||||
|  |         if (psms_out.IsSuccess()) { | ||||||
|  |             Logger_.debug(Poco::format("SMS sent to %s",PhoneNumber)); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |         std::string ErrMsg{psms_out.GetError().GetMessage()}; | ||||||
|  |         Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg)); | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/SMS_provider_aws.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/SMS_provider_aws.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_SMS_PROVIDER_AWS_H | ||||||
|  | #define OWSEC_SMS_PROVIDER_AWS_H | ||||||
|  |  | ||||||
|  | #include <aws/core/Aws.h> | ||||||
|  | #include <aws/s3/S3Client.h> | ||||||
|  | #include <aws/core/auth/AWSCredentials.h> | ||||||
|  |  | ||||||
|  | #include "SMS_provider.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     class SMS_provider_aws : public SMS_provider { | ||||||
|  |     public: | ||||||
|  |         explicit SMS_provider_aws(Poco::Logger &L) : Logger_(L) {} | ||||||
|  |         ~SMS_provider_aws() {}; | ||||||
|  |         bool Initialize() final ; | ||||||
|  |         bool Start() final ; | ||||||
|  |         bool Stop() final ; | ||||||
|  |         bool Send(const std::string &Number, const std::string &Message) final; | ||||||
|  |         bool Running() final; | ||||||
|  |     private: | ||||||
|  |         bool                                Running_=false; | ||||||
|  |         Poco::Logger                        &Logger_; | ||||||
|  |         std::string                         SecretKey_; | ||||||
|  |         std::string                         AccessKey_; | ||||||
|  |         std::string                         Region_; | ||||||
|  |         Aws::Client::ClientConfiguration    AwsConfig_; | ||||||
|  |         Aws::Auth::AWSCredentials           AwsCreds_; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OWSEC_SMS_PROVIDER_AWS_H | ||||||
							
								
								
									
										76
									
								
								src/SMS_provider_twilio.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/SMS_provider_twilio.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #include "SMS_provider_twilio.h" | ||||||
|  |  | ||||||
|  | #include "Poco/Net/HTTPBasicCredentials.h" | ||||||
|  | #include "Poco/URI.h" | ||||||
|  | #include "Poco/Net/HTMLForm.h" | ||||||
|  | #include "Poco/Net/HTTPSClientSession.h" | ||||||
|  | #include "Poco/Net/HTTPResponse.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     bool SMS_provider_twilio::Initialize() { | ||||||
|  |         Sid_ = MicroService::instance().ConfigGetString("smssender.twilio.sid",""); | ||||||
|  |         Token_ = MicroService::instance().ConfigGetString("smssender.twilio.token",""); | ||||||
|  |         PhoneNumber_ = MicroService::instance().ConfigGetString("smssender.twilio.phonenumber",""); | ||||||
|  |  | ||||||
|  |         if(Sid_.empty() || Token_.empty() || PhoneNumber_.empty()) { | ||||||
|  |             Logger_.debug("SMSSender is disabled. Please provide SID, TOKEN, and PHONE NUMBER."); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |         Running_=true; | ||||||
|  |         Uri_ = "https://api.twilio.com/2010-04-01/Accounts/" + Sid_ + "/Messages.json"; | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_twilio::Start() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_twilio::Stop() { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_twilio::Running() { | ||||||
|  |         return Running_; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bool SMS_provider_twilio::Send(const std::string &PhoneNumber, const std::string &Message) { | ||||||
|  |         if(!Running_) | ||||||
|  |             return false; | ||||||
|  |  | ||||||
|  |         Poco::Net::HTTPBasicCredentials Creds(Sid_,Token_); | ||||||
|  |         Poco::URI   uri(Uri_); | ||||||
|  |         Poco::Net::HTMLForm  form; | ||||||
|  |  | ||||||
|  |         Poco::Net::HTTPSClientSession session(uri.getHost(), uri.getPort()); | ||||||
|  |         Poco::Net::HTTPRequest req(Poco::Net::HTTPRequest::HTTP_POST, uri.getPath(), Poco::Net::HTTPMessage::HTTP_1_1); | ||||||
|  |         Creds.authenticate(req); | ||||||
|  |  | ||||||
|  |         Poco::JSON::Object  RObj; | ||||||
|  |  | ||||||
|  |         form.add("To",PhoneNumber); | ||||||
|  |         form.add("From",PhoneNumber_); | ||||||
|  |         form.add("Body","This is from twillio"); | ||||||
|  |  | ||||||
|  |         form.prepareSubmit(req); | ||||||
|  |         std::ostream& ostr = session.sendRequest(req); | ||||||
|  |         form.write(ostr); | ||||||
|  |  | ||||||
|  |         Poco::Net::HTTPResponse res; | ||||||
|  |         std::istream& rs = session.receiveResponse(res); | ||||||
|  |  | ||||||
|  |         if(res.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { | ||||||
|  |             Logger_.information(Poco::format("Message sent to %s", PhoneNumber)); | ||||||
|  |             return true; | ||||||
|  |         } else { | ||||||
|  |             std::ostringstream os; | ||||||
|  |             Poco::StreamCopier::copyStream(rs,os); | ||||||
|  |             Logger_.information(Poco::format("Message was not to %s: Error:%s", PhoneNumber, os.str())); | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										30
									
								
								src/SMS_provider_twilio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/SMS_provider_twilio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-15. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWSEC_SMS_PROVIDER_TWILIO_H | ||||||
|  | #define OWSEC_SMS_PROVIDER_TWILIO_H | ||||||
|  |  | ||||||
|  | #include "SMS_provider.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     class SMS_provider_twilio : public SMS_provider { | ||||||
|  |     public: | ||||||
|  |         explicit SMS_provider_twilio(Poco::Logger &L) : Logger_(L) {} | ||||||
|  |         ~SMS_provider_twilio() {}; | ||||||
|  |         bool Initialize() final ; | ||||||
|  |         bool Start() final ; | ||||||
|  |         bool Stop() final ; | ||||||
|  |         bool Send(const std::string &Number, const std::string &Message) final; | ||||||
|  |         bool Running() final; | ||||||
|  |     private: | ||||||
|  |         bool                                Running_=false; | ||||||
|  |         Poco::Logger                        &Logger_; | ||||||
|  |         std::string                         Sid_; | ||||||
|  |         std::string                         Token_; | ||||||
|  |         std::string                         PhoneNumber_; | ||||||
|  |         std::string                         Uri_; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OWSEC_SMS_PROVIDER_TWILIO_H | ||||||
| @@ -17,21 +17,21 @@ | |||||||
| #include "Poco/Net/AcceptCertificateHandler.h" | #include "Poco/Net/AcceptCertificateHandler.h" | ||||||
|  |  | ||||||
| #include "SMTPMailerService.h" | #include "SMTPMailerService.h" | ||||||
| #include "Utils.h" | #include "framework/MicroService.h" | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
|     class SMTPMailerService * SMTPMailerService::instance_ = nullptr; |     class SMTPMailerService * SMTPMailerService::instance_ = nullptr; | ||||||
|  |  | ||||||
|     void SMTPMailerService::LoadMyConfig() { |     void SMTPMailerService::LoadMyConfig() { | ||||||
|         MailHost_ = Daemon()->ConfigGetString("mailer.hostname"); |         MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); | ||||||
|         SenderLoginUserName_ = Daemon()->ConfigGetString("mailer.username"); |         SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); | ||||||
|         SenderLoginPassword_ = Daemon()->ConfigGetString("mailer.password"); |         SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password"); | ||||||
|         Sender_ = Daemon()->ConfigGetString("mailer.sender"); |         Sender_ = MicroService::instance().ConfigGetString("mailer.sender"); | ||||||
|         LoginMethod_ = Daemon()->ConfigGetString("mailer.loginmethod"); |         LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod"); | ||||||
|         MailHostPort_ = (int)Daemon()->ConfigGetInt("mailer.port"); |         MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port"); | ||||||
|         TemplateDir_ = Daemon()->ConfigPath("mailer.templates", Daemon()->DataDir()); |         TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); | ||||||
|  |         Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     int SMTPMailerService::Start() { |     int SMTPMailerService::Start() { | ||||||
| @@ -47,7 +47,7 @@ namespace OpenWifi { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SMTPMailerService::reinitialize(Poco::Util::Application &self) { |     void SMTPMailerService::reinitialize(Poco::Util::Application &self) { | ||||||
|         Daemon()->LoadConfigurationFile(); |         MicroService::instance().LoadConfigurationFile(); | ||||||
|         Logger_.information("Reinitializing."); |         Logger_.information("Reinitializing."); | ||||||
|         LoadMyConfig(); |         LoadMyConfig(); | ||||||
|     } |     } | ||||||
| @@ -55,16 +55,24 @@ namespace OpenWifi { | |||||||
|     bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) { |     bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) { | ||||||
|         std::lock_guard G(Mutex_); |         std::lock_guard G(Mutex_); | ||||||
|  |  | ||||||
|  |         /* | ||||||
|         uint64_t Now = std::time(nullptr); |         uint64_t Now = std::time(nullptr); | ||||||
|         auto CE = Cache_.find(Poco::toLower(Recipient)); |         std::string RecipientLower = Poco::toLower(Recipient); | ||||||
|  |         auto CE = Cache_.find(RecipientLower); | ||||||
|         if(CE!=Cache_.end()) { |         if(CE!=Cache_.end()) { | ||||||
|             // only allow messages to the same user within 2 minutes |             // only allow messages to the same user within 2 minutes | ||||||
|             if((CE->second.LastRequest-Now)<30) |             if(!((CE->second.LastRequest-Now)<30 && CE->second.HowManyRequests<10)) | ||||||
|                 return false; |  | ||||||
|             if((CE->second.HowManyRequests>30)) |  | ||||||
|                 return false; |                 return false; | ||||||
|  |             if(CE->second.LastRequest-Now>30) { | ||||||
|  |                 CE->second.LastRequest = Now; | ||||||
|  |                 CE->second.HowManyRequests=0; | ||||||
|  |             } else { | ||||||
|  |                 CE->second.HowManyRequests++; | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             Cache_[RecipientLower] = MessageCacheEntry{.LastRequest=Now, .HowManyRequests=0}; | ||||||
|         } |         } | ||||||
|  | */ | ||||||
|         Messages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr), |         Messages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr), | ||||||
|                                             .LastTry=0, |                                             .LastTry=0, | ||||||
|                                             .Sent=0, |                                             .Sent=0, | ||||||
| @@ -96,7 +104,7 @@ namespace OpenWifi { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 //  Clean the list |                 //  Clean the list | ||||||
|                 std::remove_if(Messages_.begin(),Messages_.end(),[Now](MessageEvent &E){ return (E.Sent!=0 || ((Now-E.LastTry)>(24*60*60)));}); |                 std::remove_if(Messages_.begin(),Messages_.end(),[Now](MessageEvent &E){ return (E.Sent!=0 || ((Now-E.LastTry)>(15*60)));}); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -110,11 +118,19 @@ namespace OpenWifi { | |||||||
|     bool SMTPMailerService::SendIt(const MessageEvent &Msg) { |     bool SMTPMailerService::SendIt(const MessageEvent &Msg) { | ||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             Poco::SharedPtr<Poco::Net::InvalidCertificateHandler> ptrHandler = new Poco::Net::AcceptCertificateHandler(false); |  | ||||||
|  |  | ||||||
|             Poco::Net::MailMessage  Message; |             Poco::Net::MailMessage  Message; | ||||||
|             std::string             Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; |             std::string             Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; | ||||||
|             Message.setSender(Sender_); |  | ||||||
|  |             auto H1 = Msg.Attrs.find(SENDER); | ||||||
|  |             std::string TheSender; | ||||||
|  |             if(H1!=Msg.Attrs.end()) { | ||||||
|  |                 TheSender = H1->second ; | ||||||
|  |             } else { | ||||||
|  |                 TheSender = Sender_ ; | ||||||
|  |             } | ||||||
|  |             Message.setSender( TheSender ); | ||||||
|  |             Logger_.information(Poco::format("Sending message to:%s from %s",Recipient,TheSender)); | ||||||
|  |  | ||||||
|             Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); |             Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); | ||||||
|             Message.setSubject(Msg.Attrs.find(SUBJECT)->second); |             Message.setSubject(Msg.Attrs.find(SUBJECT)->second); | ||||||
|  |  | ||||||
| @@ -145,7 +161,7 @@ namespace OpenWifi { | |||||||
|                                                             Poco::Net::Context::VERIFY_RELAXED, 9, true, |                                                             Poco::Net::Context::VERIFY_RELAXED, 9, true, | ||||||
|                                                             "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH")); |                                                             "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH")); | ||||||
|             Poco::Net::SSLManager::instance().initializeClient(nullptr, |             Poco::Net::SSLManager::instance().initializeClient(nullptr, | ||||||
|                                                                ptrHandler, |                                                                &ptrHandler_, | ||||||
|                                                                ptrContext); |                                                                ptrContext); | ||||||
|             session.login(); |             session.login(); | ||||||
|             session.startTLS(ptrContext); |             session.startTLS(ptrContext); | ||||||
|   | |||||||
| @@ -5,9 +5,11 @@ | |||||||
| #ifndef UCENTRALSEC_SMTPMAILERSERVICE_H | #ifndef UCENTRALSEC_SMTPMAILERSERVICE_H | ||||||
| #define UCENTRALSEC_SMTPMAILERSERVICE_H | #define UCENTRALSEC_SMTPMAILERSERVICE_H | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
| #include "Poco/File.h" | #include "Poco/File.h" | ||||||
|  | #include "Poco/Net/InvalidCertificateHandler.h" | ||||||
|  | #include "Poco/Net/AcceptCertificateHandler.h" | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
| @@ -23,7 +25,9 @@ namespace OpenWifi { | |||||||
|         TEMPLATE_TXT, |         TEMPLATE_TXT, | ||||||
|         TEMPLATE_HTML, |         TEMPLATE_HTML, | ||||||
|         LOGO, |         LOGO, | ||||||
|         TEXT |         TEXT, | ||||||
|  |         CHALLENGE_CODE, | ||||||
|  |         SENDER | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|     static const std::map<MESSAGE_ATTRIBUTES,const std::string> |     static const std::map<MESSAGE_ATTRIBUTES,const std::string> | ||||||
| @@ -38,7 +42,9 @@ namespace OpenWifi { | |||||||
|                                  {  TEMPLATE_TXT, "TEMPLATE_TXT"}, |                                  {  TEMPLATE_TXT, "TEMPLATE_TXT"}, | ||||||
|                                  {  TEMPLATE_HTML, "TEMPLATE_HTML"}, |                                  {  TEMPLATE_HTML, "TEMPLATE_HTML"}, | ||||||
|                                  {  LOGO, "LOGO"}, |                                  {  LOGO, "LOGO"}, | ||||||
|                                  {  TEXT, "TEXT"} |                                  {  TEXT, "TEXT"}, | ||||||
|  |                                  {  CHALLENGE_CODE, "CHALLENGE_CODE"}, | ||||||
|  |                                  {  SENDER, "SENDER"} | ||||||
|                                  }; |                                  }; | ||||||
|  |  | ||||||
|     inline const std::string & MessageAttributeToVar(MESSAGE_ATTRIBUTES Attr) { |     inline const std::string & MessageAttributeToVar(MESSAGE_ATTRIBUTES Attr) { | ||||||
| @@ -80,6 +86,7 @@ namespace OpenWifi { | |||||||
|             bool SendIt(const MessageEvent &Msg); |             bool SendIt(const MessageEvent &Msg); | ||||||
|             void LoadMyConfig(); |             void LoadMyConfig(); | ||||||
|             void reinitialize(Poco::Util::Application &self) override; |             void reinitialize(Poco::Util::Application &self) override; | ||||||
|  |             bool Enabled() const { return Enabled_; } | ||||||
|         private: |         private: | ||||||
|             static SMTPMailerService * instance_; |             static SMTPMailerService * instance_; | ||||||
|             std::string             MailHost_; |             std::string             MailHost_; | ||||||
| @@ -94,9 +101,12 @@ namespace OpenWifi { | |||||||
|             std::map<std::string,MessageCacheEntry> Cache_; |             std::map<std::string,MessageCacheEntry> Cache_; | ||||||
|             Poco::Thread            SenderThr_; |             Poco::Thread            SenderThr_; | ||||||
|             std::atomic_bool        Running_=false; |             std::atomic_bool        Running_=false; | ||||||
|  |             bool                    Enabled_=false; | ||||||
|  |             Poco::Net::AcceptCertificateHandler  ptrHandler_; | ||||||
|  |  | ||||||
|             SMTPMailerService() noexcept: |             SMTPMailerService() noexcept: | ||||||
|                 SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer") |                 SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer"), | ||||||
|  |                 ptrHandler_(false) | ||||||
|             { |             { | ||||||
|                 std::string E{"SHA512"}; |                 std::string E{"SHA512"}; | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -7,57 +7,22 @@ | |||||||
| // | // | ||||||
|  |  | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "Daemon.h" |  | ||||||
| #include "Poco/Util/Application.h" |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
|     class Storage *Storage::instance_ = nullptr; |     class Storage *Storage::instance_ = nullptr; | ||||||
|  |  | ||||||
| 	std::string Storage::ConvertParams(const std::string & S) const { |  | ||||||
| 		std::string R; |  | ||||||
|  |  | ||||||
| 		R.reserve(S.size()*2+1); |  | ||||||
|  |  | ||||||
| 		if(dbType_==pgsql) { |  | ||||||
| 			auto Idx=1; |  | ||||||
| 			for(auto const & i:S) |  | ||||||
| 			{ |  | ||||||
| 				if(i=='?') { |  | ||||||
| 					R += '$'; |  | ||||||
| 					R.append(std::to_string(Idx++)); |  | ||||||
| 				} else { |  | ||||||
| 					R += i; |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			R = S; |  | ||||||
| 		} |  | ||||||
| 		return R; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     int Storage::Start() { |     int Storage::Start() { | ||||||
| 		std::lock_guard		Guard(Mutex_); | 		std::lock_guard		Guard(Mutex_); | ||||||
|  |  | ||||||
| 		Logger_.setLevel(Poco::Message::PRIO_NOTICE); | 		StorageClass::Start(); | ||||||
|         Logger_.notice("Starting."); |  | ||||||
|         std::string DBType = Daemon()->ConfigGetString("storage.type"); |  | ||||||
|         if (DBType == "sqlite") { |  | ||||||
|             Setup_SQLite(); |  | ||||||
|         } else if (DBType == "postgresql") { |  | ||||||
|             Setup_PostgreSQL(); |  | ||||||
|         } else if (DBType == "mysql") { |  | ||||||
|             Setup_MySQL(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
| 		Create_Tables(); | 		Create_Tables(); | ||||||
|  |  | ||||||
| 		return 0; | 		return 0; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void Storage::Stop() { |     void Storage::Stop() { | ||||||
|         Logger_.notice("Stopping."); |         Logger_.notice("Stopping."); | ||||||
|  |         StorageClass::Stop(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| // namespace | // namespace | ||||||
| @@ -9,20 +9,9 @@ | |||||||
| #ifndef UCENTRAL_USTORAGESERVICE_H | #ifndef UCENTRAL_USTORAGESERVICE_H | ||||||
| #define UCENTRAL_USTORAGESERVICE_H | #define UCENTRAL_USTORAGESERVICE_H | ||||||
|  |  | ||||||
| #include "Poco/Data/Session.h" | #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||||
| #include "Poco/Data/SessionPool.h" | #include "framework/StorageClass.h" | ||||||
| #include "Poco/Data/SQLite/Connector.h" |  | ||||||
| #include "Poco/File.h" |  | ||||||
| #include "Poco/TemporaryFile.h" |  | ||||||
|  |  | ||||||
| #ifndef SMALL_BUILD |  | ||||||
| #include "Poco/Data/PostgreSQL/Connector.h" |  | ||||||
| #include "Poco/Data/MySQL/Connector.h" |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| #include "AuthService.h" | #include "AuthService.h" | ||||||
| #include "RESTAPI_SecurityObjects.h" |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|  |  | ||||||
| @@ -66,19 +55,9 @@ namespace OpenWifi { | |||||||
|  |  | ||||||
|     }; |     }; | ||||||
|  |  | ||||||
|  |     class Storage : public StorageClass { | ||||||
|  |  | ||||||
|  |  | ||||||
|     class Storage : public SubSystemServer { |  | ||||||
|  |  | ||||||
|     public: |     public: | ||||||
|  |  | ||||||
|         enum StorageType { |  | ||||||
|             sqlite, |  | ||||||
|             pgsql, |  | ||||||
|             mysql |  | ||||||
|         }; |  | ||||||
|  |  | ||||||
|         enum AUTH_ERROR { |         enum AUTH_ERROR { | ||||||
|             SUCCESS, |             SUCCESS, | ||||||
|             PASSWORD_CHANGE_REQUIRED, |             PASSWORD_CHANGE_REQUIRED, | ||||||
| @@ -169,46 +148,16 @@ namespace OpenWifi { | |||||||
|         bool CompleteAction(std::string &ActionId); |         bool CompleteAction(std::string &ActionId); | ||||||
|         bool CancelAction(std::string &ActionId); |         bool CancelAction(std::string &ActionId); | ||||||
|  |  | ||||||
|  |  | ||||||
| 	  private: | 	  private: | ||||||
| 		static Storage      							*instance_; | 		static Storage      							*instance_; | ||||||
| 		std::unique_ptr<Poco::Data::SessionPool>        Pool_= nullptr; |  | ||||||
| 		StorageType 									dbType_ = sqlite; |  | ||||||
| 		std::unique_ptr<Poco::Data::SQLite::Connector>  SQLiteConn_= nullptr; |  | ||||||
| #ifndef SMALL_BUILD |  | ||||||
| 		std::unique_ptr<Poco::Data::PostgreSQL::Connector>  PostgresConn_= nullptr; |  | ||||||
| 		std::unique_ptr<Poco::Data::MySQL::Connector>       MySQLConn_= nullptr; |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
|         int Create_Tables(); |         int Create_Tables(); | ||||||
|         int Create_UserTable(); |         int Create_UserTable(); | ||||||
|         int Create_AvatarTable(); |         int Create_AvatarTable(); | ||||||
|         int Create_TokensTable(); |         int Create_TokensTable(); | ||||||
|  |  | ||||||
| 		[[nodiscard]] std::string ConvertParams(const std::string &S) const; |  | ||||||
| 		[[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { |  | ||||||
| 		    if(dbType_==sqlite) { |  | ||||||
| 		        return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; |  | ||||||
| 		    } else if(dbType_==pgsql) { |  | ||||||
| 		        return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; |  | ||||||
| 		    } else if(dbType_==mysql) { |  | ||||||
| 		        return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; |  | ||||||
| 		    } |  | ||||||
| 		    return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Storage() noexcept: |  | ||||||
|             SubSystemServer("Storage", "STORAGE-SVR", "storage") |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|         int 	Setup_SQLite(); |  | ||||||
|         int 	Setup_MySQL(); |  | ||||||
|         int 	Setup_PostgreSQL(); |  | ||||||
|  |  | ||||||
|    }; |    }; | ||||||
|  |  | ||||||
|     inline Storage * Storage() { return Storage::instance(); }; |     inline Storage * StorageService() { return Storage::instance(); }; | ||||||
|  |  | ||||||
| }  // namespace | }  // namespace | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,306 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #include "SubSystemServer.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Net/X509Certificate.h" |  | ||||||
| #include "Poco/DateTimeFormatter.h" |  | ||||||
| #include "Poco/DateTimeFormat.h" |  | ||||||
| #include "Poco/Net/PrivateKeyPassphraseHandler.h" |  | ||||||
| #include "Poco/Net/SSLManager.h" |  | ||||||
|  |  | ||||||
| #include "openssl/ssl.h" |  | ||||||
|  |  | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| SubSystemServer::SubSystemServer(std::string Name, const std::string &LoggingPrefix, |  | ||||||
| 								 std::string SubSystemConfigPrefix) |  | ||||||
| 	: Name_(std::move(Name)), Logger_(Poco::Logger::get(LoggingPrefix)), |  | ||||||
| 	  SubSystemConfigPrefix_(std::move(SubSystemConfigPrefix)) { |  | ||||||
| 	Logger_.setLevel(Poco::Message::PRIO_NOTICE); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SubSystemServer::initialize(Poco::Util::Application &self) { |  | ||||||
| 	Logger_.notice("Initializing..."); |  | ||||||
| 	auto i = 0; |  | ||||||
| 	bool good = true; |  | ||||||
|  |  | ||||||
| 	ConfigServersList_.clear(); |  | ||||||
| 	while (good) { |  | ||||||
| 		std::string root{SubSystemConfigPrefix_ + ".host." + std::to_string(i) + "."}; |  | ||||||
|  |  | ||||||
| 		std::string address{root + "address"}; |  | ||||||
| 		if (Daemon()->ConfigGetString(address, "").empty()) { |  | ||||||
| 			good = false; |  | ||||||
| 		} else { |  | ||||||
| 			std::string port{root + "port"}; |  | ||||||
| 			std::string key{root + "key"}; |  | ||||||
| 			std::string key_password{root + "key.password"}; |  | ||||||
| 			std::string cert{root + "cert"}; |  | ||||||
| 			std::string name{root + "name"}; |  | ||||||
| 			std::string backlog{root + "backlog"}; |  | ||||||
| 			std::string rootca{root + "rootca"}; |  | ||||||
| 			std::string issuer{root + "issuer"}; |  | ||||||
| 			std::string clientcas(root + "clientcas"); |  | ||||||
| 			std::string cas{root + "cas"}; |  | ||||||
|  |  | ||||||
| 			std::string level{root + "security"}; |  | ||||||
| 			Poco::Net::Context::VerificationMode M = Poco::Net::Context::VERIFY_RELAXED; |  | ||||||
|  |  | ||||||
| 			auto L = Daemon()->ConfigGetString(level, ""); |  | ||||||
|  |  | ||||||
| 			if (L == "strict") { |  | ||||||
| 				M = Poco::Net::Context::VERIFY_STRICT; |  | ||||||
| 			} else if (L == "none") { |  | ||||||
| 				M = Poco::Net::Context::VERIFY_NONE; |  | ||||||
| 			} else if (L == "relaxed") { |  | ||||||
| 				M = Poco::Net::Context::VERIFY_RELAXED; |  | ||||||
| 			} else if (L == "once") |  | ||||||
| 				M = Poco::Net::Context::VERIFY_ONCE; |  | ||||||
|  |  | ||||||
| 			PropertiesFileServerEntry entry(Daemon()->ConfigGetString(address, ""), |  | ||||||
| 											Daemon()->ConfigGetInt(port, 0), |  | ||||||
| 											Daemon()->ConfigPath(key, ""), |  | ||||||
| 											Daemon()->ConfigPath(cert, ""), |  | ||||||
| 											Daemon()->ConfigPath(rootca, ""), |  | ||||||
| 											Daemon()->ConfigPath(issuer, ""), |  | ||||||
| 											Daemon()->ConfigPath(clientcas, ""), |  | ||||||
| 											Daemon()->ConfigPath(cas, ""), |  | ||||||
| 											Daemon()->ConfigGetString(key_password, ""), |  | ||||||
| 											Daemon()->ConfigGetString(name, ""), M, |  | ||||||
| 											(int)Daemon()->ConfigGetInt(backlog, 64)); |  | ||||||
| 			ConfigServersList_.push_back(entry); |  | ||||||
| 			i++; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SubSystemServer::uninitialize() { |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SubSystemServer::reinitialize(Poco::Util::Application &self) { |  | ||||||
| 	Logger_.information("Reloading of this subsystem is not supported."); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void SubSystemServer::defineOptions(Poco::Util::OptionSet &options) {} |  | ||||||
|  |  | ||||||
| class MyPrivateKeyPassphraseHandler : public Poco::Net::PrivateKeyPassphraseHandler { |  | ||||||
|   public: |  | ||||||
| 	explicit MyPrivateKeyPassphraseHandler(const std::string &Password, Poco::Logger & Logger): |  | ||||||
| 		PrivateKeyPassphraseHandler(true), |  | ||||||
| 		Logger_(Logger), |  | ||||||
| 	 	Password_(Password) {} |  | ||||||
| 		void onPrivateKeyRequested(const void * pSender,std::string & privateKey) { |  | ||||||
| 			Logger_.information("Returning key passphrase."); |  | ||||||
| 			privateKey = Password_; |  | ||||||
| 		}; |  | ||||||
|   private: |  | ||||||
| 	std::string Password_; |  | ||||||
| 	Poco::Logger & Logger_; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| Poco::Net::SecureServerSocket PropertiesFileServerEntry::CreateSecureSocket(Poco::Logger &L) const { |  | ||||||
| 	Poco::Net::Context::Params P; |  | ||||||
|  |  | ||||||
| 	P.verificationMode = level_; |  | ||||||
| 	P.verificationDepth = 9; |  | ||||||
| 	P.loadDefaultCAs = root_ca_.empty(); |  | ||||||
| 	P.cipherList = "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"; |  | ||||||
| 	P.dhUse2048Bits = true; |  | ||||||
| 	P.caLocation = cas_; |  | ||||||
|  |  | ||||||
| 	auto Context = Poco::AutoPtr<Poco::Net::Context>(new Poco::Net::Context(Poco::Net::Context::TLS_SERVER_USE, P)); |  | ||||||
|  |  | ||||||
| 	if(!key_file_password_.empty()) { |  | ||||||
| 		auto PassphraseHandler = Poco::SharedPtr<MyPrivateKeyPassphraseHandler>( new MyPrivateKeyPassphraseHandler(key_file_password_,L)); |  | ||||||
| 		Poco::Net::SSLManager::instance().initializeServer(PassphraseHandler, nullptr,Context); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (!cert_file_.empty() && !key_file_.empty()) { |  | ||||||
| 		Poco::Crypto::X509Certificate Cert(cert_file_); |  | ||||||
| 		Poco::Crypto::X509Certificate Root(root_ca_); |  | ||||||
|  |  | ||||||
| 		Context->useCertificate(Cert); |  | ||||||
| 		Context->addChainCertificate(Root); |  | ||||||
|  |  | ||||||
| 		Context->addCertificateAuthority(Root); |  | ||||||
|  |  | ||||||
| 		if (level_ == Poco::Net::Context::VERIFY_STRICT) { |  | ||||||
| 			if (issuer_cert_file_.empty()) { |  | ||||||
| 				L.fatal("In strict mode, you must supply ans issuer certificate"); |  | ||||||
| 			} |  | ||||||
| 			if (client_cas_.empty()) { |  | ||||||
| 				L.fatal("In strict mode, client cas must be supplied"); |  | ||||||
| 			} |  | ||||||
| 			Poco::Crypto::X509Certificate Issuing(issuer_cert_file_); |  | ||||||
| 			Context->addChainCertificate(Issuing); |  | ||||||
| 			Context->addCertificateAuthority(Issuing); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		Poco::Crypto::RSAKey Key("", key_file_, key_file_password_); |  | ||||||
| 		Context->usePrivateKey(Key); |  | ||||||
|  |  | ||||||
| 		SSL_CTX *SSLCtx = Context->sslContext(); |  | ||||||
| 		if (!SSL_CTX_check_private_key(SSLCtx)) { |  | ||||||
| 			L.fatal(Poco::format("Wrong Certificate(%s) for Key(%s)", cert_file_, key_file_)); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		SSL_CTX_set_verify(SSLCtx, SSL_VERIFY_PEER, nullptr); |  | ||||||
|  |  | ||||||
| 		if (level_ == Poco::Net::Context::VERIFY_STRICT) { |  | ||||||
| 			SSL_CTX_set_client_CA_list(SSLCtx, SSL_load_client_CA_file(client_cas_.c_str())); |  | ||||||
| 		} |  | ||||||
| 		SSL_CTX_enable_ct(SSLCtx, SSL_CT_VALIDATION_STRICT); |  | ||||||
| 		SSL_CTX_dane_enable(SSLCtx); |  | ||||||
|  |  | ||||||
| 		Context->enableSessionCache(); |  | ||||||
| 		Context->setSessionCacheSize(0); |  | ||||||
| 		Context->setSessionTimeout(10); |  | ||||||
| 		Context->enableExtendedCertificateVerification(true); |  | ||||||
| 		Context->disableStatelessSessionResumption(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (address_ == "*") { |  | ||||||
| 		Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard( |  | ||||||
| 			Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6 |  | ||||||
| 											  : Poco::Net::AddressFamily::IPv4)); |  | ||||||
| 		Poco::Net::SocketAddress SockAddr(Addr, port_); |  | ||||||
|  |  | ||||||
| 		return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); |  | ||||||
| 	} else { |  | ||||||
| 		Poco::Net::IPAddress Addr(address_); |  | ||||||
| 		Poco::Net::SocketAddress SockAddr(Addr, port_); |  | ||||||
|  |  | ||||||
| 		return Poco::Net::SecureServerSocket(SockAddr, backlog_, Context); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PropertiesFileServerEntry::LogCertInfo(Poco::Logger &L, |  | ||||||
| 											const Poco::Crypto::X509Certificate &C) { |  | ||||||
|  |  | ||||||
| 	L.information("============================================================================================="); |  | ||||||
| 	L.information(Poco::format(">          Issuer: %s", C.issuerName())); |  | ||||||
| 	L.information("---------------------------------------------------------------------------------------------"); |  | ||||||
| 	L.information(Poco::format(">     Common Name: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); |  | ||||||
| 	L.information(Poco::format(">         Country: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_COUNTRY))); |  | ||||||
| 	L.information(Poco::format(">        Locality: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); |  | ||||||
| 	L.information(Poco::format(">      State/Prov: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); |  | ||||||
| 	L.information(Poco::format(">        Org name: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">        Org unit: %s", |  | ||||||
| 					 C.issuerName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">           Email: %s", |  | ||||||
| 					 C.issuerName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); |  | ||||||
| 	L.information(Poco::format(">         Serial#: %s", |  | ||||||
| 							   C.issuerName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); |  | ||||||
| 	L.information("---------------------------------------------------------------------------------------------"); |  | ||||||
| 	L.information(Poco::format(">         Subject: %s", C.subjectName())); |  | ||||||
| 	L.information("---------------------------------------------------------------------------------------------"); |  | ||||||
| 	L.information(Poco::format(">     Common Name: %s", |  | ||||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_COMMON_NAME))); |  | ||||||
| 	L.information(Poco::format(">         Country: %s", |  | ||||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_COUNTRY))); |  | ||||||
| 	L.information(Poco::format(">        Locality: %s", |  | ||||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_LOCALITY_NAME))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">      State/Prov: %s", |  | ||||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_STATE_OR_PROVINCE))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">        Org name: %s", |  | ||||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_NAME))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">        Org unit: %s", |  | ||||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_ORGANIZATION_UNIT_NAME))); |  | ||||||
| 	L.information( |  | ||||||
| 		Poco::format(">           Email: %s", |  | ||||||
| 					 C.subjectName(Poco::Crypto::X509Certificate::NID_PKCS9_EMAIL_ADDRESS))); |  | ||||||
| 	L.information(Poco::format(">         Serial#: %s", |  | ||||||
| 							   C.subjectName(Poco::Crypto::X509Certificate::NID_SERIAL_NUMBER))); |  | ||||||
| 	L.information("---------------------------------------------------------------------------------------------"); |  | ||||||
| 	L.information(Poco::format(">  Signature Algo: %s", C.signatureAlgorithm())); |  | ||||||
| 	auto From = Poco::DateTimeFormatter::format(C.validFrom(), Poco::DateTimeFormat::HTTP_FORMAT); |  | ||||||
| 	L.information(Poco::format(">      Valid from: %s", From)); |  | ||||||
| 	auto Expires = |  | ||||||
| 		Poco::DateTimeFormatter::format(C.expiresOn(), Poco::DateTimeFormat::HTTP_FORMAT); |  | ||||||
| 	L.information(Poco::format(">      Expires on: %s", Expires)); |  | ||||||
| 	L.information(Poco::format(">         Version: %d", (int)C.version())); |  | ||||||
| 	L.information(Poco::format(">        Serial #: %s", C.serialNumber())); |  | ||||||
| 	L.information("============================================================================================="); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PropertiesFileServerEntry::LogCert(Poco::Logger &L) const { |  | ||||||
| 	try { |  | ||||||
| 		Poco::Crypto::X509Certificate C(cert_file_); |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 		L.information(Poco::format("Certificate Filename: %s", cert_file_)); |  | ||||||
| 		LogCertInfo(L, C); |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
|  |  | ||||||
| 		if (!issuer_cert_file_.empty()) { |  | ||||||
| 			Poco::Crypto::X509Certificate C1(issuer_cert_file_); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			L.information(Poco::format("Issues Certificate Filename: %s", issuer_cert_file_)); |  | ||||||
| 			LogCertInfo(L, C1); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!client_cas_.empty()) { |  | ||||||
| 			std::vector<Poco::Crypto::X509Certificate> Certs = |  | ||||||
| 				Poco::Net::X509Certificate::readPEM(client_cas_); |  | ||||||
|  |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			L.information(Poco::format("Client CAs Filename: %s", client_cas_)); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			auto i = 1; |  | ||||||
| 			for (const auto &C3 : Certs) { |  | ||||||
| 				L.information(Poco::format(" Index: %d", i)); |  | ||||||
| 				L.information("============================================================================================="); |  | ||||||
| 				LogCertInfo(L, C3); |  | ||||||
| 				i++; |  | ||||||
| 			} |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 	} catch (const Poco::Exception &E) { |  | ||||||
| 		L.log(E); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| void PropertiesFileServerEntry::LogCas(Poco::Logger &L) const { |  | ||||||
| 	try { |  | ||||||
| 		std::vector<Poco::Crypto::X509Certificate> Certs = |  | ||||||
| 			Poco::Net::X509Certificate::readPEM(root_ca_); |  | ||||||
|  |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 		L.information(Poco::format("CA Filename: %s", root_ca_)); |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 		auto i = 1; |  | ||||||
| 		for (const auto &C : Certs) { |  | ||||||
| 			L.information(Poco::format(" Index: %d", i)); |  | ||||||
| 			L.information("============================================================================================="); |  | ||||||
| 			LogCertInfo(L, C); |  | ||||||
| 			i++; |  | ||||||
| 		} |  | ||||||
| 		L.information("============================================================================================="); |  | ||||||
| 	} catch (const Poco::Exception &E) { |  | ||||||
| 		L.log(E); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| } |  | ||||||
| @@ -1,95 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRAL_SUBSYSTEMSERVER_H |  | ||||||
| #define UCENTRAL_SUBSYSTEMSERVER_H |  | ||||||
|  |  | ||||||
| #include <mutex> |  | ||||||
|  |  | ||||||
| #include "Poco/Util/Application.h" |  | ||||||
| #include "Poco/Util/Option.h" |  | ||||||
| #include "Poco/Util/OptionSet.h" |  | ||||||
| #include "Poco/Util/HelpFormatter.h" |  | ||||||
| #include "Poco/Logger.h" |  | ||||||
| #include "Poco/Net/SecureServerSocket.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Net/X509Certificate.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi { |  | ||||||
| class PropertiesFileServerEntry { |  | ||||||
|   public: |  | ||||||
| 	PropertiesFileServerEntry(std::string Address, uint32_t port, std::string Key_file, |  | ||||||
| 							  std::string Cert_file, std::string RootCa, std::string Issuer, |  | ||||||
| 							  std::string ClientCas, std::string Cas, |  | ||||||
| 							  std::string Key_file_password = "", std::string Name = "", |  | ||||||
| 							  Poco::Net::Context::VerificationMode M = |  | ||||||
| 								  Poco::Net::Context::VerificationMode::VERIFY_RELAXED, |  | ||||||
| 							  int backlog = 64) |  | ||||||
| 		: address_(std::move(Address)), port_(port), key_file_(std::move(Key_file)), |  | ||||||
| 		  cert_file_(std::move(Cert_file)), root_ca_(std::move(RootCa)), |  | ||||||
| 		  issuer_cert_file_(std::move(Issuer)), client_cas_(std::move(ClientCas)), |  | ||||||
| 		  cas_(std::move(Cas)), key_file_password_(std::move(Key_file_password)), |  | ||||||
| 		  name_(std::move(Name)), level_(M), backlog_(backlog){}; |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] const std::string &Address() const { return address_; }; |  | ||||||
| 	[[nodiscard]] uint32_t Port() const { return port_; }; |  | ||||||
| 	[[nodiscard]] const std::string &KeyFile() const { return key_file_; }; |  | ||||||
| 	[[nodiscard]] const std::string &CertFile() const { return cert_file_; }; |  | ||||||
| 	[[nodiscard]] const std::string &RootCA() const { return root_ca_; }; |  | ||||||
| 	[[nodiscard]] const std::string &KeyFilePassword() const { return key_file_password_; }; |  | ||||||
| 	[[nodiscard]] const std::string &IssuerCertFile() const { return issuer_cert_file_; }; |  | ||||||
| 	[[nodiscard]] const std::string &Name() const { return name_; }; |  | ||||||
| 	[[nodiscard]] Poco::Net::SecureServerSocket CreateSecureSocket(Poco::Logger &L) const; |  | ||||||
| 	[[nodiscard]] int Backlog() const { return backlog_; } |  | ||||||
| 	void LogCert(Poco::Logger &L) const; |  | ||||||
| 	void LogCas(Poco::Logger &L) const; |  | ||||||
| 	static void LogCertInfo(Poco::Logger &L, const Poco::Crypto::X509Certificate &C); |  | ||||||
|  |  | ||||||
|   private: |  | ||||||
| 	std::string address_; |  | ||||||
| 	std::string cert_file_; |  | ||||||
| 	std::string key_file_; |  | ||||||
| 	std::string root_ca_; |  | ||||||
| 	std::string key_file_password_; |  | ||||||
| 	std::string issuer_cert_file_; |  | ||||||
| 	std::string client_cas_; |  | ||||||
| 	std::string cas_; |  | ||||||
| 	uint32_t port_; |  | ||||||
| 	std::string name_; |  | ||||||
| 	int backlog_; |  | ||||||
| 	Poco::Net::Context::VerificationMode level_; |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| class SubSystemServer : public Poco::Util::Application::Subsystem { |  | ||||||
|  |  | ||||||
|   public: |  | ||||||
| 	SubSystemServer(std::string Name, const std::string &LoggingName, std::string SubSystemPrefix); |  | ||||||
| 	void initialize(Poco::Util::Application &self) override; |  | ||||||
| 	void uninitialize() override; |  | ||||||
| 	void reinitialize(Poco::Util::Application &self) override; |  | ||||||
| 	void defineOptions(Poco::Util::OptionSet &options) override; |  | ||||||
| 	inline const std::string & Name() const { return Name_; }; |  | ||||||
| 	const char * name() const override { return Name_.c_str(); } |  | ||||||
|  |  | ||||||
| 	const PropertiesFileServerEntry & Host(uint64_t index) { return ConfigServersList_[index]; }; |  | ||||||
| 	uint64_t HostSize() const { return ConfigServersList_.size(); } |  | ||||||
| 	Poco::Logger &Logger() { return Logger_; }; |  | ||||||
| 	void SetLoggingLevel(Poco::Message::Priority NewPriority) { Logger_.setLevel(NewPriority); } |  | ||||||
| 	int GetLoggingLevel() { return Logger_.getLevel(); } |  | ||||||
| 	virtual int Start() = 0; |  | ||||||
| 	virtual void Stop() = 0; |  | ||||||
|  |  | ||||||
|   protected: |  | ||||||
| 	std::recursive_mutex Mutex_; |  | ||||||
| 	Poco::Logger 		&Logger_; |  | ||||||
| 	std::string 		Name_; |  | ||||||
| 	std::vector<PropertiesFileServerEntry> ConfigServersList_; |  | ||||||
| 	std::string 		SubSystemConfigPrefix_; |  | ||||||
| }; |  | ||||||
| } |  | ||||||
| #endif //UCENTRAL_SUBSYSTEMSERVER_H |  | ||||||
							
								
								
									
										491
									
								
								src/Utils.cpp
									
									
									
									
									
								
							
							
						
						
									
										491
									
								
								src/Utils.cpp
									
									
									
									
									
								
							| @@ -1,491 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
| #include <stdexcept> |  | ||||||
| #include <fstream> |  | ||||||
| #include <cstdlib> |  | ||||||
| #include <regex> |  | ||||||
| #include <random> |  | ||||||
| #include <chrono> |  | ||||||
|  |  | ||||||
| #include "Utils.h" |  | ||||||
|  |  | ||||||
| #include "Poco/Exception.h" |  | ||||||
| #include "Poco/DateTimeFormat.h" |  | ||||||
| #include "Poco/DateTimeFormatter.h" |  | ||||||
| #include "Poco/DateTime.h" |  | ||||||
| #include "Poco/DateTimeParser.h" |  | ||||||
| #include "Poco/StringTokenizer.h" |  | ||||||
| #include "Poco/Message.h" |  | ||||||
| #include "Poco/File.h" |  | ||||||
| #include "Poco/StreamCopier.h" |  | ||||||
| #include "Poco/Path.h" |  | ||||||
|  |  | ||||||
| #include "uCentralProtocol.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
|  |  | ||||||
| namespace OpenWifi::Utils { |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool ValidSerialNumber(const std::string &Serial) { |  | ||||||
| 		return ((Serial.size() < uCentralProtocol::SERIAL_NUMBER_LENGTH) && |  | ||||||
| 				std::all_of(Serial.begin(),Serial.end(),[](auto i){return std::isxdigit(i);})); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter ) { |  | ||||||
| 		std::vector<std::string> ReturnList; |  | ||||||
|  |  | ||||||
| 		unsigned long P=0; |  | ||||||
|  |  | ||||||
| 		while(P<List.size()) |  | ||||||
| 		{ |  | ||||||
| 			unsigned long P2 = List.find_first_of(Delimiter, P); |  | ||||||
| 			if(P2==std::string::npos) { |  | ||||||
| 				ReturnList.push_back(List.substr(P)); |  | ||||||
| 				break; |  | ||||||
| 			} |  | ||||||
| 			else |  | ||||||
| 				ReturnList.push_back(List.substr(P,P2-P)); |  | ||||||
| 			P=P2+1; |  | ||||||
| 		} |  | ||||||
| 		return ReturnList; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string FormatIPv6(const std::string & I ) |  | ||||||
| 	{ |  | ||||||
| 		if(I.substr(0,8) == "[::ffff:") |  | ||||||
| 		{ |  | ||||||
| 			unsigned long PClosingBracket = I.find_first_of(']'); |  | ||||||
|  |  | ||||||
| 			std::string ip = I.substr(8, PClosingBracket-8); |  | ||||||
| 			std::string port = I.substr(PClosingBracket+1); |  | ||||||
| 			return ip + port; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return I; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string SerialToMAC(const std::string &Serial) { |  | ||||||
| 		std::string R = Serial; |  | ||||||
|  |  | ||||||
| 		if(R.size()<12) |  | ||||||
| 			padTo(R,12,'0'); |  | ||||||
| 		else if (R.size()>12) |  | ||||||
| 			R = R.substr(0,12); |  | ||||||
|  |  | ||||||
| 		char buf[18]; |  | ||||||
|  |  | ||||||
| 		buf[0] = R[0]; buf[1] = R[1] ; buf[2] = ':' ; |  | ||||||
| 		buf[3] = R[2] ; buf[4] = R[3]; buf[5] = ':' ; |  | ||||||
| 		buf[6] = R[4]; buf[7] = R[5] ; buf[8] = ':' ; |  | ||||||
| 		buf[9] = R[6] ; buf[10]= R[7]; buf[11] = ':'; |  | ||||||
| 		buf[12] = R[8] ; buf[13]= R[9]; buf[14] = ':'; |  | ||||||
| 		buf[15] = R[10] ; buf[16]= R[11];buf[17] = 0; |  | ||||||
|  |  | ||||||
| 		return buf; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string ToHex(const std::vector<unsigned char> & B) { |  | ||||||
| 		std::string R; |  | ||||||
| 		R.reserve(B.size()*2); |  | ||||||
|  |  | ||||||
| 		static const char hex[] = "0123456789abcdef"; |  | ||||||
|  |  | ||||||
| 		for(const auto &i:B) |  | ||||||
| 		{ |  | ||||||
| 			R += (hex[ (i & 0xf0) >> 4]); |  | ||||||
| 			R += (hex[ (i & 0x0f) ]); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return R; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	inline static const char kEncodeLookup[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |  | ||||||
| 	inline static const char kPadCharacter = '='; |  | ||||||
|  |  | ||||||
| 	std::string base64encode(const byte *input, unsigned long size) { |  | ||||||
| 		std::string encoded; |  | ||||||
| 		encoded.reserve(((size / 3) + (size % 3 > 0)) * 4); |  | ||||||
|  |  | ||||||
| 		std::uint32_t temp; |  | ||||||
|  |  | ||||||
| 		std::size_t i; |  | ||||||
|  |  | ||||||
| 		int ee = (int)(size/3); |  | ||||||
|  |  | ||||||
| 		for (i = 0; i < 3*ee; ++i) { |  | ||||||
| 			temp = input[i++] << 16; |  | ||||||
| 			temp += input[i++] << 8; |  | ||||||
| 			temp += input[i]; |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0000003F)]); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch (size % 3) { |  | ||||||
| 		case 1: |  | ||||||
| 			temp = input[i] << 16; |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); |  | ||||||
| 			encoded.append(2, kPadCharacter); |  | ||||||
| 			break; |  | ||||||
| 		case 2: |  | ||||||
| 			temp = input[i++] << 16; |  | ||||||
| 			temp += input[i] << 8; |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00FC0000) >> 18]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x0003F000) >> 12]); |  | ||||||
| 			encoded.append(1, kEncodeLookup[(temp & 0x00000FC0) >> 6]); |  | ||||||
| 			encoded.append(1, kPadCharacter); |  | ||||||
| 			break; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return encoded; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::vector<byte> base64decode(const std::string& input) |  | ||||||
| 	{ |  | ||||||
| 		if(input.length() % 4) |  | ||||||
| 			throw std::runtime_error("Invalid base64 length!"); |  | ||||||
|  |  | ||||||
| 		std::size_t padding=0; |  | ||||||
|  |  | ||||||
| 		if(input.length()) |  | ||||||
| 		{ |  | ||||||
| 			if(input[input.length() - 1] == kPadCharacter) padding++; |  | ||||||
| 			if(input[input.length() - 2] == kPadCharacter) padding++; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		std::vector<byte> decoded; |  | ||||||
| 		decoded.reserve(((input.length() / 4) * 3) - padding); |  | ||||||
|  |  | ||||||
| 		std::uint32_t temp=0; |  | ||||||
| 		auto it = input.begin(); |  | ||||||
|  |  | ||||||
| 		while(it < input.end()) |  | ||||||
| 		{ |  | ||||||
| 			for(std::size_t i = 0; i < 4; ++i) |  | ||||||
| 			{ |  | ||||||
| 				temp <<= 6; |  | ||||||
| 				if     (*it >= 0x41 && *it <= 0x5A) temp |= *it - 0x41; |  | ||||||
| 				else if(*it >= 0x61 && *it <= 0x7A) temp |= *it - 0x47; |  | ||||||
| 				else if(*it >= 0x30 && *it <= 0x39) temp |= *it + 0x04; |  | ||||||
| 				else if(*it == 0x2B)                temp |= 0x3E; |  | ||||||
| 				else if(*it == 0x2F)                temp |= 0x3F; |  | ||||||
| 				else if(*it == kPadCharacter) |  | ||||||
| 				{ |  | ||||||
| 					switch(input.end() - it) |  | ||||||
| 					{ |  | ||||||
| 					case 1: |  | ||||||
| 						decoded.push_back((temp >> 16) & 0x000000FF); |  | ||||||
| 						decoded.push_back((temp >> 8 ) & 0x000000FF); |  | ||||||
| 						return decoded; |  | ||||||
| 					case 2: |  | ||||||
| 						decoded.push_back((temp >> 10) & 0x000000FF); |  | ||||||
| 						return decoded; |  | ||||||
| 					default: |  | ||||||
| 						throw std::runtime_error("Invalid padding in base64!"); |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 				else throw std::runtime_error("Invalid character in base64!"); |  | ||||||
|  |  | ||||||
| 				++it; |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			decoded.push_back((temp >> 16) & 0x000000FF); |  | ||||||
| 			decoded.push_back((temp >> 8 ) & 0x000000FF); |  | ||||||
| 			decoded.push_back((temp      ) & 0x000000FF); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		return decoded; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string to_RFC3339(uint64_t t) |  | ||||||
| 	{ |  | ||||||
| 		if(t==0) |  | ||||||
| 			return ""; |  | ||||||
| 		return Poco::DateTimeFormatter::format(Poco::DateTime(Poco::Timestamp::fromEpochTime(t)), Poco::DateTimeFormat::ISO8601_FORMAT); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t from_RFC3339(const std::string &TimeString) |  | ||||||
| 	{ |  | ||||||
| 		if(TimeString.empty() || TimeString=="0") |  | ||||||
| 			return 0; |  | ||||||
|  |  | ||||||
| 		try { |  | ||||||
| 			int             TZ; |  | ||||||
| 			Poco::DateTime  DT = Poco::DateTimeParser::parse(Poco::DateTimeFormat::ISO8601_FORMAT,TimeString,TZ); |  | ||||||
| 			return DT.timestamp().epochTime(); |  | ||||||
| 		} |  | ||||||
| 		catch( const Poco::Exception & E ) |  | ||||||
| 		{ |  | ||||||
|  |  | ||||||
| 		} |  | ||||||
| 		return 0; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds) { |  | ||||||
| 		Poco::StringTokenizer	TimeTokens(Time,":",Poco::StringTokenizer::TOK_TRIM); |  | ||||||
|  |  | ||||||
| 		Hours =  Minutes = Hours = 0 ; |  | ||||||
| 		if(TimeTokens.count()==1) { |  | ||||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); |  | ||||||
| 		} else if(TimeTokens.count()==2) { |  | ||||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); |  | ||||||
| 			Minutes = std::atoi(TimeTokens[1].c_str()); |  | ||||||
| 		} else if(TimeTokens.count()==3) { |  | ||||||
| 			Hours 	= std::atoi(TimeTokens[0].c_str()); |  | ||||||
| 			Minutes = std::atoi(TimeTokens[1].c_str()); |  | ||||||
| 			Seconds = std::atoi(TimeTokens[2].c_str()); |  | ||||||
| 		} else |  | ||||||
| 			return false; |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 	bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day) { |  | ||||||
| 		Poco::StringTokenizer	DateTokens(Time,"-",Poco::StringTokenizer::TOK_TRIM); |  | ||||||
|  |  | ||||||
| 		Year =  Month = Day = 0 ; |  | ||||||
| 		if(DateTokens.count()==3) { |  | ||||||
| 			Year 	= std::atoi(DateTokens[0].c_str()); |  | ||||||
| 			Month 	= std::atoi(DateTokens[1].c_str()); |  | ||||||
| 			Day 	= std::atoi(DateTokens[2].c_str()); |  | ||||||
| 		} else |  | ||||||
| 			return false; |  | ||||||
| 		return true; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2) { |  | ||||||
| 		if(H1<H2) |  | ||||||
| 			return true; |  | ||||||
| 		if(H1>H2) |  | ||||||
| 			return false; |  | ||||||
| 		if(M1<M2) |  | ||||||
| 			return true; |  | ||||||
| 		if(M2>M1) |  | ||||||
| 			return false; |  | ||||||
| 		if(S1<=S2) |  | ||||||
| 			return true; |  | ||||||
| 		return false; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string LogLevelToString(int Level) { |  | ||||||
| 		switch(Level) { |  | ||||||
| 			case Poco::Message::PRIO_DEBUG: return "debug"; |  | ||||||
| 			case Poco::Message::PRIO_INFORMATION: return "information"; |  | ||||||
| 			case Poco::Message::PRIO_FATAL: return "fatal"; |  | ||||||
| 			case Poco::Message::PRIO_WARNING: return "warning"; |  | ||||||
| 			case Poco::Message::PRIO_NOTICE: return "notice"; |  | ||||||
| 			case Poco::Message::PRIO_CRITICAL: return "critical"; |  | ||||||
| 			case Poco::Message::PRIO_ERROR: return "error"; |  | ||||||
| 			case Poco::Message::PRIO_TRACE: return "trace"; |  | ||||||
| 			default: return "none"; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool SerialNumberMatch(const std::string &S1, const std::string &S2, int Bits) { |  | ||||||
| 		auto S1_i = SerialNumberToInt(S1); |  | ||||||
| 		auto S2_i = SerialNumberToInt(S2); |  | ||||||
| 		return ((S1_i>>Bits)==(S2_i>>Bits)); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t SerialNumberToInt(const std::string & S) { |  | ||||||
| 		uint64_t R=0; |  | ||||||
|  |  | ||||||
| 		for(const auto &i:S) |  | ||||||
| 			if(i>='0' && i<='9') { |  | ||||||
| 				R <<= 4; |  | ||||||
| 				R += (i-'0'); |  | ||||||
| 			} else if(i>='a' && i<='f') { |  | ||||||
| 				R <<= 4; |  | ||||||
| 				R += (i-'a') + 10 ; |  | ||||||
| 			} else if(i>='A' && i<='F') { |  | ||||||
| 				R <<= 4; |  | ||||||
| 				R += (i-'A') + 10 ; |  | ||||||
| 			} |  | ||||||
| 		return R; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t SerialNumberToOUI(const std::string & S) { |  | ||||||
| 		uint64_t Result = 0 ; |  | ||||||
| 		int Digits=0; |  | ||||||
|  |  | ||||||
| 		for(const auto &i:S) { |  | ||||||
| 			if(std::isxdigit(i)) { |  | ||||||
| 				if(i>='0' && i<='9') { |  | ||||||
| 					Result <<=4; |  | ||||||
| 					Result += i-'0'; |  | ||||||
| 				} else if(i>='A' && i<='F') { |  | ||||||
| 					Result <<=4; |  | ||||||
| 					Result += i-'A'+10; |  | ||||||
| 				} else if(i>='a' && i<='f') { |  | ||||||
| 					Result <<=4; |  | ||||||
| 					Result += i-'a'+10; |  | ||||||
| 				} |  | ||||||
| 				Digits++; |  | ||||||
| 				if(Digits==6) |  | ||||||
| 					break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t GetDefaultMacAsInt64() { |  | ||||||
| 		uint64_t Result=0; |  | ||||||
| 		auto IFaceList = Poco::Net::NetworkInterface::list(); |  | ||||||
|  |  | ||||||
| 		for(const auto &iface:IFaceList) { |  | ||||||
| 			if(iface.isRunning() && !iface.isLoopback()) { |  | ||||||
| 				auto MAC = iface.macAddress(); |  | ||||||
| 				for (auto const &i : MAC) { |  | ||||||
| 					Result <<= 8; |  | ||||||
| 					Result += (uint8_t)i; |  | ||||||
| 				} |  | ||||||
| 				if (Result != 0) |  | ||||||
| 					break; |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	void SaveSystemId(uint64_t Id) { |  | ||||||
| 		try { |  | ||||||
| 			std::ofstream O; |  | ||||||
| 			O.open(Daemon()->DataDir() + "/system.id",std::ios::binary | std::ios::trunc); |  | ||||||
| 			O << Id; |  | ||||||
| 			O.close(); |  | ||||||
| 		} catch (...) |  | ||||||
| 		{ |  | ||||||
| 			std::cout << "Could not save system ID" << std::endl; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t InitializeSystemId() { |  | ||||||
| 		std::random_device	RDev; |  | ||||||
| 		std::srand(RDev()); |  | ||||||
| 		std::chrono::high_resolution_clock	Clock; |  | ||||||
| 		auto Now = Clock.now().time_since_epoch().count(); |  | ||||||
| 		auto S = (GetDefaultMacAsInt64() + std::rand() + Now)  ; |  | ||||||
| 		SaveSystemId(S); |  | ||||||
| 		std::cout << "ID: " << S << std::endl; |  | ||||||
| 		return S; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	uint64_t GetSystemId() { |  | ||||||
| 		uint64_t ID=0; |  | ||||||
|  |  | ||||||
| 		// if the system ID file exists, open and read it. |  | ||||||
| 		Poco::File	SID( Daemon()->DataDir() + "/system.id"); |  | ||||||
| 		try { |  | ||||||
| 			if (SID.exists()) { |  | ||||||
| 				std::ifstream I; |  | ||||||
| 				I.open(SID.path()); |  | ||||||
| 				I >> ID; |  | ||||||
| 				I.close(); |  | ||||||
| 				if (ID == 0) |  | ||||||
| 					return InitializeSystemId(); |  | ||||||
| 				return ID; |  | ||||||
| 			} else { |  | ||||||
| 				return InitializeSystemId(); |  | ||||||
| 			} |  | ||||||
| 		} catch (...) { |  | ||||||
| 			return InitializeSystemId(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	bool ValidEMailAddress(const std::string &email) { |  | ||||||
| 		// define a regular expression |  | ||||||
| 		const std::regex pattern |  | ||||||
| 			("(\\w+)(\\.|_)?(\\w*)@(\\w+)(\\.(\\w+))+"); |  | ||||||
|  |  | ||||||
| 		// try to match the string with the regular expression |  | ||||||
| 		return std::regex_match(email, pattern); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string LoadFile( const Poco::File & F) { |  | ||||||
| 	    std::string Result; |  | ||||||
| 	    try { |  | ||||||
| 	        std::ostringstream OS; |  | ||||||
| 	        std::ifstream   IF(F.path()); |  | ||||||
| 	        Poco::StreamCopier::copyStream(IF, OS); |  | ||||||
| 	        Result = OS.str(); |  | ||||||
| 	    } catch (...) { |  | ||||||
|  |  | ||||||
| 	    } |  | ||||||
| 	    return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     void ReplaceVariables( std::string & Content , const Types::StringPairVec & P) { |  | ||||||
| 	    for(const auto &[Variable,Value]:P) { |  | ||||||
| 	        Poco::replaceInPlace(Content,"${" + Variable + "}", Value); |  | ||||||
| 	    } |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     MediaTypeEncoding FindMediaType(const Poco::File &F) { |  | ||||||
| 	    const auto E = Poco::Path(F.path()).getExtension(); |  | ||||||
| 	    if(E=="png") |  | ||||||
| 	        return MediaTypeEncoding{   .Encoding = BINARY, |  | ||||||
|                                         .ContentType = "image/png" }; |  | ||||||
| 	    if(E=="gif") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = BINARY, |  | ||||||
|                                         .ContentType = "image/gif" }; |  | ||||||
|         if(E=="jpeg" || E=="jpg") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = BINARY, |  | ||||||
|                                         .ContentType = "image/jpeg" }; |  | ||||||
|         if(E=="svg" || E=="svgz") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, |  | ||||||
|                                         .ContentType = "image/svg+xml" }; |  | ||||||
|         if(E=="html") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, |  | ||||||
|                                         .ContentType = "text/html" }; |  | ||||||
|         if(E=="css") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, |  | ||||||
|                                         .ContentType = "text/css" }; |  | ||||||
|         if(E=="js") |  | ||||||
|             return MediaTypeEncoding{   .Encoding = PLAIN, |  | ||||||
|                                         .ContentType = "application/javascript" }; |  | ||||||
|         return MediaTypeEncoding{       .Encoding = BINARY, |  | ||||||
|                                         .ContentType = "application/octet-stream" }; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	std::string BinaryFileToHexString(const Poco::File &F) { |  | ||||||
|         static const char hex[] = "0123456789abcdef"; |  | ||||||
|         std::string Result; |  | ||||||
|         try { |  | ||||||
|             std::ifstream IF(F.path()); |  | ||||||
|  |  | ||||||
|             int Count = 0; |  | ||||||
|             while (IF.good()) { |  | ||||||
|                 if (Count) |  | ||||||
|                     Result += ", "; |  | ||||||
|                 if ((Count % 32) == 0) |  | ||||||
|                     Result += "\r\n"; |  | ||||||
|                 Count++; |  | ||||||
|                 unsigned char C = IF.get(); |  | ||||||
|                 Result += "0x"; |  | ||||||
|                 Result += (char) (hex[(C & 0xf0) >> 4]); |  | ||||||
|                 Result += (char) (hex[(C & 0x0f)]); |  | ||||||
|             } |  | ||||||
|         } catch(...) { |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|         return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|     std::string SecondsToNiceText(uint64_t Seconds) { |  | ||||||
| 	    std::string Result; |  | ||||||
| 	    int Days = Seconds / (24*60*60); |  | ||||||
| 	    Seconds -= Days * (24*60*60); |  | ||||||
| 	    int Hours= Seconds / (60*60); |  | ||||||
| 	    Seconds -= Hours * (60*60); |  | ||||||
| 	    int Minutes = Seconds / 60; |  | ||||||
| 	    Seconds -= Minutes * 60; |  | ||||||
| 	    Result = std::to_string(Days) +" days, " + std::to_string(Hours) + ":" + std::to_string(Minutes) + ":" + std::to_string(Seconds); |  | ||||||
| 	    return Result; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } |  | ||||||
							
								
								
									
										87
									
								
								src/Utils.h
									
									
									
									
									
								
							
							
						
						
									
										87
									
								
								src/Utils.h
									
									
									
									
									
								
							| @@ -1,87 +0,0 @@ | |||||||
| // |  | ||||||
| //	License type: BSD 3-Clause License |  | ||||||
| //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE |  | ||||||
| // |  | ||||||
| //	Created by Stephane Bourque on 2021-03-04. |  | ||||||
| //	Arilia Wireless Inc. |  | ||||||
| // |  | ||||||
|  |  | ||||||
| #ifndef UCENTRALGW_UTILS_H |  | ||||||
| #define UCENTRALGW_UTILS_H |  | ||||||
|  |  | ||||||
| #include <vector> |  | ||||||
| #include <string> |  | ||||||
| #include <iomanip> |  | ||||||
| #include <sstream> |  | ||||||
|  |  | ||||||
| #include "Poco/Net/NetworkInterface.h" |  | ||||||
| #include "Poco/Net/IPAddress.h" |  | ||||||
| #include "Poco/String.h" |  | ||||||
| #include "Poco/File.h" |  | ||||||
| #include "OpenWifiTypes.h" |  | ||||||
|  |  | ||||||
| #define DBGLINE { std::cout << __FILE__ << ":" << __func__ << ":" << __LINE__ << std::endl; }; |  | ||||||
|  |  | ||||||
| namespace OpenWifi::Utils { |  | ||||||
|  |  | ||||||
|     enum MediaTypeEncodings { |  | ||||||
|         PLAIN, |  | ||||||
|         BINARY, |  | ||||||
|         BASE64 |  | ||||||
|     }; |  | ||||||
|     struct MediaTypeEncoding { |  | ||||||
|         MediaTypeEncodings  Encoding=PLAIN; |  | ||||||
|         std::string         ContentType; |  | ||||||
|     }; |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::vector<std::string> Split(const std::string &List, char Delimiter=','); |  | ||||||
| 	[[nodiscard]] std::string FormatIPv6(const std::string & I ); |  | ||||||
| 	inline void padTo(std::string& str, size_t num, char paddingChar = '\0') { |  | ||||||
| 		str.append(num - str.length() % num, paddingChar); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string SerialToMAC(const std::string &Serial); |  | ||||||
| 	[[nodiscard]] std::string ToHex(const std::vector<unsigned char> & B); |  | ||||||
|  |  | ||||||
| 	using byte = std::uint8_t; |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] std::string base64encode(const byte *input, unsigned long size); |  | ||||||
| 	std::vector<byte> base64decode(const std::string& input); |  | ||||||
|  |  | ||||||
| //	[[nodiscard]] std::string to_RFC3339(uint64_t t); |  | ||||||
| //	[[nodiscard]] uint64_t from_RFC3339(const std::string &t); |  | ||||||
|  |  | ||||||
| 	bool ParseTime(const std::string &Time, int & Hours, int & Minutes, int & Seconds); |  | ||||||
| 	bool ParseDate(const std::string &Time, int & Year, int & Month, int & Day); |  | ||||||
| 	bool CompareTime( int H1, int H2, int M1, int M2, int S1, int S2); |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool ValidSerialNumber(const std::string &Serial); |  | ||||||
| 	[[nodiscard]] std::string LogLevelToString(int Level); |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool SerialNumberMatch(const std::string &S1, const std::string &S2, int extrabits=2); |  | ||||||
| 	[[nodiscard]] uint64_t SerialNumberToInt(const std::string & S); |  | ||||||
| 	[[nodiscard]] uint64_t SerialNumberToOUI(const std::string & S); |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] uint64_t GetDefaultMacAsInt64(); |  | ||||||
| 	[[nodiscard]] uint64_t GetSystemId(); |  | ||||||
|  |  | ||||||
| 	[[nodiscard]] bool ValidEMailAddress(const std::string &E); |  | ||||||
| 	[[nodiscard]] std::string LoadFile( const Poco::File & F); |  | ||||||
|     void ReplaceVariables( std::string & Content , const Types::StringPairVec & P); |  | ||||||
|  |  | ||||||
|     [[nodiscard]] MediaTypeEncoding FindMediaType(const Poco::File &F); |  | ||||||
|     [[nodiscard]] std::string BinaryFileToHexString( const Poco::File &F); |  | ||||||
|     [[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds); |  | ||||||
|  |  | ||||||
| 	template< typename T > |  | ||||||
| 	std::string int_to_hex( T i ) |  | ||||||
| 	{ |  | ||||||
| 		std::stringstream stream; |  | ||||||
| 		stream << std::setfill ('0') << std::setw(12) |  | ||||||
| 		<< std::hex << i; |  | ||||||
| 		return stream.str(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
| #endif // UCENTRALGW_UTILS_H |  | ||||||
							
								
								
									
										273
									
								
								src/framework/CountryCodes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								src/framework/CountryCodes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,273 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-08. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OWPROV_COUNTRYCODES_H | ||||||
|  | #define OWPROV_COUNTRYCODES_H | ||||||
|  |  | ||||||
|  | #include <vector> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |      | ||||||
|  |     struct CountryInfo { | ||||||
|  |         std::string     code; | ||||||
|  |         std::string     name; | ||||||
|  |     }; | ||||||
|  |      | ||||||
|  |     inline static const std::vector<CountryInfo> CountryCodes { | ||||||
|  |         { .code= "US", .name= "United States" }, | ||||||
|  |         { .code= "GB", .name= "United Kingdom" }, | ||||||
|  |         { .code= "CA", .name= "Canada" }, | ||||||
|  |         { .code= "AF", .name= "Afghanistan" }, | ||||||
|  |         { .code= "AX", .name= "Aland Islands" }, | ||||||
|  |         { .code= "AL", .name= "Albania" }, | ||||||
|  |         { .code= "DZ", .name= "Algeria" }, | ||||||
|  |         { .code= "AS", .name= "American Samoa" }, | ||||||
|  |         { .code= "AD", .name= "Andorra" }, | ||||||
|  |         { .code= "AO", .name= "Angola" }, | ||||||
|  |         { .code= "AI", .name= "Anguilla" }, | ||||||
|  |         { .code= "AQ", .name= "Antarctica" }, | ||||||
|  |         { .code= "AG", .name= "Antigua And Barbuda" }, | ||||||
|  |         { .code= "AR", .name= "Argentina" }, | ||||||
|  |         { .code= "AM", .name= "Armenia" }, | ||||||
|  |         { .code= "AN", .name= "Netherlands Antilles" }, | ||||||
|  |         { .code= "AW", .name= "Aruba" }, | ||||||
|  |         { .code= "AU", .name= "Australia" }, | ||||||
|  |         { .code= "AT", .name= "Austria" }, | ||||||
|  |         { .code= "AZ", .name= "Azerbaijan" }, | ||||||
|  |         { .code= "BS", .name= "Bahamas" }, | ||||||
|  |         { .code= "BH", .name= "Bahrain" }, | ||||||
|  |         { .code= "BD", .name= "Bangladesh" }, | ||||||
|  |         { .code= "BB", .name= "Barbados" }, | ||||||
|  |         { .code= "BY", .name= "Belarus" }, | ||||||
|  |         { .code= "BE", .name= "Belgium" }, | ||||||
|  |         { .code= "BZ", .name= "Belize" }, | ||||||
|  |         { .code= "BJ", .name= "Benin" }, | ||||||
|  |         { .code= "BM", .name= "Bermuda" }, | ||||||
|  |         { .code= "BT", .name= "Bhutan" }, | ||||||
|  |         { .code= "BO", .name= "Bolivia" }, | ||||||
|  |         { .code= "BA", .name= "Bosnia And Herzegovina" }, | ||||||
|  |         { .code= "BW", .name= "Botswana" }, | ||||||
|  |         { .code= "BV", .name= "Bouvet Island" }, | ||||||
|  |         { .code= "BR", .name= "Brazil" }, | ||||||
|  |         { .code= "IO", .name= "British Indian Ocean Territory" }, | ||||||
|  |         { .code= "BN", .name= "Brunei Darussalam" }, | ||||||
|  |         { .code= "BG", .name= "Bulgaria" }, | ||||||
|  |         { .code= "BF", .name= "Burkina Faso" }, | ||||||
|  |         { .code= "BI", .name= "Burundi" }, | ||||||
|  |         { .code= "KH", .name= "Cambodia" }, | ||||||
|  |         { .code= "CM", .name= "Cameroon" }, | ||||||
|  |         { .code= "CA", .name= "Canada" }, | ||||||
|  |         { .code= "CV", .name= "Cape Verde" }, | ||||||
|  |         { .code= "KY", .name= "Cayman Islands" }, | ||||||
|  |         { .code= "CF", .name= "Central African Republic" }, | ||||||
|  |         { .code= "TD", .name= "Chad" }, | ||||||
|  |         { .code= "CL", .name= "Chile" }, | ||||||
|  |         { .code= "CN", .name= "China" }, | ||||||
|  |         { .code= "CX", .name= "Christmas Island" }, | ||||||
|  |         { .code= "CC", .name= "Cocos (Keeling) Islands" }, | ||||||
|  |         { .code= "CO", .name= "Colombia" }, | ||||||
|  |         { .code= "KM", .name= "Comoros" }, | ||||||
|  |         { .code= "CG", .name= "Congo" }, | ||||||
|  |         { .code= "CD", .name= "Congo, Democratic Republic" }, | ||||||
|  |         { .code= "CK", .name= "Cook Islands" }, | ||||||
|  |         { .code= "CR", .name= "Costa Rica" }, | ||||||
|  |         { .code= "CI", .name= "Cote D\"Ivoire" }, | ||||||
|  |         { .code= "HR", .name= "Croatia" }, | ||||||
|  |         { .code= "CU", .name= "Cuba" }, | ||||||
|  |         { .code= "CY", .name= "Cyprus" }, | ||||||
|  |         { .code= "CZ", .name= "Czech Republic" }, | ||||||
|  |         { .code= "DK", .name= "Denmark" }, | ||||||
|  |         { .code= "DJ", .name= "Djibouti" }, | ||||||
|  |         { .code= "DM", .name= "Dominica" }, | ||||||
|  |         { .code= "DO", .name= "Dominican Republic" }, | ||||||
|  |         { .code= "EC", .name= "Ecuador" }, | ||||||
|  |         { .code= "EG", .name= "Egypt" }, | ||||||
|  |         { .code= "SV", .name= "El Salvador" }, | ||||||
|  |         { .code= "GQ", .name= "Equatorial Guinea" }, | ||||||
|  |         { .code= "ER", .name= "Eritrea" }, | ||||||
|  |         { .code= "EE", .name= "Estonia" }, | ||||||
|  |         { .code= "ET", .name= "Ethiopia" }, | ||||||
|  |         { .code= "FK", .name= "Falkland Islands (Malvinas)" }, | ||||||
|  |         { .code= "FO", .name= "Faroe Islands" }, | ||||||
|  |         { .code= "FJ", .name= "Fiji" }, | ||||||
|  |         { .code= "FI", .name= "Finland" }, | ||||||
|  |         { .code= "FR", .name= "France" }, | ||||||
|  |         { .code= "GF", .name= "French Guiana" }, | ||||||
|  |         { .code= "PF", .name= "French Polynesia" }, | ||||||
|  |         { .code= "TF", .name= "French Southern Territories" }, | ||||||
|  |         { .code= "GA", .name= "Gabon" }, | ||||||
|  |         { .code= "GM", .name= "Gambia" }, | ||||||
|  |         { .code= "GE", .name= "Georgia" }, | ||||||
|  |         { .code= "DE", .name= "Germany" }, | ||||||
|  |         { .code= "GH", .name= "Ghana" }, | ||||||
|  |         { .code= "GI", .name= "Gibraltar" }, | ||||||
|  |         { .code= "GR", .name= "Greece" }, | ||||||
|  |         { .code= "GL", .name= "Greenland" }, | ||||||
|  |         { .code= "GD", .name= "Grenada" }, | ||||||
|  |         { .code= "GP", .name= "Guadeloupe" }, | ||||||
|  |         { .code= "GU", .name= "Guam" }, | ||||||
|  |         { .code= "GT", .name= "Guatemala" }, | ||||||
|  |         { .code= "GG", .name= "Guernsey" }, | ||||||
|  |         { .code= "GN", .name= "Guinea" }, | ||||||
|  |         { .code= "GW", .name= "Guinea-Bissau" }, | ||||||
|  |         { .code= "GY", .name= "Guyana" }, | ||||||
|  |         { .code= "HT", .name= "Haiti" }, | ||||||
|  |         { .code= "HM", .name= "Heard Island & Mcdonald Islands" }, | ||||||
|  |         { .code= "VA", .name= "Holy See (Vatican City State)" }, | ||||||
|  |         { .code= "HN", .name= "Honduras" }, | ||||||
|  |         { .code= "HK", .name= "Hong Kong" }, | ||||||
|  |         { .code= "HU", .name= "Hungary" }, | ||||||
|  |         { .code= "IS", .name= "Iceland" }, | ||||||
|  |         { .code= "IN", .name= "India" }, | ||||||
|  |         { .code= "ID", .name= "Indonesia" }, | ||||||
|  |         { .code= "IR", .name= "Iran, Islamic Republic Of" }, | ||||||
|  |         { .code= "IQ", .name= "Iraq" }, | ||||||
|  |         { .code= "IE", .name= "Ireland" }, | ||||||
|  |         { .code= "IM", .name= "Isle Of Man" }, | ||||||
|  |         { .code= "IL", .name= "Israel" }, | ||||||
|  |         { .code= "IT", .name= "Italy" }, | ||||||
|  |         { .code= "JM", .name= "Jamaica" }, | ||||||
|  |         { .code= "JP", .name= "Japan" }, | ||||||
|  |         { .code= "JE", .name= "Jersey" }, | ||||||
|  |         { .code= "JO", .name= "Jordan" }, | ||||||
|  |         { .code= "KZ", .name= "Kazakhstan" }, | ||||||
|  |         { .code= "KE", .name= "Kenya" }, | ||||||
|  |         { .code= "KI", .name= "Kiribati" }, | ||||||
|  |         { .code= "KR", .name= "Korea" }, | ||||||
|  |         { .code= "KW", .name= "Kuwait" }, | ||||||
|  |         { .code= "KG", .name= "Kyrgyzstan" }, | ||||||
|  |         { .code= "LA", .name= "Lao People\"s Democratic Republic" }, | ||||||
|  |         { .code= "LV", .name= "Latvia" }, | ||||||
|  |         { .code= "LB", .name= "Lebanon" }, | ||||||
|  |         { .code= "LS", .name= "Lesotho" }, | ||||||
|  |         { .code= "LR", .name= "Liberia" }, | ||||||
|  |         { .code= "LY", .name= "Libyan Arab Jamahiriya" }, | ||||||
|  |         { .code= "LI", .name= "Liechtenstein" }, | ||||||
|  |         { .code= "LT", .name= "Lithuania" }, | ||||||
|  |         { .code= "LU", .name= "Luxembourg" }, | ||||||
|  |         { .code= "MO", .name= "Macao" }, | ||||||
|  |         { .code= "MK", .name= "Macedonia" }, | ||||||
|  |         { .code= "MG", .name= "Madagascar" }, | ||||||
|  |         { .code= "MW", .name= "Malawi" }, | ||||||
|  |         { .code= "MY", .name= "Malaysia" }, | ||||||
|  |         { .code= "MV", .name= "Maldives" }, | ||||||
|  |         { .code= "ML", .name= "Mali" }, | ||||||
|  |         { .code= "MT", .name= "Malta" }, | ||||||
|  |         { .code= "MH", .name= "Marshall Islands" }, | ||||||
|  |         { .code= "MQ", .name= "Martinique" }, | ||||||
|  |         { .code= "MR", .name= "Mauritania" }, | ||||||
|  |         { .code= "MU", .name= "Mauritius" }, | ||||||
|  |         { .code= "YT", .name= "Mayotte" }, | ||||||
|  |         { .code= "MX", .name= "Mexico" }, | ||||||
|  |         { .code= "FM", .name= "Micronesia, Federated States Of" }, | ||||||
|  |         { .code= "MD", .name= "Moldova" }, | ||||||
|  |         { .code= "MC", .name= "Monaco" }, | ||||||
|  |         { .code= "MN", .name= "Mongolia" }, | ||||||
|  |         { .code= "ME", .name= "Montenegro" }, | ||||||
|  |         { .code= "MS", .name= "Montserrat" }, | ||||||
|  |         { .code= "MA", .name= "Morocco" }, | ||||||
|  |         { .code= "MZ", .name= "Mozambique" }, | ||||||
|  |         { .code= "MM", .name= "Myanmar" }, | ||||||
|  |         { .code= "NA", .name= "Namibia" }, | ||||||
|  |         { .code= "NR", .name= "Nauru" }, | ||||||
|  |         { .code= "NP", .name= "Nepal" }, | ||||||
|  |         { .code= "NL", .name= "Netherlands" }, | ||||||
|  |         { .code= "AN", .name= "Netherlands Antilles" }, | ||||||
|  |         { .code= "NC", .name= "New Caledonia" }, | ||||||
|  |         { .code= "NZ", .name= "New Zealand" }, | ||||||
|  |         { .code= "NI", .name= "Nicaragua" }, | ||||||
|  |         { .code= "NE", .name= "Niger" }, | ||||||
|  |         { .code= "NG", .name= "Nigeria" }, | ||||||
|  |         { .code= "NU", .name= "Niue" }, | ||||||
|  |         { .code= "NF", .name= "Norfolk Island" }, | ||||||
|  |         { .code= "MP", .name= "Northern Mariana Islands" }, | ||||||
|  |         { .code= "NO", .name= "Norway" }, | ||||||
|  |         { .code= "OM", .name= "Oman" }, | ||||||
|  |         { .code= "PK", .name= "Pakistan" }, | ||||||
|  |         { .code= "PW", .name= "Palau" }, | ||||||
|  |         { .code= "PS", .name= "Palestinian Territory, Occupied" }, | ||||||
|  |         { .code= "PA", .name= "Panama" }, | ||||||
|  |         { .code= "PG", .name= "Papua New Guinea" }, | ||||||
|  |         { .code= "PY", .name= "Paraguay" }, | ||||||
|  |         { .code= "PE", .name= "Peru" }, | ||||||
|  |         { .code= "PH", .name= "Philippines" }, | ||||||
|  |         { .code= "PN", .name= "Pitcairn" }, | ||||||
|  |         { .code= "PL", .name= "Poland" }, | ||||||
|  |         { .code= "PT", .name= "Portugal" }, | ||||||
|  |         { .code= "PR", .name= "Puerto Rico" }, | ||||||
|  |         { .code= "QA", .name= "Qatar" }, | ||||||
|  |         { .code= "RE", .name= "Reunion" }, | ||||||
|  |         { .code= "RO", .name= "Romania" }, | ||||||
|  |         { .code= "RU", .name= "Russian Federation" }, | ||||||
|  |         { .code= "RW", .name= "Rwanda" }, | ||||||
|  |         { .code= "BL", .name= "Saint Barthelemy" }, | ||||||
|  |         { .code= "SH", .name= "Saint Helena" }, | ||||||
|  |         { .code= "KN", .name= "Saint Kitts And Nevis" }, | ||||||
|  |         { .code= "LC", .name= "Saint Lucia" }, | ||||||
|  |         { .code= "MF", .name= "Saint Martin" }, | ||||||
|  |         { .code= "PM", .name= "Saint Pierre And Miquelon" }, | ||||||
|  |         { .code= "VC", .name= "Saint Vincent And Grenadines" }, | ||||||
|  |         { .code= "WS", .name= "Samoa" }, | ||||||
|  |         { .code= "SM", .name= "San Marino" }, | ||||||
|  |         { .code= "ST", .name= "Sao Tome And Principe" }, | ||||||
|  |         { .code= "SA", .name= "Saudi Arabia" }, | ||||||
|  |         { .code= "SN", .name= "Senegal" }, | ||||||
|  |         { .code= "RS", .name= "Serbia" }, | ||||||
|  |         { .code= "SC", .name= "Seychelles" }, | ||||||
|  |         { .code= "SL", .name= "Sierra Leone" }, | ||||||
|  |         { .code= "SG", .name= "Singapore" }, | ||||||
|  |         { .code= "SK", .name= "Slovakia" }, | ||||||
|  |         { .code= "SI", .name= "Slovenia" }, | ||||||
|  |         { .code= "SB", .name= "Solomon Islands" }, | ||||||
|  |         { .code= "SO", .name= "Somalia" }, | ||||||
|  |         { .code= "ZA", .name= "South Africa" }, | ||||||
|  |         { .code= "GS", .name= "South Georgia And Sandwich Isl." }, | ||||||
|  |         { .code= "ES", .name= "Spain" }, | ||||||
|  |         { .code= "LK", .name= "Sri Lanka" }, | ||||||
|  |         { .code= "SD", .name= "Sudan" }, | ||||||
|  |         { .code= "SR", .name= "Suriname" }, | ||||||
|  |         { .code= "SJ", .name= "Svalbard And Jan Mayen" }, | ||||||
|  |         { .code= "SZ", .name= "Swaziland" }, | ||||||
|  |         { .code= "SE", .name= "Sweden" }, | ||||||
|  |         { .code= "CH", .name= "Switzerland" }, | ||||||
|  |         { .code= "SY", .name= "Syrian Arab Republic" }, | ||||||
|  |         { .code= "TW", .name= "Taiwan" }, | ||||||
|  |         { .code= "TJ", .name= "Tajikistan" }, | ||||||
|  |         { .code= "TZ", .name= "Tanzania" }, | ||||||
|  |         { .code= "TH", .name= "Thailand" }, | ||||||
|  |         { .code= "TL", .name= "Timor-Leste" }, | ||||||
|  |         { .code= "TG", .name= "Togo" }, | ||||||
|  |         { .code= "TK", .name= "Tokelau" }, | ||||||
|  |         { .code= "TO", .name= "Tonga" }, | ||||||
|  |         { .code= "TT", .name= "Trinidad And Tobago" }, | ||||||
|  |         { .code= "TN", .name= "Tunisia" }, | ||||||
|  |         { .code= "TR", .name= "Turkey" }, | ||||||
|  |         { .code= "TM", .name= "Turkmenistan" }, | ||||||
|  |         { .code= "TC", .name= "Turks And Caicos Islands" }, | ||||||
|  |         { .code= "TV", .name= "Tuvalu" }, | ||||||
|  |         { .code= "UG", .name= "Uganda" }, | ||||||
|  |         { .code= "UA", .name= "Ukraine" }, | ||||||
|  |         { .code= "AE", .name= "United Arab Emirates" }, | ||||||
|  |         { .code= "GB", .name= "United Kingdom" }, | ||||||
|  |         { .code= "US", .name= "United States" }, | ||||||
|  |         { .code= "UM", .name= "United States Outlying Islands" }, | ||||||
|  |         { .code= "UY", .name= "Uruguay" }, | ||||||
|  |         { .code= "UZ", .name= "Uzbekistan" }, | ||||||
|  |         { .code= "VU", .name= "Vanuatu" }, | ||||||
|  |         { .code= "VE", .name= "Venezuela" }, | ||||||
|  |         { .code= "VN", .name= "Viet Nam" }, | ||||||
|  |         { .code= "VG", .name= "Virgin Islands, British" }, | ||||||
|  |         { .code= "VI", .name= "Virgin Islands, U.S." }, | ||||||
|  |         { .code= "WF", .name= "Wallis And Futuna" }, | ||||||
|  |         { .code= "EH", .name= "Western Sahara" }, | ||||||
|  |         { .code= "YE", .name= "Yemen" }, | ||||||
|  |         { .code= "ZM", .name= "Zambia" }, | ||||||
|  |         { .code= "ZW", .name= "Zimbabwe" } | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OWPROV_COUNTRYCODES_H | ||||||
| @@ -1,7 +1,10 @@ | |||||||
| //
 | //
 | ||||||
| // Created by stephane bourque on 2021-06-07.
 | //	License type: BSD 3-Clause License
 | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
 | ||||||
|  | //
 | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04.
 | ||||||
|  | //	Arilia Wireless Inc.
 | ||||||
| //
 | //
 | ||||||
| 
 |  | ||||||
| #ifndef UCENTRALGW_KAFKA_TOPICS_H | #ifndef UCENTRALGW_KAFKA_TOPICS_H | ||||||
| #define UCENTRALGW_KAFKA_TOPICS_H | #define UCENTRALGW_KAFKA_TOPICS_H | ||||||
| 
 | 
 | ||||||
							
								
								
									
										3665
									
								
								src/framework/MicroService.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3665
									
								
								src/framework/MicroService.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -9,8 +9,6 @@ | |||||||
| #ifndef UCENTRALGW_UCENTRALTYPES_H | #ifndef UCENTRALGW_UCENTRALTYPES_H | ||||||
| #define UCENTRALGW_UCENTRALTYPES_H | #define UCENTRALGW_UCENTRALTYPES_H | ||||||
| 
 | 
 | ||||||
| #include "SubSystemServer.h" |  | ||||||
| 
 |  | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <string> | #include <string> | ||||||
| #include <map> | #include <map> | ||||||
| @@ -29,15 +27,14 @@ namespace OpenWifi::Types { | |||||||
|     typedef std::queue<StringPair>	                        StringPairQueue; |     typedef std::queue<StringPair>	                        StringPairQueue; | ||||||
| 	typedef std::vector<std::string>						StringVec; | 	typedef std::vector<std::string>						StringVec; | ||||||
| 	typedef std::set<std::string>                           StringSet; | 	typedef std::set<std::string>                           StringSet; | ||||||
| 	typedef std::vector<SubSystemServer*>					SubSystemVec; |  | ||||||
| 	typedef std::map<std::string,std::set<std::string>>		StringMapStringSet; | 	typedef std::map<std::string,std::set<std::string>>		StringMapStringSet; | ||||||
| 	typedef std::function<void(std::string, std::string)>   TopicNotifyFunction; | 	typedef std::function<void(std::string, std::string)>   TopicNotifyFunction; | ||||||
| 	typedef std::list<std::pair<TopicNotifyFunction,int>>   TopicNotifyFunctionList; | 	typedef std::list<std::pair<TopicNotifyFunction,int>>   TopicNotifyFunctionList; | ||||||
| 	typedef std::map<std::string, TopicNotifyFunctionList>  NotifyTable; | 	typedef std::map<std::string, TopicNotifyFunctionList>  NotifyTable; | ||||||
|     typedef std::map<std::string,uint64_t>                  CountedMap; |     typedef std::map<std::string,uint64_t>                  CountedMap; | ||||||
| 
 |     typedef std::vector<uint64_t>                           TagList; | ||||||
|     typedef std::string         UUID_t; |     typedef std::string                                     UUID_t; | ||||||
|     typedef std::vector<UUID_t> UUIDvec_t; |     typedef std::vector<UUID_t>                             UUIDvec_t; | ||||||
| 
 | 
 | ||||||
|     inline void UpdateCountedMap(CountedMap &M, const std::string &S, uint64_t Increment=1) { |     inline void UpdateCountedMap(CountedMap &M, const std::string &S, uint64_t Increment=1) { | ||||||
|         auto it = M.find(S); |         auto it = M.find(S); | ||||||
| @@ -50,6 +50,10 @@ namespace OpenWifi::RESTAPI::Errors { | |||||||
|     static const std::string InvalidPassword{"Invalid password."}; |     static const std::string InvalidPassword{"Invalid password."}; | ||||||
|     static const std::string PasswordRejected{"Password was rejected. This maybe an old password."}; |     static const std::string PasswordRejected{"Password was rejected. This maybe an old password."}; | ||||||
|     static const std::string InvalidIPRanges{"Invalid IP range specifications."}; |     static const std::string InvalidIPRanges{"Invalid IP range specifications."}; | ||||||
|  |     static const std::string InvalidLOrderBy{"Invalid orderBy specification."}; | ||||||
|  |     static const std::string NeedMobileNumber{"You must provide at least one validated phone number."}; | ||||||
|  |     static const std::string BadMFAMethod{"MFA only supports sms or email."}; | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif //OWPROV_RESTAPI_ERRORS_H
 | #endif //OWPROV_RESTAPI_ERRORS_H
 | ||||||
| @@ -113,6 +113,7 @@ namespace OpenWifi::RESTAPI::Protocol { | |||||||
| 
 | 
 | ||||||
|     static const char * NEWPASSWORD = "newPassword"; |     static const char * NEWPASSWORD = "newPassword"; | ||||||
|     static const char * USERS = "users"; |     static const char * USERS = "users"; | ||||||
|  |     static const char * WITHEXTENDEDINFO = "withExtendedInfo"; | ||||||
| 
 | 
 | ||||||
|     static const char * ERRORTEXT = "errorText"; |     static const char * ERRORTEXT = "errorText"; | ||||||
|     static const char * ERRORCODE = "errorCode"; |     static const char * ERRORCODE = "errorCode"; | ||||||
| @@ -127,9 +128,12 @@ namespace OpenWifi::RESTAPI::Protocol { | |||||||
|     static const char * ACCESSPOLICY = "accessPolicy"; |     static const char * ACCESSPOLICY = "accessPolicy"; | ||||||
|     static const char * PASSWORDPOLICY = "passwordPolicy"; |     static const char * PASSWORDPOLICY = "passwordPolicy"; | ||||||
|     static const char * FORGOTPASSWORD = "forgotPassword"; |     static const char * FORGOTPASSWORD = "forgotPassword"; | ||||||
|  |     static const char * RESENDMFACODE = "resendMFACode"; | ||||||
|  |     static const char * COMPLETEMFACHALLENGE = "completeMFAChallenge"; | ||||||
|     static const char * ME = "me"; |     static const char * ME = "me"; | ||||||
|     static const char * TELEMETRY = "telemetry"; |     static const char * TELEMETRY = "telemetry"; | ||||||
|     static const char * INTERVAL = "interval"; |     static const char * INTERVAL = "interval"; | ||||||
|  |     static const char * UI = "UI"; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
							
								
								
									
										181
									
								
								src/framework/StorageClass.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/framework/StorageClass.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,181 @@ | |||||||
|  | // | ||||||
|  | // Created by stephane bourque on 2021-10-06. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef OPENWIFI_STORAGE_H | ||||||
|  | #define OPENWIFI_STORAGE_H | ||||||
|  |  | ||||||
|  | #include "Poco/Data/Session.h" | ||||||
|  | #include "Poco/Data/SessionPool.h" | ||||||
|  | #include "Poco/Data/SQLite/Connector.h" | ||||||
|  | #include "Poco/JSON/Object.h" | ||||||
|  |  | ||||||
|  | #ifndef SMALL_BUILD | ||||||
|  | #include "Poco/Data/PostgreSQL/Connector.h" | ||||||
|  | #include "Poco/Data/MySQL/Connector.h" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  |  | ||||||
|  | namespace OpenWifi { | ||||||
|  |     enum DBType { | ||||||
|  |         sqlite, | ||||||
|  |         pgsql, | ||||||
|  |         mysql | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     class StorageClass : public SubSystemServer { | ||||||
|  |     public: | ||||||
|  | /*        static StorageClass *instance() { | ||||||
|  |             if (instance_ == nullptr) { | ||||||
|  |                 instance_ = new StorageClass; | ||||||
|  |             } | ||||||
|  |             return instance_; | ||||||
|  |         } | ||||||
|  | */ | ||||||
|  |         StorageClass() noexcept: | ||||||
|  |             SubSystemServer("StorageClass", "STORAGE-SVR", "storage") | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         int Start() override { | ||||||
|  |             std::lock_guard		Guard(Mutex_); | ||||||
|  |  | ||||||
|  |             Logger_.setLevel(Poco::Message::PRIO_NOTICE); | ||||||
|  |             Logger_.notice("Starting."); | ||||||
|  |             std::string DBType = MicroService::instance().ConfigGetString("storage.type"); | ||||||
|  |  | ||||||
|  |             if (DBType == "sqlite") { | ||||||
|  |                 Setup_SQLite(); | ||||||
|  |             } else if (DBType == "postgresql") { | ||||||
|  |                 Setup_PostgreSQL(); | ||||||
|  |             } else if (DBType == "mysql") { | ||||||
|  |                 Setup_MySQL(); | ||||||
|  |             } | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         void Stop() override { | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||||
|  |             if(dbType_==sqlite) { | ||||||
|  |                 return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; | ||||||
|  |             } else if(dbType_==pgsql) { | ||||||
|  |                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |             } else if(dbType_==mysql) { | ||||||
|  |                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |             } | ||||||
|  |             return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline std::string ConvertParams(const std::string & S) const { | ||||||
|  |             std::string R; | ||||||
|  |             R.reserve(S.size()*2+1); | ||||||
|  |             if(dbType_==pgsql) { | ||||||
|  |                 auto Idx=1; | ||||||
|  |                 for(auto const & i:S) | ||||||
|  |                 { | ||||||
|  |                     if(i=='?') { | ||||||
|  |                         R += '$'; | ||||||
|  |                         R.append(std::to_string(Idx++)); | ||||||
|  |                     } else { | ||||||
|  |                         R += i; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } else { | ||||||
|  |                 R = S; | ||||||
|  |             } | ||||||
|  |             return R; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         inline int Setup_SQLite(); | ||||||
|  |         inline int Setup_MySQL(); | ||||||
|  |         inline int Setup_PostgreSQL(); | ||||||
|  |  | ||||||
|  |     protected: | ||||||
|  |         std::unique_ptr<Poco::Data::SessionPool>        	Pool_; | ||||||
|  |         std::unique_ptr<Poco::Data::SQLite::Connector>  	SQLiteConn_; | ||||||
|  |         std::unique_ptr<Poco::Data::PostgreSQL::Connector>  PostgresConn_; | ||||||
|  |         std::unique_ptr<Poco::Data::MySQL::Connector>       MySQLConn_; | ||||||
|  |         DBType                                              dbType_ = sqlite; | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  | //    inline StorageClass * Storage() { return StorageClass::instance(); } | ||||||
|  |  | ||||||
|  | #ifdef	SMALL_BUILD | ||||||
|  |     int Service::Setup_MySQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||||
|  |     int Service::Setup_PostgreSQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||||
|  | #else | ||||||
|  |  | ||||||
|  |     inline int StorageClass::Setup_SQLite() { | ||||||
|  |         Logger_.notice("SQLite StorageClass enabled."); | ||||||
|  |         dbType_ = sqlite; | ||||||
|  |         auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); | ||||||
|  |         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); | ||||||
|  |         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); | ||||||
|  |         SQLiteConn_ = std::make_unique<Poco::Data::SQLite::Connector>(); | ||||||
|  |         SQLiteConn_->registerConnector(); | ||||||
|  |         Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline int StorageClass::Setup_MySQL() { | ||||||
|  |         Logger_.notice("MySQL StorageClass enabled."); | ||||||
|  |         dbType_ = mysql; | ||||||
|  |         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.mysql.maxsessions", 64); | ||||||
|  |         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.mysql.idletime", 60); | ||||||
|  |         auto Host = MicroService::instance().ConfigGetString("storage.type.mysql.host"); | ||||||
|  |         auto Username = MicroService::instance().ConfigGetString("storage.type.mysql.username"); | ||||||
|  |         auto Password = MicroService::instance().ConfigGetString("storage.type.mysql.password"); | ||||||
|  |         auto Database = MicroService::instance().ConfigGetString("storage.type.mysql.database"); | ||||||
|  |         auto Port = MicroService::instance().ConfigGetString("storage.type.mysql.port"); | ||||||
|  |  | ||||||
|  |         std::string ConnectionStr = | ||||||
|  |                 "host=" + Host + | ||||||
|  |                 ";user=" + Username + | ||||||
|  |                 ";password=" + Password + | ||||||
|  |                 ";db=" + Database + | ||||||
|  |                 ";port=" + Port + | ||||||
|  |                 ";compress=true;auto-reconnect=true"; | ||||||
|  |  | ||||||
|  |         MySQLConn_ = std::make_unique<Poco::Data::MySQL::Connector>(); | ||||||
|  |         MySQLConn_->registerConnector(); | ||||||
|  |         Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline int StorageClass::Setup_PostgreSQL() { | ||||||
|  |         Logger_.notice("PostgreSQL StorageClass enabled."); | ||||||
|  |         dbType_ = pgsql; | ||||||
|  |         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.postgresql.maxsessions", 64); | ||||||
|  |         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.postgresql.idletime", 60); | ||||||
|  |         auto Host = MicroService::instance().ConfigGetString("storage.type.postgresql.host"); | ||||||
|  |         auto Username = MicroService::instance().ConfigGetString("storage.type.postgresql.username"); | ||||||
|  |         auto Password = MicroService::instance().ConfigGetString("storage.type.postgresql.password"); | ||||||
|  |         auto Database = MicroService::instance().ConfigGetString("storage.type.postgresql.database"); | ||||||
|  |         auto Port = MicroService::instance().ConfigGetString("storage.type.postgresql.port"); | ||||||
|  |         auto ConnectionTimeout = MicroService::instance().ConfigGetString("storage.type.postgresql.connectiontimeout"); | ||||||
|  |  | ||||||
|  |         std::string ConnectionStr = | ||||||
|  |                 "host=" + Host + | ||||||
|  |                 " user=" + Username + | ||||||
|  |                 " password=" + Password + | ||||||
|  |                 " dbname=" + Database + | ||||||
|  |                 " port=" + Port + | ||||||
|  |                 " connect_timeout=" + ConnectionTimeout; | ||||||
|  |  | ||||||
|  |         PostgresConn_ = std::make_unique<Poco::Data::PostgreSQL::Connector>(); | ||||||
|  |         PostgresConn_->registerConnector(); | ||||||
|  |         Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); | ||||||
|  |  | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif //OPENWIFI_STORAGE_H | ||||||
							
								
								
									
										758
									
								
								src/framework/orm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										758
									
								
								src/framework/orm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,758 @@ | |||||||
|  | // | ||||||
|  | //	License type: BSD 3-Clause License | ||||||
|  | //	License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE | ||||||
|  | // | ||||||
|  | //	Created by Stephane Bourque on 2021-03-04. | ||||||
|  | //	Arilia Wireless Inc. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | #ifndef __OPENWIFI_ORM_H__ | ||||||
|  | #define __OPENWIFI_ORM_H__ | ||||||
|  |  | ||||||
|  | #include <string> | ||||||
|  | #include <memory> | ||||||
|  | #include <iostream> | ||||||
|  | #include <fstream> | ||||||
|  | #include <map> | ||||||
|  | #include <vector> | ||||||
|  | #include <array> | ||||||
|  |  | ||||||
|  | #include "Poco/Tuple.h" | ||||||
|  | #include "Poco/Data/SessionPool.h" | ||||||
|  | #include "Poco/Data/Statement.h" | ||||||
|  | #include "Poco/Data/RecordSet.h" | ||||||
|  | #include "Poco/Data/SQLite/Connector.h" | ||||||
|  | #include "Poco/Logger.h" | ||||||
|  | #include "Poco/StringTokenizer.h" | ||||||
|  | #include "StorageClass.h" | ||||||
|  |  | ||||||
|  | namespace ORM { | ||||||
|  |  | ||||||
|  |     enum FieldType { | ||||||
|  |         FT_INT, | ||||||
|  |         FT_BIGINT, | ||||||
|  |         FT_TEXT, | ||||||
|  |         FT_VARCHAR, | ||||||
|  |         FT_BLOB | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     enum Indextype { | ||||||
|  |         ASC, | ||||||
|  |         DESC | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     struct Field { | ||||||
|  |         std::string Name; | ||||||
|  |         FieldType   Type; | ||||||
|  |         int         Size=0; | ||||||
|  |         bool        Index=false; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         Field(std::string N, FieldType T, int S=0, bool Index=false) : | ||||||
|  |             Name(std::move(N)), | ||||||
|  |             Type(T), | ||||||
|  |             Size(S), | ||||||
|  |             Index(Index) {} | ||||||
|  |  | ||||||
|  |         explicit Field(std::string N) : | ||||||
|  |             Name(std::move(N)) | ||||||
|  |         { | ||||||
|  |             Type = FT_TEXT; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Field(std::string N, int S) : | ||||||
|  |             Name(std::move(N)), Size(S) | ||||||
|  |         { | ||||||
|  |             if(Size>0 && Size<255) | ||||||
|  |                 Type = FT_VARCHAR; | ||||||
|  |             else | ||||||
|  |                 Type = FT_TEXT; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Field(std::string N, int S, bool I): | ||||||
|  |             Name(std::move(N)), Size(S), Index(I) | ||||||
|  |         { | ||||||
|  |             if(Size>0 && Size<255) | ||||||
|  |                 Type = FT_VARCHAR; | ||||||
|  |             else | ||||||
|  |                 Type = FT_TEXT; | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Field>  FieldVec; | ||||||
|  |  | ||||||
|  |     struct IndexEntry { | ||||||
|  |         std::string     FieldName; | ||||||
|  |         Indextype   Type; | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<IndexEntry>     IndexEntryVec; | ||||||
|  |  | ||||||
|  |     struct Index { | ||||||
|  |         std::string         Name; | ||||||
|  |         IndexEntryVec   Entries; | ||||||
|  |     }; | ||||||
|  |     typedef std::vector<Index>      IndexVec; | ||||||
|  |  | ||||||
|  |     inline std::string FieldTypeToChar(OpenWifi::DBType Type, FieldType T, int Size=0) { | ||||||
|  |         switch(T) { | ||||||
|  |             case FT_INT:    return "INT"; | ||||||
|  |             case FT_BIGINT: return "BIGINT"; | ||||||
|  |             case FT_TEXT:   return "TEXT"; | ||||||
|  |             case FT_VARCHAR: | ||||||
|  |                 if(Size) | ||||||
|  |                     return std::string("VARCHAR(") + std::to_string(Size) + std::string(")"); | ||||||
|  |                 else | ||||||
|  |                     return "TEXT"; | ||||||
|  |                 case FT_BLOB: | ||||||
|  |                     if(Type==OpenWifi::DBType::mysql) | ||||||
|  |                         return "LONGBLOB"; | ||||||
|  |                     else if(Type==OpenWifi::DBType::pgsql) | ||||||
|  |                         return "BYTEA"; | ||||||
|  |                     else if(Type==OpenWifi::DBType::sqlite) | ||||||
|  |                         return "BLOB"; | ||||||
|  |                     default: | ||||||
|  |                         assert(false); | ||||||
|  |                         return ""; | ||||||
|  |         } | ||||||
|  |         assert(false); | ||||||
|  |         return ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline std::string Escape(const std::string &S) { | ||||||
|  |         std::string R; | ||||||
|  |  | ||||||
|  |         for(const auto &i:S) | ||||||
|  |             if(i=='\'') | ||||||
|  |                 R += "''"; | ||||||
|  |             else | ||||||
|  |                 R += i; | ||||||
|  |             return R; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     enum SqlComparison { EQ = 0, NEQ, LT, LTE, GT, GTE }; | ||||||
|  |     enum SqlBinaryOp { AND = 0 , OR }; | ||||||
|  |  | ||||||
|  |     static const std::vector<std::string> BOPS{" and ", " or "}; | ||||||
|  |     static const std::vector<std::string> SQLCOMPS{"=","!=","<","<=",">",">="}; | ||||||
|  |  | ||||||
|  |     inline std::string to_string(uint64_t V) { | ||||||
|  |         return std::to_string(V); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline std::string to_string(int V) { | ||||||
|  |         return std::to_string(V); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline std::string to_string(bool V) { | ||||||
|  |         return std::to_string(V); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline std::string to_string(const std::string &S) { | ||||||
|  |         return S; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     inline std::string to_string(const char * S) { | ||||||
|  |         return S; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     template <typename RecordTuple, typename RecordType> class DB { | ||||||
|  |     public: | ||||||
|  |         DB( OpenWifi::DBType dbtype, | ||||||
|  |             const char *TableName, | ||||||
|  |             const FieldVec & Fields, | ||||||
|  |             const IndexVec & Indexes, | ||||||
|  |             Poco::Data::SessionPool & Pool, | ||||||
|  |                 Poco::Logger &L, | ||||||
|  |                 const char *Prefix): | ||||||
|  |                 Type(dbtype), | ||||||
|  |                 DBName(TableName), | ||||||
|  |                 Pool_(Pool), | ||||||
|  |                 Logger_(L), | ||||||
|  |                 Prefix_(Prefix) | ||||||
|  |         { | ||||||
|  |             bool first = true; | ||||||
|  |             int  Place=0; | ||||||
|  |  | ||||||
|  |             assert( RecordTuple::length == Fields.size()); | ||||||
|  |  | ||||||
|  |             for(const auto &i:Fields) { | ||||||
|  |  | ||||||
|  |                 FieldNames_[i.Name] = Place; | ||||||
|  |                 if(!first) { | ||||||
|  |                     CreateFields_ += ", "; | ||||||
|  |                     SelectFields_ += ", "; | ||||||
|  |                     UpdateFields_ += ", "; | ||||||
|  |                     SelectList_ += ", "; | ||||||
|  |                 } else { | ||||||
|  |                     SelectList_ += "("; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 CreateFields_ += i.Name + " " + FieldTypeToChar(Type, i.Type,i.Size) + (i.Index ? " unique primary key" : ""); | ||||||
|  |                 SelectFields_ += i.Name ; | ||||||
|  |                 UpdateFields_ += i.Name + "=?"; | ||||||
|  |                 SelectList_ += "?"; | ||||||
|  |                 first = false; | ||||||
|  |                 Place++; | ||||||
|  |             } | ||||||
|  |             SelectList_ += ")"; | ||||||
|  |  | ||||||
|  |             if(!Indexes.empty()) { | ||||||
|  |                 if(Type==OpenWifi::DBType::sqlite || Type==OpenWifi::DBType::pgsql) { | ||||||
|  |                     for(const auto &j:Indexes) { | ||||||
|  |                         std::string IndexLine; | ||||||
|  |  | ||||||
|  |                         IndexLine = std::string("CREATE INDEX IF NOT EXISTS ") + j.Name + std::string(" ON ") + DBName + " ("; | ||||||
|  |                         bool first_entry=true; | ||||||
|  |                         for(const auto &k:j.Entries) { | ||||||
|  |                             assert(FieldNames_.find(k.FieldName) != FieldNames_.end()); | ||||||
|  |                             if(!first_entry) { | ||||||
|  |                                 IndexLine += " , "; | ||||||
|  |                             } | ||||||
|  |                             first_entry = false; | ||||||
|  |                             IndexLine += k.FieldName + std::string(" ") + std::string(k.Type == Indextype::ASC ? "ASC" : "DESC") ; | ||||||
|  |                         } | ||||||
|  |                         IndexLine += " );"; | ||||||
|  |                         IndexCreation += IndexLine; | ||||||
|  |                     } | ||||||
|  |                 } else if(Type==OpenWifi::DBType::mysql) { | ||||||
|  |                     bool firstIndex = true; | ||||||
|  |                     std::string IndexLine; | ||||||
|  |                     for(const auto &j:Indexes) { | ||||||
|  |                         if(!firstIndex) | ||||||
|  |                             IndexLine += ", "; | ||||||
|  |                         firstIndex = false; | ||||||
|  |                         IndexLine += " INDEX " + j.Name + " ( " ; | ||||||
|  |                         bool first_entry=true; | ||||||
|  |                         for(const auto &k:j.Entries) { | ||||||
|  |                             assert(FieldNames_.find(k.FieldName) != FieldNames_.end()); | ||||||
|  |                             if(!first_entry) { | ||||||
|  |                                 IndexLine += " ,"; | ||||||
|  |                             } | ||||||
|  |                             first_entry = false; | ||||||
|  |                             IndexLine += k.FieldName + std::string(k.Type == Indextype::ASC ? " ASC" : " DESC"); | ||||||
|  |                         } | ||||||
|  |                         IndexLine += " ) "; | ||||||
|  |                     } | ||||||
|  |                     IndexCreation = IndexLine; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [[nodiscard]] const std::string & CreateFields() const { return CreateFields_; }; | ||||||
|  |         [[nodiscard]] const std::string & SelectFields() const { return SelectFields_; }; | ||||||
|  |         [[nodiscard]] const std::string & SelectList() const { return SelectList_; }; | ||||||
|  |         [[nodiscard]] const std::string & UpdateFields() const { return UpdateFields_; }; | ||||||
|  |  | ||||||
|  |         inline std::string OP(const char *F, SqlComparison O , int V) { | ||||||
|  |             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||||
|  |             return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline std::string OP(const char *F, SqlComparison O , uint64_t V) { | ||||||
|  |             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||||
|  |             return std::string{"("} + F + SQLCOMPS[O] + std::to_string(V) + ")" ; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::string OP(const char *F, SqlComparison O , const std::string & V) { | ||||||
|  |             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||||
|  |             return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::string OP(const char *F, SqlComparison O , const char * V) { | ||||||
|  |             assert( FieldNames_.find(F) != FieldNames_.end() ); | ||||||
|  |             return std::string{"("} + F + SQLCOMPS[O] + "'" + Escape(V) + "')" ; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         static std::string OP( const std::string &P1, SqlBinaryOp BOP , const std::string &P2) { | ||||||
|  |             return std::string("(")+P1 + BOPS[BOP] + P2 +")"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         std::string OP( bool Paran, const std::string &P1, SqlBinaryOp BOP , const std::string &P2) { | ||||||
|  |             return P1 + BOPS[BOP] + P2 +")"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename... Others> std::string OP( bool ParanOpen, const std::string &P1, SqlBinaryOp BOP , const std::string &P2, Others... More) { | ||||||
|  |             return P1 + BOPS[BOP] + OP(ParanOpen, P2, More...) + ")"; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename... Others> std::string OP( const std::string &P1, SqlBinaryOp BOP , const std::string &P2, Others... More) { | ||||||
|  |             return std::string{"("} + P1 + BOPS[BOP] + OP(true, P2, More...); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool  Create() { | ||||||
|  |             std::string S; | ||||||
|  |  | ||||||
|  |             if(Type==OpenWifi::DBType::mysql) { | ||||||
|  |                 if(IndexCreation.empty()) | ||||||
|  |                     S = "create table if not exists " + DBName +" ( " + CreateFields_ + " )" ; | ||||||
|  |                 else | ||||||
|  |                     S = "create table if not exists " + DBName +" ( " + CreateFields_ + " ), " + IndexCreation + " )"; | ||||||
|  |             } else if (Type==OpenWifi::DBType::pgsql || Type==OpenWifi::DBType::sqlite) { | ||||||
|  |                 S = "create table if not exists " + DBName + " ( " + CreateFields_ + " ); " + IndexCreation ; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             // std::cout << "CREATE-DB: " << S << std::endl; | ||||||
|  |  | ||||||
|  |             try { | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   CreateStatement(Session); | ||||||
|  |  | ||||||
|  |                 CreateStatement << S; | ||||||
|  |                 CreateStatement.execute(); | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 std::cout << "Exception while creating DB: " << E.name() << std::endl; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [[nodiscard]] std::string ConvertParams(const std::string & S) const { | ||||||
|  |             if(Type!=OpenWifi::DBType::pgsql) | ||||||
|  |                 return S; | ||||||
|  |  | ||||||
|  |             std::string R; | ||||||
|  |             R.reserve(S.size()*2+1); | ||||||
|  |             auto Idx=1; | ||||||
|  |             for(auto const & i:S) | ||||||
|  |             { | ||||||
|  |                 if(i=='?') { | ||||||
|  |                     R += '$'; | ||||||
|  |                     R.append(std::to_string(Idx++)); | ||||||
|  |                 } else { | ||||||
|  |                     R += i; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return R; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         void Convert( RecordTuple &in , RecordType &out); | ||||||
|  |         void Convert( RecordType &in , RecordTuple &out); | ||||||
|  |  | ||||||
|  |         inline const std::string & Prefix() { return Prefix_; }; | ||||||
|  |  | ||||||
|  |         bool CreateRecord( RecordType & R) { | ||||||
|  |             try { | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Insert(Session); | ||||||
|  |  | ||||||
|  |                 RecordTuple RT; | ||||||
|  |                 Convert(R, RT); | ||||||
|  |                 std::string St = "insert into  " + DBName + " ( " + SelectFields_ + " ) values " + SelectList_; | ||||||
|  |                 Insert  << ConvertParams(St) , | ||||||
|  |                     Poco::Data::Keywords::use(RT); | ||||||
|  |                 Insert.execute(); | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template<typename T> bool GetRecord( const char * FieldName, T Value,  RecordType & R) { | ||||||
|  |             try { | ||||||
|  |  | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Select(Session); | ||||||
|  |                 RecordTuple             RT; | ||||||
|  |  | ||||||
|  |                 std::string St = "select " + SelectFields_ + " from " + DBName + " where " + FieldName + "=?" ; | ||||||
|  |  | ||||||
|  |                 Select  << ConvertParams(St) , | ||||||
|  |                     Poco::Data::Keywords::into(RT), | ||||||
|  |                     Poco::Data::Keywords::use(Value); | ||||||
|  |                 if(Select.execute()==1) { | ||||||
|  |                     Convert(RT,R); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 return false; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         typedef std::vector<std::string> StringVec; | ||||||
|  |  | ||||||
|  |         template <  typename T, | ||||||
|  |                 typename T0, typename T1> bool GR(const char *FieldName, T & R,T0 &V0, T1 &V1) { | ||||||
|  |             try { | ||||||
|  |  | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Select(Session); | ||||||
|  |                 RecordTuple RT; | ||||||
|  |  | ||||||
|  |                 std::string St = "select " + SelectFields_ + " from " + DBName | ||||||
|  |                                 + " where " + FieldName[0] + "=? and " + FieldName[1] + "=?"  ; | ||||||
|  |                 Select  << ConvertParams(St) , | ||||||
|  |                     Poco::Data::Keywords::into(RT), | ||||||
|  |                     Poco::Data::Keywords::use(V0), | ||||||
|  |                     Poco::Data::Keywords::use(V1); | ||||||
|  |  | ||||||
|  |                 if(Select.execute()==1) { | ||||||
|  |                     Convert(RT,R); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         typedef std::vector<RecordTuple> RecordList; | ||||||
|  |  | ||||||
|  |         bool GetRecords( uint64_t Offset, uint64_t HowMany, std::vector<RecordType> & Records, const std::string & Where = "", const std::string & OrderBy = "") { | ||||||
|  |             try { | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Select(Session); | ||||||
|  |                 RecordList RL; | ||||||
|  |                 std::string St = "select " + SelectFields_ + " from " + DBName + | ||||||
|  |                         (Where.empty() ? "" : " where " + Where) + OrderBy + | ||||||
|  |                         ComputeRange(Offset, HowMany) ; | ||||||
|  |  | ||||||
|  |                 Select  << St , | ||||||
|  |                     Poco::Data::Keywords::into(RL); | ||||||
|  |  | ||||||
|  |                 if(Select.execute()>0) { | ||||||
|  |                     for(auto &i:RL) { | ||||||
|  |                         RecordType  R; | ||||||
|  |                         Convert(i, R); | ||||||
|  |                         Records.template emplace_back(R); | ||||||
|  |                     } | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 return false; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename T> bool UpdateRecord( const char *FieldName, T & Value,  RecordType & R) { | ||||||
|  |             try { | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Update(Session); | ||||||
|  |  | ||||||
|  |                 RecordTuple RT; | ||||||
|  |  | ||||||
|  |                 Convert(R, RT); | ||||||
|  |  | ||||||
|  |                 std::string St = "update " + DBName + " set " + UpdateFields_ + " where " + FieldName + "=?" ; | ||||||
|  |                 Update  << ConvertParams(St) , | ||||||
|  |                     Poco::Data::Keywords::use(RT), | ||||||
|  |                     Poco::Data::Keywords::use(Value); | ||||||
|  |                 Update.execute(); | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename T> bool GetNameAndDescription(const char *FieldName, T & Value, std::string & Name, std::string & Description ) { | ||||||
|  |             try { | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Select(Session); | ||||||
|  |                 RecordTuple             RT; | ||||||
|  |  | ||||||
|  |                 std::string St = "select " + SelectFields_ + " from " + DBName + " where " + FieldName + "=?" ; | ||||||
|  |                 RecordType R; | ||||||
|  |                 Select  << ConvertParams(St) , | ||||||
|  |                 Poco::Data::Keywords::into(RT), | ||||||
|  |                 Poco::Data::Keywords::use(Value); | ||||||
|  |                 if(Select.execute()==1) { | ||||||
|  |                     Convert(RT,R); | ||||||
|  |                     Name = R.info.name; | ||||||
|  |                     Description = R.info.description; | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |                 return false; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename T> bool DeleteRecord( const char *FieldName, T Value) { | ||||||
|  |             try { | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Delete(Session); | ||||||
|  |  | ||||||
|  |                 std::string St = "delete from " + DBName + " where " + FieldName + "=?" ; | ||||||
|  |                 Delete  << ConvertParams(St) , | ||||||
|  |                     Poco::Data::Keywords::use(Value); | ||||||
|  |                 Delete.execute(); | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool DeleteRecords( const std::string & WhereClause ) { | ||||||
|  |             try { | ||||||
|  |                 assert( !WhereClause.empty()); | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Delete(Session); | ||||||
|  |  | ||||||
|  |                 std::string St = "delete from " + DBName + " where " + WhereClause; | ||||||
|  |                 Delete  << St; | ||||||
|  |                 Delete.execute(); | ||||||
|  |                 return true; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool Exists(const char *FieldName, std::string & Value) { | ||||||
|  |             try { | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 RecordType  R; | ||||||
|  |                 if(GetRecord(FieldName,Value,R)) | ||||||
|  |                     return true; | ||||||
|  |                 return false; | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool Iterate( std::function<bool(const RecordType &R)> F) { | ||||||
|  |             try { | ||||||
|  |  | ||||||
|  |                 uint64_t    Offset=1; | ||||||
|  |                 uint64_t    Batch=50; | ||||||
|  |                 bool Done=false; | ||||||
|  |                 while(!Done) { | ||||||
|  |                     std::vector<RecordType> Records; | ||||||
|  |                     if(GetRecords(Offset,Batch,Records)) { | ||||||
|  |                         for(const auto &i:Records) { | ||||||
|  |                             if(!F(i)) | ||||||
|  |                                 return true; | ||||||
|  |                         } | ||||||
|  |                         if(Records.size()<Batch) | ||||||
|  |                             return true; | ||||||
|  |                         Offset += Batch; | ||||||
|  |                     } else { | ||||||
|  |                         Done=true; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 return true; | ||||||
|  |             } catch(const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         bool PrepareOrderBy(const std::string &OrderByList, std::string &OrderByString) { | ||||||
|  |             auto items = Poco::StringTokenizer(OrderByList,","); | ||||||
|  |             std::string ItemList; | ||||||
|  |  | ||||||
|  |             for(const auto &i:items) { | ||||||
|  |                 auto T = Poco::StringTokenizer(i,":"); | ||||||
|  |                 if(T.count()!=2) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 if(T[1]!="a" && T[1]!="d") { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 if(!ItemList.empty()) | ||||||
|  |                     ItemList += " , "; | ||||||
|  |                 auto hint = FieldNames_.find(T[0]); | ||||||
|  |                 if(hint==FieldNames_.end()) { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 ItemList += T[0] + (T[1]=="a" ? " ASC" : " DESC"); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             if(!ItemList.empty()) { | ||||||
|  |                 OrderByString = " ORDER BY " + ItemList; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             std::cout << OrderByString << std::endl; | ||||||
|  |  | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         uint64_t Count( const std::string & Where="" ) { | ||||||
|  |             try { | ||||||
|  |                 uint64_t Cnt=0; | ||||||
|  |  | ||||||
|  |                 Poco::Data::Session     Session = Pool_.get(); | ||||||
|  |                 Poco::Data::Statement   Select(Session); | ||||||
|  |  | ||||||
|  |                 std::string st{"SELECT COUNT(*) FROM " + DBName + " " + (Where.empty() ? "" : (" where " + Where)) }; | ||||||
|  |  | ||||||
|  |                 Select << st , | ||||||
|  |                     Poco::Data::Keywords::into(Cnt); | ||||||
|  |                 Select.execute(); | ||||||
|  |  | ||||||
|  |                 return Cnt; | ||||||
|  |  | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         template <typename X> bool ManipulateVectorMember( X T, const char *FieldName, std::string & ParentUUID, std::string & ChildUUID, bool Add) { | ||||||
|  |             try { | ||||||
|  |                 assert( FieldNames_.find(FieldName) != FieldNames_.end() ); | ||||||
|  |  | ||||||
|  |                 RecordType R; | ||||||
|  |                 if(GetRecord(FieldName, ParentUUID, R)) { | ||||||
|  |                     auto it = std::find((R.*T).begin(),(R.*T).end(),ChildUUID); | ||||||
|  |                     if(Add) { | ||||||
|  |                         if(it!=(R.*T).end() && *it == ChildUUID) | ||||||
|  |                             return false; | ||||||
|  |                         (R.*T).push_back(ChildUUID); | ||||||
|  |                         std::sort((R.*T).begin(),(R.*T).end()); | ||||||
|  |                     } else { | ||||||
|  |                         if(it!=(R.*T).end() && *it == ChildUUID) | ||||||
|  |                             (R.*T).erase(it); | ||||||
|  |                         else | ||||||
|  |                             return false; | ||||||
|  |                     } | ||||||
|  |                     UpdateRecord(FieldName, ParentUUID, R); | ||||||
|  |                     return true; | ||||||
|  |                 } | ||||||
|  |             } catch (const Poco::Exception &E) { | ||||||
|  |                 Logger_.log(E); | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteChild( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::children, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteLocation( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::locations, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteContact( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::contacts, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteVenue( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::venues, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteDevice( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::devices, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteEntity( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::entities, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DelUser( const char *FieldName, std::string & ParentUUID, std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::users, FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { | ||||||
|  |             std::string FakeUUID{ Prefix + ":" + ChildUUID}; | ||||||
|  |             return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteInUse(const char *FieldName, std::string & ParentUUID, const std::string & Prefix, const std::string & ChildUUID) { | ||||||
|  |             std::string FakeUUID{ Prefix + ":" + ChildUUID}; | ||||||
|  |             return ManipulateVectorMember(&RecordType::inUse,FieldName, ParentUUID, FakeUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddContact(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::contacts,FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool DeleteLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, false); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool AddLocation(const char *FieldName, std::string & ParentUUID, const std::string & ChildUUID) { | ||||||
|  |             return ManipulateVectorMember(&RecordType::locations,FieldName, ParentUUID, ChildUUID, true); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         inline bool GetInUse(const char *FieldName, std::string & UUID, std::vector<std::string> & UUIDs ) { | ||||||
|  |             RecordType  R; | ||||||
|  |             if(GetRecord(FieldName,UUID,R)) { | ||||||
|  |                 UUIDs = R.inUse; | ||||||
|  |                 return true; | ||||||
|  |             } | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||||
|  |             if(From<1) From=1; | ||||||
|  |             switch(Type) { | ||||||
|  |                 case OpenWifi::DBType::sqlite: | ||||||
|  |                     return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) +  " "; | ||||||
|  |                 case OpenWifi::DBType::pgsql: | ||||||
|  |                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |                 case OpenWifi::DBType::mysql: | ||||||
|  |                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |                 default: | ||||||
|  |                     return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         Poco::Logger & Logger() { return Logger_; } | ||||||
|  |  | ||||||
|  |     private: | ||||||
|  |         OpenWifi::DBType            Type; | ||||||
|  |         std::string                 DBName; | ||||||
|  |         std::string                 CreateFields_; | ||||||
|  |         std::string                 SelectFields_; | ||||||
|  |         std::string                 SelectList_; | ||||||
|  |         std::string                 UpdateFields_; | ||||||
|  |         std::string                 IndexCreation; | ||||||
|  |         std::map<std::string,int>   FieldNames_; | ||||||
|  |         Poco::Data::SessionPool     &Pool_; | ||||||
|  |         Poco::Logger                &Logger_; | ||||||
|  |         std::string                 Prefix_; | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -105,7 +105,7 @@ namespace OpenWifi::uCentralProtocol { | |||||||
| 			ET_TELEMETRY | 			ET_TELEMETRY | ||||||
| 		}; | 		}; | ||||||
| 
 | 
 | ||||||
| 	static EVENT_MSG EventFromString(const std::string & Method) { | 	inline static EVENT_MSG EventFromString(const std::string & Method) { | ||||||
| 		if (!Poco::icompare(Method, CONNECT)) { | 		if (!Poco::icompare(Method, CONNECT)) { | ||||||
| 			return ET_CONNECT; | 			return ET_CONNECT; | ||||||
| 		} else if (!Poco::icompare(Method, STATE)) { | 		} else if (!Poco::icompare(Method, STATE)) { | ||||||
| @@ -6,11 +6,11 @@ | |||||||
| #include <fstream> | #include <fstream> | ||||||
| 
 | 
 | ||||||
| #include "storage_avatar.h" | #include "storage_avatar.h" | ||||||
| #include "StorageService.h" | #include "../StorageService.h" | ||||||
| 
 | 
 | ||||||
| #include "Poco/File.h" | #include "Poco/File.h" | ||||||
| #include "Poco/Data/LOBStream.h" | #include "Poco/Data/LOBStream.h" | ||||||
| #include "Daemon.h" | #include "../Daemon.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
| @@ -3,7 +3,6 @@ | |||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "StorageService.h" | #include "StorageService.h" | ||||||
| #include "Utils.h" |  | ||||||
| #include "storage_users.h" | #include "storage_users.h" | ||||||
| #include "storage_avatar.h" | #include "storage_avatar.h" | ||||||
| 
 | 
 | ||||||
| @@ -1,5 +1,5 @@ | |||||||
| 
 | 
 | ||||||
| #include "StorageService.h" | #include "../StorageService.h" | ||||||
| 
 | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
| @@ -79,7 +79,7 @@ namespace OpenWifi { | |||||||
| 
 | 
 | ||||||
|             uint32_t RevocationDate = 0 ; |             uint32_t RevocationDate = 0 ; | ||||||
| 
 | 
 | ||||||
|             std::string St2{"SELECT Revoked From Tokens WHERE Token=?"}; |             std::string St2{"SELECT RevocationDate From Tokens WHERE Token=?"}; | ||||||
|             Select << ConvertParams(St2), |             Select << ConvertParams(St2), | ||||||
|                 Poco::Data::Keywords::into(RevocationDate), |                 Poco::Data::Keywords::into(RevocationDate), | ||||||
|                 Poco::Data::Keywords::use(Token); |                 Poco::Data::Keywords::use(Token); | ||||||
| @@ -100,7 +100,6 @@ namespace OpenWifi { | |||||||
|             Poco::Data::Session Sess = Pool_->get(); |             Poco::Data::Session Sess = Pool_->get(); | ||||||
|             Poco::Data::Statement Update(Sess); |             Poco::Data::Statement Update(Sess); | ||||||
| 
 | 
 | ||||||
|             uint32_t Revoked = 1 ; |  | ||||||
|             uint64_t Now = std::time(nullptr); |             uint64_t Now = std::time(nullptr); | ||||||
| 
 | 
 | ||||||
|             // update users set lastLogin=? where id=?
 |             // update users set lastLogin=? where id=?
 | ||||||
| @@ -2,13 +2,14 @@ | |||||||
| // Created by stephane bourque on 2021-06-25.
 | // Created by stephane bourque on 2021-06-25.
 | ||||||
| //
 | //
 | ||||||
| 
 | 
 | ||||||
| #include "StorageService.h" | #include <vector> | ||||||
| #include "RESTAPI_utils.h" |  | ||||||
| #include "Daemon.h" |  | ||||||
| 
 | 
 | ||||||
| #include "Poco/Tuple.h" | #include "Poco/Tuple.h" | ||||||
| #include "storage_users.h" | #include "storage_users.h" | ||||||
| 
 | 
 | ||||||
|  | #include "StorageService.h" | ||||||
|  | #include "framework/MicroService.h" | ||||||
|  | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
| 
 | 
 | ||||||
|     bool Convert(const UserInfoRecord &T, SecurityObjects::UserInfo &U) { |     bool Convert(const UserInfoRecord &T, SecurityObjects::UserInfo &U) { | ||||||
| @@ -35,7 +36,7 @@ namespace OpenWifi { | |||||||
|         U.suspended = T.get<20>(); |         U.suspended = T.get<20>(); | ||||||
|         U.blackListed = T.get<21>(); |         U.blackListed = T.get<21>(); | ||||||
|         U.userRole = SecurityObjects::UserTypeFromString(T.get<22>()); |         U.userRole = SecurityObjects::UserTypeFromString(T.get<22>()); | ||||||
|         U.userTypeProprietaryInfo = T.get<23>(); |         U.userTypeProprietaryInfo = RESTAPI_utils::to_object<SecurityObjects::UserLoginLoginExtensions>(T.get<23>()); | ||||||
|         U.securityPolicy = T.get<24>(); |         U.securityPolicy = T.get<24>(); | ||||||
|         U.securityPolicyChange = T.get<25>(); |         U.securityPolicyChange = T.get<25>(); | ||||||
|         U.currentPassword = T.get<26>(); |         U.currentPassword = T.get<26>(); | ||||||
| @@ -69,7 +70,7 @@ namespace OpenWifi { | |||||||
|         T.set<20>(U.suspended); |         T.set<20>(U.suspended); | ||||||
|         T.set<21>(U.blackListed); |         T.set<21>(U.blackListed); | ||||||
|         T.set<22>(SecurityObjects::UserTypeToString(U.userRole)); |         T.set<22>(SecurityObjects::UserTypeToString(U.userRole)); | ||||||
|         T.set<23>(U.userTypeProprietaryInfo); |         T.set<23>(RESTAPI_utils::to_string(U.userTypeProprietaryInfo)); | ||||||
|         T.set<24>(U.securityPolicy); |         T.set<24>(U.securityPolicy); | ||||||
|         T.set<25>(U.securityPolicyChange); |         T.set<25>(U.securityPolicyChange); | ||||||
|         T.set<26>(U.currentPassword); |         T.set<26>(U.currentPassword); | ||||||
| @@ -102,7 +103,7 @@ namespace OpenWifi { | |||||||
|             if(!Records.empty()) |             if(!Records.empty()) | ||||||
|                 return false; |                 return false; | ||||||
| 
 | 
 | ||||||
|             NewUser.Id = Daemon()->CreateUUID(); |             NewUser.Id = MicroService::instance().CreateUUID(); | ||||||
|             NewUser.creationDate = std::time(nullptr); |             NewUser.creationDate = std::time(nullptr); | ||||||
| 
 | 
 | ||||||
|             //  if there is a password, we assume that we do not want email verification,
 |             //  if there is a password, we assume that we do not want email verification,
 | ||||||
| @@ -120,40 +121,16 @@ namespace OpenWifi { | |||||||
|             auto Notes = RESTAPI_utils::to_string(NewUser.notes); |             auto Notes = RESTAPI_utils::to_string(NewUser.notes); | ||||||
|             auto UserType = SecurityObjects::UserTypeToString(NewUser.userRole); |             auto UserType = SecurityObjects::UserTypeToString(NewUser.userRole); | ||||||
|             auto OldPasswords = RESTAPI_utils::to_string(NewUser.lastPasswords); |             auto OldPasswords = RESTAPI_utils::to_string(NewUser.lastPasswords); | ||||||
|  |             auto userTypeProprietaryInfo = RESTAPI_utils::to_string(NewUser.userTypeProprietaryInfo); | ||||||
| 
 | 
 | ||||||
|             St1 = "INSERT INTO Users (" + AllUsersFieldsForSelect + ") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; |             St1 = "INSERT INTO Users (" + AllUsersFieldsForSelect + ") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; | ||||||
|             Poco::Data::Statement Statement(Sess); |             Poco::Data::Statement Statement(Sess); | ||||||
|  | 
 | ||||||
|  |             UserInfoRecord  R; | ||||||
|  |             Convert(NewUser, R); | ||||||
|  | 
 | ||||||
|             Statement << ConvertParams(St1), |             Statement << ConvertParams(St1), | ||||||
|                     Poco::Data::Keywords::use(NewUser.Id), |                     Poco::Data::Keywords::use(R); | ||||||
|                     Poco::Data::Keywords::use(NewUser.name), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.description), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.avatar), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.email), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.validated), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.validationEmail), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.validationDate), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.creationDate), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.validationURI), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.changePassword), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.lastLogin), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.currentLoginURI), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.lastPasswordChange), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.lastEmailCheck), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.waitingForEmailCheck), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.locale), |  | ||||||
|                     Poco::Data::Keywords::use(Notes), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.location), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.owner), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.suspended), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.blackListed), |  | ||||||
|                     Poco::Data::Keywords::use(UserType), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.userTypeProprietaryInfo), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.securityPolicy), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.securityPolicyChange), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.currentPassword), |  | ||||||
|                     Poco::Data::Keywords::use(OldPasswords), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.oauthType), |  | ||||||
|                     Poco::Data::Keywords::use(NewUser.oauthUserInfo); |  | ||||||
|             Statement.execute(); |             Statement.execute(); | ||||||
|             return true; |             return true; | ||||||
| 
 | 
 | ||||||
| @@ -254,37 +231,11 @@ namespace OpenWifi { | |||||||
|             auto Notes = RESTAPI_utils::to_string(UInfo.notes); |             auto Notes = RESTAPI_utils::to_string(UInfo.notes); | ||||||
|             auto UserType = SecurityObjects::UserTypeToString(UInfo.userRole); |             auto UserType = SecurityObjects::UserTypeToString(UInfo.userRole); | ||||||
|             auto OldPasswords = RESTAPI_utils::to_string(UInfo.lastPasswords); |             auto OldPasswords = RESTAPI_utils::to_string(UInfo.lastPasswords); | ||||||
|  |             auto userTypeProprietaryInfo = RESTAPI_utils::to_string(UInfo.userTypeProprietaryInfo); | ||||||
|  |             UserInfoRecord R; | ||||||
|  |             Convert(UInfo, R); | ||||||
|             Update << ConvertParams(St1), |             Update << ConvertParams(St1), | ||||||
|                     Poco::Data::Keywords::use(UInfo.Id), |                     Poco::Data::Keywords::use(R), | ||||||
|                     Poco::Data::Keywords::use(UInfo.name), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.description), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.avatar), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.email), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.validated), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.validationEmail), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.validationDate), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.creationDate), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.validationURI), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.changePassword), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.lastLogin), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.currentLoginURI), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.lastPasswordChange), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.lastEmailCheck), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.waitingForEmailCheck), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.locale), |  | ||||||
|                     Poco::Data::Keywords::use(Notes), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.location), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.owner), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.suspended), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.blackListed), |  | ||||||
|                     Poco::Data::Keywords::use(UserType), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.userTypeProprietaryInfo), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.securityPolicy), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.securityPolicyChange), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.currentPassword), |  | ||||||
|                     Poco::Data::Keywords::use(OldPasswords), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.oauthType), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.oauthUserInfo), |  | ||||||
|                     Poco::Data::Keywords::use(UInfo.Id); |                     Poco::Data::Keywords::use(UInfo.Id); | ||||||
|             Update.execute(); |             Update.execute(); | ||||||
|             return true; |             return true; | ||||||
| @@ -5,6 +5,9 @@ | |||||||
| #ifndef UCENTRALSEC_STORAGE_USERS_H | #ifndef UCENTRALSEC_STORAGE_USERS_H | ||||||
| #define UCENTRALSEC_STORAGE_USERS_H | #define UCENTRALSEC_STORAGE_USERS_H | ||||||
| 
 | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
| namespace OpenWifi { | namespace OpenWifi { | ||||||
|     static const std::string AllUsersFieldsForCreation{ |     static const std::string AllUsersFieldsForCreation{ | ||||||
|         " Id             varchar(36) UNIQUE PRIMARY KEY," |         " Id             varchar(36) UNIQUE PRIMARY KEY," | ||||||
| @@ -116,9 +119,6 @@ namespace OpenWifi { | |||||||
|             "canceled       bigint" |             "canceled       bigint" | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     typedef Poco::Tuple < |     typedef Poco::Tuple < | ||||||
|         std::string,    // Id = 0;
 |         std::string,    // Id = 0;
 | ||||||
|         std::string,    // name;
 |         std::string,    // name;
 | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user