mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
				synced 2025-10-30 18:27:49 +00:00 
			
		
		
		
	Compare commits
	
		
			65 Commits
		
	
	
		
			feature/wi
			...
			v2.4.0-RC5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4510cd034f | ||
|   | af5774ce36 | ||
|   | 2573b8cd4f | ||
|   | 9c5b18a536 | ||
|   | 768c428a67 | ||
|   | 389ceb8b7d | ||
|   | 157f18c117 | ||
|   | 2538f9c768 | ||
|   | 85d998ad76 | ||
|   | a407f2e38d | ||
|   | d0d2be0870 | ||
|   | 78cba562e6 | ||
|   | 350df38c3f | ||
|   | 9e79b73e20 | ||
|   | eb4dfc25f2 | ||
|   | bedec254c5 | ||
|   | 96a566a2b5 | ||
|   | ad2eb1711e | ||
|   | 7244bcb455 | ||
|   | 1db5201418 | ||
|   | 1bc635f553 | ||
|   | 257ac42d7c | ||
|   | acb38e5313 | ||
|   | 7940f0bd85 | ||
|   | 62c06d0bad | ||
|   | 494a199610 | ||
|   | 5307b0b35a | ||
|   | c58728f38e | ||
|   | 1f09c3b619 | ||
|   | d9c6388502 | ||
|   | 5e35906aec | ||
|   | 773618ae07 | ||
|   | cca4441ac7 | ||
|   | 730ca7b292 | ||
|   | 5b4dbb088f | ||
|   | bc11a19ee4 | ||
|   | c835e4d0b9 | ||
|   | f1a2ba90f6 | ||
|   | 5b96ef396f | ||
|   | c204d34bf4 | ||
|   | 4b982bf64b | ||
|   | 37298cc600 | ||
|   | 03619cc900 | ||
|   | f4fc6975e1 | ||
|   | 1f1d596c5a | ||
|   | a5802bf631 | ||
|   | 6471eabc82 | ||
|   | ab6fbaca11 | ||
|   | 1e8e5c18b2 | ||
|   | 3cf23af068 | ||
|   | 1a0b549731 | ||
|   | a835d2e571 | ||
|   | ff7455af24 | ||
|   | 48610bac5d | ||
|   | 7bd5b4d4e6 | ||
|   | e1a51c2a91 | ||
|   | cd0222f765 | ||
|   | 12fddd8bc4 | ||
|   | 9095d831db | ||
|   | 4e8f97df9b | ||
|   | 28808eae93 | ||
|   | 6c24a23863 | ||
|   | 5931c91054 | ||
|   | 9d956c13f7 | ||
|   | ea1adde361 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -18,3 +18,4 @@ _deps | ||||
| *.csr | ||||
| /cmake-build/ | ||||
| /smake-build-debug/ | ||||
| test_scripts/curl/result.json | ||||
|   | ||||
| @@ -30,9 +30,20 @@ else() | ||||
|     file(WRITE build ${BUILD_NUM}) | ||||
| endif() | ||||
|  | ||||
| set(BUILD_SHARED_LIBS 1) | ||||
| find_package(Git QUIET) | ||||
| if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") | ||||
|     execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags | ||||
|             WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | ||||
|             RESULT_VARIABLE GIT_RESULT | ||||
|             OUTPUT_VARIABLE GIT_HASH) | ||||
|     if(NOT GIT_RESULT EQUAL "0") | ||||
|         message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}") | ||||
|     endif() | ||||
|     string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}") | ||||
| endif() | ||||
| add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT) | ||||
|  | ||||
| add_definitions(-DAPP_VERSION="${CMAKE_PROJECT_VERSION}" -DBUILD_NUMBER="${BUILD_NUM}") | ||||
| set(BUILD_SHARED_LIBS 1) | ||||
| add_definitions(-DTIP_SECURITY_SERVICE="1") | ||||
|  | ||||
| set(Boost_USE_STATIC_LIBS OFF) | ||||
| @@ -50,8 +61,11 @@ find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataS | ||||
|  | ||||
| include_directories(/usr/local/include  /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include) | ||||
|  | ||||
| configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY) | ||||
|  | ||||
| add_executable( owsec | ||||
|         build | ||||
|         src/ow_version.h.in | ||||
|         src/framework/CountryCodes.h | ||||
|         src/framework/KafkaTopics.h | ||||
|         src/framework/MicroService.h | ||||
| @@ -90,7 +104,7 @@ add_executable( owsec | ||||
|         src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h | ||||
|         src/storage/storage_tokens.h | ||||
|         src/ActionLinkManager.cpp src/ActionLinkManager.h | ||||
|         ) | ||||
|         src/ACLProcessor.h) | ||||
|  | ||||
| if(NOT SMALL_BUILD) | ||||
|     target_link_libraries(owsec PUBLIC | ||||
|   | ||||
| @@ -53,9 +53,12 @@ RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
|  | ||||
| ADD CMakeLists.txt build /owsec/ | ||||
| ADD cmake /owsec/cmake | ||||
| ADD src /owsec/src | ||||
| ADD .git /owsec/.git | ||||
|  | ||||
|  | ||||
| WORKDIR /owsec | ||||
| RUN mkdir cmake-build | ||||
| @@ -91,6 +94,7 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr | ||||
|     -O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem | ||||
|  | ||||
| COPY readiness_check /readiness_check | ||||
| COPY test_scripts/curl/cli /cli | ||||
|  | ||||
| EXPOSE 16001 17001 16101 | ||||
|  | ||||
|   | ||||
							
								
								
									
										36
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								README.md
									
									
									
									
									
								
							| @@ -98,6 +98,40 @@ to get a sample. The default is | ||||
| ### `authentication.oldpasswords` | ||||
| The number of older passwords to keep. Default is 5. | ||||
|  | ||||
| ### Changing default password | ||||
|  | ||||
| On the first startup of the service new user will be created with the default credentials from properties `authentication.default.username` and `authentication.default.password`, but **you will have to change the password** before making any real requests. | ||||
|  | ||||
| You can this using [owgw-ui](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui/) on first login or using the following script: | ||||
|  | ||||
| ``` | ||||
| export OWSEC=openwifi.wlan.local:16001 # endpoint to your owsec RESTAPI endpoint | ||||
| #export FLAGS="-k" # uncomment and add curl flags that you would like to pass for the request (for example '-k' may be used to pass errors with self-signed certificates) | ||||
| export OWSEC_DEFAULT_USERNAME=root@system.com # default username that you've set in property 'authentication.default.username' | ||||
| export OWSEC_DEFAULT_PASSWORD=weLoveWifi # default password __in cleartext__ from property 'authentication.default.password' | ||||
| export OWSEC_NEW_PASSWORD=NewPass123% # new password that must be set for the user (must comply with 'authentication.validation.expression') | ||||
| test_scripts/curl/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD | ||||
| ``` | ||||
|  | ||||
| CLI is also included in Docker image if you want to run it this way: | ||||
|  | ||||
| ``` | ||||
| export OWSEC=openwifi.wlan.local:16001 | ||||
| #export FLAGS="-k" | ||||
| export OWSEC_DEFAULT_USERNAME=root@system.com | ||||
| export OWSEC_DEFAULT_PASSWORD=weLoveWifi | ||||
| export OWSEC_NEW_PASSWORD=NewPass123% | ||||
| docker run --rm -ti \ | ||||
|   --network=host \ | ||||
|   --env OWSEC \ | ||||
|   --env FLAGS \ | ||||
|   --env OWSEC_DEFAULT_USERNAME \ | ||||
|   --env OWSEC_DEFAULT_PASSWORD \ | ||||
|   --env OWSEC_NEW_PASSWORD \ | ||||
|   tip-tip-wlan-cloud-ucentral.jfrog.io/owsec:main \ | ||||
|   /cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD | ||||
| ``` | ||||
|  | ||||
| ### Kafka integration | ||||
| This security service uses Kafka to coordinate security with other services that are part of the system. You must have a Kafka service running | ||||
| in order to use this. You can find several examples of Kafka services available with Docker. Here are the values you need to configure. | ||||
| @@ -217,4 +251,4 @@ mailer.sender = OpenWIFI | ||||
| mailer.loginmethod = login | ||||
| mailer.port = 587 | ||||
| mailer.templates = $OWSEC_ROOT/templates | ||||
| ``` | ||||
| ``` | ||||
|   | ||||
| @@ -8,7 +8,7 @@ fullnameOverride: "" | ||||
| images: | ||||
|   owsec: | ||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec | ||||
|     tag: main | ||||
|     tag: v2.4.0-RC5 | ||||
|     pullPolicy: Always | ||||
| #    regcred: | ||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||
|   | ||||
| @@ -61,6 +61,8 @@ components: | ||||
|                   - 6     # INTERNAL_ERROR, | ||||
|                   - 7     # ACCESS_DENIED, | ||||
|                   - 8     # INVALID_TOKEN | ||||
|                   - 9     # expired token | ||||
|                   - 10    # rate limit exceeded | ||||
|               ErrorDetails: | ||||
|                 type: string | ||||
|               ErrorDescription: | ||||
|   | ||||
| @@ -40,6 +40,7 @@ openwifi.system.commandchannel = /tmp/app.ucentralsec | ||||
| openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem | ||||
| openwifi.service.key.password = mypassword | ||||
|  | ||||
| smssender.enabled = false | ||||
| smssender.provider = aws | ||||
| smssender.aws.secretkey = *************************************** | ||||
| smssender.aws.accesskey = *************************************** | ||||
| @@ -53,6 +54,7 @@ smssender.aws.region = ************** | ||||
| # | ||||
| # Security Microservice Specific Section | ||||
| # | ||||
| mailer.enabled = false | ||||
| mailer.hostname = smtp.gmail.com | ||||
| mailer.username = ************************ | ||||
| mailer.password = ************************ | ||||
|   | ||||
							
								
								
									
										45
									
								
								src/ACLProcessor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								src/ACLProcessor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-12. | ||||
| // | ||||
|  | ||||
| #ifndef OWSEC_ACLPROCESSOR_H | ||||
| #define OWSEC_ACLPROCESSOR_H | ||||
|  | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class ACLProcessor { | ||||
|     public: | ||||
|         enum ACL_OPS { | ||||
|             READ, | ||||
|             MODIFY, | ||||
|             DELETE, | ||||
|             CREATE | ||||
|         }; | ||||
|         static inline bool Can( const SecurityObjects::UserInfo & User, const SecurityObjects::UserInfo & Target, ACL_OPS Op) { | ||||
|             if(User.Id == Target.Id && Op==DELETE) | ||||
|                 return false; | ||||
|  | ||||
|             if(User.userRole==SecurityObjects::ROOT) | ||||
|                 return true; | ||||
|  | ||||
|             if(User.Id == Target.Id) | ||||
|                 return true; | ||||
|  | ||||
|             if(User.userRole!=SecurityObjects::ADMIN && User.userRole!=SecurityObjects::ROOT && Op!=READ) | ||||
|                 return false; | ||||
|  | ||||
|             if(Target.userRole==SecurityObjects::ROOT && Op!=READ) | ||||
|                 return false; | ||||
|  | ||||
|             return true; | ||||
|         } | ||||
|     private: | ||||
|  | ||||
|     }; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //OWSEC_ACLPROCESSOR_H | ||||
| @@ -18,8 +18,8 @@ namespace OpenWifi { | ||||
|         }; | ||||
|  | ||||
|         static ActionLinkManager * instance() { | ||||
|             static ActionLinkManager instance; | ||||
|             return &instance; | ||||
|             static auto * instance_ = new ActionLinkManager; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         int Start() final; | ||||
|   | ||||
| @@ -56,9 +56,10 @@ namespace OpenWifi { | ||||
| 		Logger_.notice("Stopping..."); | ||||
|     } | ||||
|  | ||||
| 	bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) | ||||
| 	bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ) | ||||
|     { | ||||
|         std::lock_guard	Guard(Mutex_); | ||||
|         Expired = false; | ||||
| 		try { | ||||
| 		    std::string CallToken; | ||||
| 		    Poco::Net::OAuth20Credentials Auth(Request); | ||||
| @@ -67,27 +68,29 @@ namespace OpenWifi { | ||||
| 		    } | ||||
|  | ||||
| 		    if(!CallToken.empty()) { | ||||
| 		        if(StorageService()->IsTokenRevoked(CallToken)) | ||||
| 		            return false; | ||||
| 		        auto Client = UserCache_.find(CallToken); | ||||
| 		        if( Client == UserCache_.end() ) { | ||||
| 		            if(StorageService()->GetToken(SessionToken,UInfo)) { | ||||
| 		                if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { | ||||
| 		                    UserCache_[UInfo.webtoken.access_token_] = UInfo; | ||||
| 		        auto Client = UserCache_.get(CallToken); | ||||
| 		        if( Client.isNull() ) { | ||||
| 		            SecurityObjects::UserInfoAndPolicy UInfo2; | ||||
| 		            uint64_t RevocationDate=0; | ||||
| 		            if(StorageService()->GetToken(CallToken,UInfo2,RevocationDate)) { | ||||
| 		                if(RevocationDate!=0) | ||||
| 		                    return false; | ||||
| 		                Expired = (UInfo2.webtoken.created_ + UInfo2.webtoken.expires_in_) < time(nullptr); | ||||
| 		                if(StorageService()->GetUserById(UInfo2.userinfo.Id,UInfo.userinfo)) { | ||||
| 		                    UInfo.webtoken = UInfo2.webtoken; | ||||
| 		                    UserCache_.update(CallToken, UInfo); | ||||
| 		                    SessionToken = CallToken; | ||||
| 		                    return true; | ||||
| 		                } | ||||
| 		            } | ||||
| 		            return false; | ||||
| 		        } | ||||
|  | ||||
| 		        if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) { | ||||
| 		        if(!Expired) { | ||||
| 		            SessionToken = CallToken; | ||||
| 		            UInfo = Client->second ; | ||||
| 		            UInfo = *Client ; | ||||
| 		            return true; | ||||
| 		        } | ||||
|  | ||||
| 		        UserCache_.erase(Client); | ||||
| 		        StorageService()->RevokeToken(CallToken); | ||||
|                 RevokeToken(CallToken); | ||||
| 		        return false; | ||||
| 		    } | ||||
| 		} catch(const Poco::Exception &E) { | ||||
| @@ -96,16 +99,24 @@ namespace OpenWifi { | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
|     void AuthService::RevokeToken(std::string & Token) { | ||||
|         UserCache_.remove(Token); | ||||
|         StorageService()->RevokeToken(Token); | ||||
|     } | ||||
|  | ||||
|     bool AuthService::DeleteUserFromCache(const std::string &UserName) { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
|         for(auto i=UserCache_.begin();i!=UserCache_.end();) { | ||||
|             if (i->second.userinfo.email==UserName) { | ||||
|                 Logout(i->first, false); | ||||
|                 i = UserCache_.erase(i); | ||||
|             } else { | ||||
|                 ++i; | ||||
|             } | ||||
|         std::vector<std::string>    OldTokens; | ||||
|  | ||||
|         UserCache_.forEach([&OldTokens,UserName](const std::string &token, const SecurityObjects::UserInfoAndPolicy& O) -> void | ||||
|         { if(O.userinfo.email==UserName) | ||||
|             OldTokens.push_back(token); | ||||
|         }); | ||||
|  | ||||
|         for(const auto &i:OldTokens) { | ||||
|             Logout(i,false); | ||||
|             UserCache_.remove(i); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| @@ -121,9 +132,6 @@ namespace OpenWifi { | ||||
|     void AuthService::Logout(const std::string &token, bool EraseFromCache) { | ||||
| 		std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
| 		if(EraseFromCache) | ||||
| 		    UserCache_.erase(token); | ||||
|  | ||||
|         try { | ||||
|             Poco::JSON::Object Obj; | ||||
|             Obj.set("event", "remove-token"); | ||||
| @@ -132,7 +140,7 @@ namespace OpenWifi { | ||||
|             std::stringstream ResultText; | ||||
|             Poco::JSON::Stringifier::stringify(Obj, ResultText); | ||||
|             std::string Tmp{token}; | ||||
|             StorageService()->RevokeToken(Tmp); | ||||
|             RevokeToken(Tmp); | ||||
|             KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(), | ||||
|                                         false); | ||||
|         } catch (const Poco::Exception &E) { | ||||
| @@ -183,9 +191,9 @@ namespace OpenWifi { | ||||
|         UInfo.webtoken.username_ = UserName; | ||||
|         UInfo.webtoken.errorCode = 0; | ||||
|         UInfo.webtoken.userMustChangePassword = false; | ||||
|         UserCache_[UInfo.webtoken.access_token_] = UInfo; | ||||
|         UserCache_.update(UInfo.webtoken.access_token_,UInfo); | ||||
|         StorageService()->SetLastLogin(UInfo.userinfo.Id); | ||||
|         StorageService()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_, | ||||
|         StorageService()->AddToken(UInfo.userinfo.Id, UInfo.webtoken.access_token_, | ||||
|                             UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_, | ||||
|                                 UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_); | ||||
|     } | ||||
| @@ -253,7 +261,7 @@ namespace OpenWifi { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ) | ||||
|     UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired ) | ||||
|     { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
| @@ -291,6 +299,7 @@ namespace OpenWifi { | ||||
|             UInfo.userinfo.lastLogin=std::time(nullptr); | ||||
|             StorageService()->SetLastLogin(UInfo.userinfo.Id); | ||||
|             CreateToken(UserName, UInfo ); | ||||
|  | ||||
|             return SUCCESS; | ||||
|         } | ||||
|  | ||||
| @@ -337,7 +346,7 @@ namespace OpenWifi { | ||||
|  | ||||
|         A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL; | ||||
|         A.userId = UInfo.email; | ||||
|         A.id = MicroService::instance().CreateUUID(); | ||||
|         A.id = MicroService::CreateUUID(); | ||||
|         A.created = std::time(nullptr); | ||||
|         A.expires = A.created + 24*60*60; | ||||
|         StorageService()->CreateAction(A); | ||||
| @@ -345,15 +354,38 @@ namespace OpenWifi { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo) { | ||||
|     bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired) { | ||||
|         std::lock_guard G(Mutex_); | ||||
|         auto It = UserCache_.find(Token); | ||||
|  | ||||
|         if(It==UserCache_.end()) | ||||
|         Expired = false; | ||||
|  | ||||
|         auto Client = UserCache_.get(Token); | ||||
|         if(!Client.isNull()) { | ||||
|             Expired = (Client->webtoken.created_ + Client->webtoken.expires_in_) < std::time(nullptr); | ||||
|             WebToken = Client->webtoken; | ||||
|             UserInfo = Client->userinfo; | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         std::string TToken{Token}; | ||||
|         if(StorageService()->IsTokenRevoked(TToken)) { | ||||
|             return false; | ||||
|         WebToken = It->second.webtoken; | ||||
|         UserInfo = It->second.userinfo; | ||||
|         return true; | ||||
|         } | ||||
|  | ||||
|         //  get the token from disk... | ||||
|         SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|         uint64_t RevocationDate=0; | ||||
|         if(StorageService()->GetToken(TToken, UInfo, RevocationDate)) { | ||||
|             if(RevocationDate!=0) | ||||
|                 return false; | ||||
|             Expired = (UInfo.webtoken.created_ + UInfo.webtoken.expires_in_) < std::time(nullptr); | ||||
|             if(StorageService()->GetUserById(UInfo.userinfo.Id,UInfo.userinfo)) { | ||||
|                 WebToken = UInfo.webtoken; | ||||
|                 UserCache_.update(UInfo.webtoken.access_token_, UInfo); | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -18,6 +18,7 @@ | ||||
| #include "Poco/SHA2Engine.h" | ||||
| #include "Poco/Crypto/DigestEngine.h" | ||||
| #include "Poco/HMACEngine.h" | ||||
| #include "Poco/ExpireLRUCache.h" | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| @@ -44,15 +45,15 @@ namespace OpenWifi{ | ||||
|         static int AccessTypeToInt(ACCESS_TYPE T); | ||||
|  | ||||
|         static AuthService *instance() { | ||||
|             static AuthService instance; | ||||
|             return &instance; | ||||
|             static auto * instance_ = new AuthService; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|  | ||||
|         [[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|         [[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|         [[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired); | ||||
|         [[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ); | ||||
|         void CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
|         [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo); | ||||
|         [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;}; | ||||
| @@ -60,8 +61,7 @@ namespace OpenWifi{ | ||||
|  | ||||
|         bool ValidatePassword(const std::string &pwd); | ||||
|  | ||||
|         [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo); | ||||
|         [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | ||||
|         [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired); | ||||
|         [[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type); | ||||
|         [[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type); | ||||
|  | ||||
| @@ -75,19 +75,21 @@ namespace OpenWifi{ | ||||
|         [[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason); | ||||
|         [[nodiscard]] bool DeleteUserFromCache(const std::string &UserName); | ||||
|         [[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
|         void RevokeToken(std::string & Token); | ||||
|  | ||||
|         [[nodiscard]] static inline const std::string GetLogoAssetURI() { | ||||
|             return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png"; | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] static inline const std::string GetLogoAssetFileName() { | ||||
|             return MicroService::instance().DataDir() + "/wwwassets/the_logo.png"; | ||||
|             return MicroService::instance().WWWAssetsDir() + "/the_logo.png"; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
| 		Poco::JWT::Signer	Signer_; | ||||
| 		Poco::SHA2Engine	SHA2_; | ||||
| 		SecurityObjects::UserInfoCache UserCache_; | ||||
| 		Poco::ExpireLRUCache<std::string,SecurityObjects::UserInfoAndPolicy>    UserCache_{2048,1200000}; | ||||
| 		// SecurityObjects::UserInfoCache UserCache_; | ||||
|         std::string         PasswordValidationStr_; | ||||
| 		std::regex          PasswordValidation_; | ||||
| 		uint64_t            TokenAging_ = 30 * 24 * 60 * 60; | ||||
| @@ -119,8 +121,8 @@ namespace OpenWifi{ | ||||
|  | ||||
|     inline AuthService * AuthService() { return AuthService::instance(); } | ||||
|  | ||||
|     [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) { | ||||
|         return AuthService()->IsAuthorized(Request, SessionToken, UInfo ); | ||||
|     [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired) { | ||||
|         return AuthService()->IsAuthorized(Request, SessionToken, UInfo, Expired ); | ||||
|     } | ||||
|  | ||||
| } // end of namespace | ||||
|   | ||||
| @@ -26,7 +26,7 @@ namespace OpenWifi { | ||||
|             return false; | ||||
|  | ||||
|         std::string Challenge = MakeChallenge(); | ||||
|         std::string uuid = MicroService::instance().CreateUUID(); | ||||
|         std::string uuid = MicroService::CreateUUID(); | ||||
|         uint64_t Created = std::time(nullptr); | ||||
|  | ||||
|         ChallengeStart.set("uuid",uuid); | ||||
| @@ -71,8 +71,9 @@ namespace OpenWifi { | ||||
|  | ||||
|         auto uuid = ChallengeResponse->get("uuid").toString(); | ||||
|         auto Hint = Cache_.find(uuid); | ||||
|         if(Hint == end(Cache_)) | ||||
|         if(Hint == end(Cache_)) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         auto answer = ChallengeResponse->get("answer").toString(); | ||||
|         if(Hint->second.Answer!=answer) { | ||||
|   | ||||
| @@ -24,8 +24,8 @@ namespace OpenWifi { | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         static MFAServer *instance() { | ||||
|             static MFAServer instance; | ||||
|             return &instance; | ||||
|             static auto * instance_ = new MFAServer; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge); | ||||
| @@ -35,7 +35,7 @@ namespace OpenWifi { | ||||
|         static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); | ||||
|  | ||||
|         static inline std::string MakeChallenge() { | ||||
|             return std::to_string(rand() % 999999); | ||||
|             return std::to_string(MicroService::instance().Random(1,999999)); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|   | ||||
| @@ -20,7 +20,7 @@ namespace OpenWifi { | ||||
|                                         Server, | ||||
|                                         Internal, | ||||
|                                         false, | ||||
|                                         true, RateLimit{.Interval=1000,.MaxCalls=5}) {} | ||||
|                                         true, RateLimit{.Interval=1000,.MaxCalls=10}) {} | ||||
|         static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; }; | ||||
|         void RequestResetPassword(SecurityObjects::ActionLink &Link); | ||||
|         void CompleteResetPassword(); | ||||
|   | ||||
| @@ -17,24 +17,39 @@ | ||||
| #include "StorageService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static void FilterCredentials(SecurityObjects::UserInfo & U) { | ||||
|         U.currentPassword.clear(); | ||||
|         U.lastPasswords.clear(); | ||||
|         U.oauthType.clear(); | ||||
|     } | ||||
|  | ||||
| 	void RESTAPI_oauth2Handler::DoGet() { | ||||
|         if (!IsAuthorized()) { | ||||
| 	    bool Expired = false; | ||||
|         if (!IsAuthorized(Expired)) { | ||||
|             if(Expired) | ||||
|                 return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN); | ||||
|             return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation); | ||||
|         } | ||||
|         bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false); | ||||
|         if(GetMe) { | ||||
|             Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); | ||||
|             Poco::JSON::Object Me; | ||||
|             UserInfo_.userinfo.to_json(Me); | ||||
|             SecurityObjects::UserInfo   ReturnedUser = UserInfo_.userinfo; | ||||
|             FilterCredentials(ReturnedUser); | ||||
|             ReturnedUser.to_json(Me); | ||||
|             return ReturnObject(Me); | ||||
|         } | ||||
|         BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_oauth2Handler::DoDelete() { | ||||
|         if (!IsAuthorized()) { | ||||
|             return UnAuthorized("Not authorized."); | ||||
|         } | ||||
| 	    bool Expired = false; | ||||
| 	    if (!IsAuthorized(Expired)) { | ||||
| 	        if(Expired) | ||||
| 	            return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN); | ||||
| 	        return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation); | ||||
| 	    } | ||||
|  | ||||
|         auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "..."); | ||||
|         if (Token == SessionToken_) { | ||||
| @@ -71,7 +86,7 @@ namespace OpenWifi { | ||||
|                 SecurityObjects::ActionLink NewLink; | ||||
|  | ||||
|                 NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; | ||||
|                 NewLink.id = MicroService::instance().CreateUUID(); | ||||
|                 NewLink.id = MicroService::CreateUUID(); | ||||
|                 NewLink.userId = UInfo1.Id; | ||||
|                 NewLink.created = std::time(nullptr); | ||||
|                 NewLink.expires = NewLink.created + (24*60*60); | ||||
| @@ -93,8 +108,8 @@ namespace OpenWifi { | ||||
|  | ||||
|         if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) { | ||||
|             Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|             if(Obj->has(RESTAPI::Protocol::UUID)) { | ||||
|                 auto uuid = Obj->get(RESTAPI::Protocol::UUID).toString(); | ||||
|             if(Obj->has("uuid")) { | ||||
|                 auto uuid = Obj->get("uuid").toString(); | ||||
|                 if(MFAServer().ResendCode(uuid)) | ||||
|                     return OK(); | ||||
|             } | ||||
| @@ -103,7 +118,7 @@ namespace OpenWifi { | ||||
|  | ||||
|         if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) { | ||||
|             Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|             if(Obj->has(RESTAPI::Protocol::UUID)) { | ||||
|             if(Obj->has("uuid")) { | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) { | ||||
|                     Poco::JSON::Object ReturnObj; | ||||
| @@ -115,7 +130,8 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo); | ||||
|         bool Expired=false; | ||||
|         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo, Expired); | ||||
|         if (Code==SUCCESS) { | ||||
|             Poco::JSON::Object ReturnObj; | ||||
|             if(AuthService()->RequiresMFA(UInfo)) { | ||||
|   | ||||
| @@ -21,7 +21,7 @@ namespace OpenWifi { | ||||
|                                                       Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 													  Server, | ||||
| 													  Internal, false, true , RateLimit{.Interval=2000,.MaxCalls=5}) {} | ||||
| 													  Internal, false, true , RateLimit{.Interval=1000,.MaxCalls=10}) {} | ||||
| 		static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; }; | ||||
| 		void DoGet() final; | ||||
| 		void DoPost() final; | ||||
|   | ||||
| @@ -7,8 +7,16 @@ | ||||
| #include "Poco/JSON/Parser.h" | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| #include "SMSSender.h" | ||||
| #include "ACLProcessor.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static void FilterCredentials(SecurityObjects::UserInfo & U) { | ||||
|         U.currentPassword.clear(); | ||||
|         U.lastPasswords.clear(); | ||||
|         U.oauthType.clear(); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_user_handler::DoGet() { | ||||
|         std::string Id = GetBinding("id", ""); | ||||
|         if(Id.empty()) { | ||||
| @@ -27,9 +35,7 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         Poco::JSON::Object  UserInfoObject; | ||||
|         UInfo.currentPassword.clear(); | ||||
|         UInfo.lastPasswords.clear(); | ||||
|         UInfo.oauthType.clear(); | ||||
|         FilterCredentials(UInfo); | ||||
|         UInfo.to_json(UserInfoObject); | ||||
|         ReturnObject(UserInfoObject); | ||||
|     } | ||||
| @@ -40,20 +46,12 @@ namespace OpenWifi { | ||||
|             return BadRequest(RESTAPI::Errors::MissingUserID); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!= SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.Id == Id) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfo UInfo; | ||||
|         if(!StorageService()->GetUserById(Id,UInfo)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         if(UInfo.userRole==SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) { | ||||
|         if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::DELETE)) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
| @@ -64,6 +62,9 @@ namespace OpenWifi { | ||||
|         if(AuthService()->DeleteUserFromCache(UInfo.email)) { | ||||
|             // nothing to do | ||||
|         } | ||||
|  | ||||
|         StorageService()->DeleteAvatar(UserInfo_.userinfo.email,Id); | ||||
|  | ||||
|         Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email)); | ||||
|         StorageService()->RevokeAllTokens(UInfo.email); | ||||
|         Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email)); | ||||
| @@ -76,57 +77,52 @@ namespace OpenWifi { | ||||
|             return BadRequest(RESTAPI::Errors::IdMustBe0); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfo   UInfo; | ||||
|         RESTAPI_utils::from_request(UInfo,*Request); | ||||
|         SecurityObjects::UserInfo   NewUser; | ||||
|         RESTAPI_utils::from_request(NewUser,*Request); | ||||
|  | ||||
|         if(UInfo.userRole == SecurityObjects::UNKNOWN) { | ||||
|         if(NewUser.userRole == SecurityObjects::UNKNOWN) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) { | ||||
|         if(!ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) { | ||||
|             return UnAuthorized("Insufficient access rights.", ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && UInfo.userRole == SecurityObjects::ROOT) { | ||||
|             return UnAuthorized("Insufficient access rights.", ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         Poco::toLowerInPlace(UInfo.email); | ||||
|         if(!Utils::ValidEMailAddress(UInfo.email)) { | ||||
|         Poco::toLowerInPlace(NewUser.email); | ||||
|         if(!Utils::ValidEMailAddress(NewUser.email)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||
|         } | ||||
|  | ||||
|         if(!UInfo.currentPassword.empty()) { | ||||
|             if(!AuthService()->ValidatePassword(UInfo.currentPassword)) { | ||||
|         if(!NewUser.currentPassword.empty()) { | ||||
|             if(!AuthService()->ValidatePassword(NewUser.currentPassword)) { | ||||
|                 return BadRequest(RESTAPI::Errors::InvalidPassword); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(UInfo.name.empty()) | ||||
|             UInfo.name = UInfo.email; | ||||
|         if(NewUser.name.empty()) | ||||
|             NewUser.name = NewUser.email; | ||||
|  | ||||
|         if(!StorageService()->CreateUser(UInfo.email,UInfo)) { | ||||
|             Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email)); | ||||
|         if(!StorageService()->CreateUser(NewUser.email,NewUser)) { | ||||
|             Logger_.information(Poco::format("Could not add user '%s'.",NewUser.email)); | ||||
|             return BadRequest(RESTAPI::Errors::RecordNotCreated); | ||||
|         } | ||||
|  | ||||
|         if(GetParameter("email_verification","false")=="true") { | ||||
|             if(AuthService::VerifyEmail(UInfo)) | ||||
|                 Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email)); | ||||
|             StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo); | ||||
|             if(AuthService::VerifyEmail(NewUser)) | ||||
|                 Logger_.information(Poco::format("Verification e-mail requested for %s",NewUser.email)); | ||||
|             StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,NewUser.Id,NewUser); | ||||
|         } | ||||
|  | ||||
|         if(!StorageService()->GetUserByEmail(UInfo.email, UInfo)) { | ||||
|             Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email)); | ||||
|         if(!StorageService()->GetUserByEmail(NewUser.email, NewUser)) { | ||||
|             Logger_.information(Poco::format("User '%s' but not retrieved.",NewUser.email)); | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         Poco::JSON::Object  UserInfoObject; | ||||
|         UInfo.to_json(UserInfoObject); | ||||
|  | ||||
|         FilterCredentials(NewUser); | ||||
|         NewUser.to_json(UserInfoObject); | ||||
|         ReturnObject(UserInfoObject); | ||||
|  | ||||
|         Logger_.information(Poco::format("User '%s' has been added by '%s')",UInfo.email, UserInfo_.userinfo.email)); | ||||
|         Logger_.information(Poco::format("User '%s' has been added by '%s')",NewUser.email, UserInfo_.userinfo.email)); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_user_handler::DoPut() { | ||||
| @@ -140,12 +136,8 @@ namespace OpenWifi { | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && Existing.userRole == SecurityObjects::ROOT) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         if(!ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) { | ||||
|             return UnAuthorized("Insufficient access rights.", ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfo   NewUser; | ||||
| @@ -224,15 +216,23 @@ namespace OpenWifi { | ||||
|                 return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|             } | ||||
|  | ||||
|             if(NewUser.userTypeProprietaryInfo.mfa.method=="email") { | ||||
|             if(!NewUser.userTypeProprietaryInfo.mfa.method.empty()) { | ||||
|                 if(NewUser.userTypeProprietaryInfo.mfa.method!="email" && NewUser.userTypeProprietaryInfo.mfa.method!="sms" ) { | ||||
|                     return BadRequest("Unknown MFA method"); | ||||
|                 } | ||||
|                 Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; | ||||
|             } | ||||
|  | ||||
|             if(Existing.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mfa.method.empty()) { | ||||
|                 return BadRequest("Illegal MFA method"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { | ||||
|             SecurityObjects::UserInfo   NewUserInfo; | ||||
|             StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); | ||||
|             Poco::JSON::Object  ModifiedObject; | ||||
|             FilterCredentials(NewUserInfo); | ||||
|             NewUserInfo.to_json(ModifiedObject); | ||||
|             return ReturnObject(ModifiedObject); | ||||
|         } | ||||
|   | ||||
| @@ -13,7 +13,8 @@ namespace OpenWifi { | ||||
|             if (i.first == "token") { | ||||
|                 //  can we find this token? | ||||
|                 SecurityObjects::UserInfoAndPolicy SecObj; | ||||
|                 if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo)) { | ||||
|                 bool Expired = false; | ||||
|                 if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo, Expired)) { | ||||
|                     Poco::JSON::Object Obj; | ||||
|                     SecObj.to_json(Obj); | ||||
|                     return ReturnObject(Obj); | ||||
|   | ||||
| @@ -562,7 +562,7 @@ namespace OpenWifi::ProvObjects { | ||||
|         } | ||||
|         I.notes = N; | ||||
|         I.modified = I.created = Now; | ||||
|         I.id = MicroService::instance().CreateUUID(); | ||||
|         I.id = MicroService::CreateUUID(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|   | ||||
| @@ -59,15 +59,15 @@ namespace OpenWifi::SecurityObjects { | ||||
|  | ||||
| 	struct MobilePhoneNumber { | ||||
| 	    std::string number; | ||||
| 	    bool verified; | ||||
| 	    bool primary; | ||||
| 	    bool verified = false; | ||||
| 	    bool primary = false; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct MfaAuthInfo { | ||||
| 	    bool enabled; | ||||
| 	    bool enabled = false; | ||||
| 	    std::string method; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| @@ -86,7 +86,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    std::string uuid; | ||||
| 	    std::string question; | ||||
| 	    std::string method; | ||||
| 	    uint64_t    created; | ||||
| 	    uint64_t    created = std::time(nullptr); | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
|   | ||||
| @@ -16,13 +16,16 @@ | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     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_ = MicroService::instance().ConfigGetBool("smssender.enabled",false); | ||||
|         if(Enabled_) { | ||||
|             Provider_ = MicroService::instance().ConfigGetString("smssender.provider","aws"); | ||||
|             if(Provider_=="aws") { | ||||
|                 ProviderImpl_ = std::make_unique<SMS_provider_aws>(Logger_); | ||||
|             } else if(Provider_=="twilio") { | ||||
|                 ProviderImpl_ = std::make_unique<SMS_provider_twilio>(Logger_); | ||||
|             } | ||||
|             Enabled_ = ProviderImpl_->Initialize(); | ||||
|         } | ||||
|         Enabled_ = ProviderImpl_->Initialize(); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -18,15 +18,15 @@ namespace OpenWifi { | ||||
|         std::string Number; | ||||
|         std::string Code; | ||||
|         std::string UserName; | ||||
|         uint64_t    Created; | ||||
|         bool        Validated=false; | ||||
|         uint64_t    Created = std::time(nullptr); | ||||
|         bool        Validated = false; | ||||
|     }; | ||||
|  | ||||
|     class SMSSender : public SubSystemServer { | ||||
|         public: | ||||
|             static SMSSender *instance() { | ||||
|                 static SMSSender instance; | ||||
|                 return &instance; | ||||
|                 static auto *instance_ = new SMSSender; | ||||
|                 return instance_; | ||||
|             } | ||||
|  | ||||
|             int  Start() final; | ||||
| @@ -37,8 +37,8 @@ namespace OpenWifi { | ||||
|             bool IsNumberValid(const std::string &Number, const std::string &UserName); | ||||
|             [[nodiscard]] bool Send(const std::string &PhoneNumber, const std::string &Message); | ||||
|         private: | ||||
|             std::string         Provider_; | ||||
|             bool                Enabled_=false; | ||||
|             std::string                             Provider_; | ||||
|             bool                                    Enabled_=false; | ||||
|             std::vector<SMSValidationCacheEntry>    Cache_; | ||||
|             std::unique_ptr<SMS_provider>           ProviderImpl_; | ||||
|  | ||||
|   | ||||
| @@ -43,18 +43,24 @@ namespace OpenWifi { | ||||
|         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()); | ||||
|         try { | ||||
|             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; | ||||
|         } catch (...) { | ||||
|  | ||||
|         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)); | ||||
|         Logger_.debug(Poco::format("SMS NOT sent to %s: failure in SMS service",PhoneNumber)); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -9,12 +9,9 @@ | ||||
| #include "Poco/Net/SMTPClientSession.h" | ||||
| #include "Poco/Net/SecureSMTPClientSession.h" | ||||
| #include "Poco/Net/StringPartSource.h" | ||||
| #include "Poco/Path.h" | ||||
| #include "Poco/Exception.h" | ||||
| #include "Poco/Net/SSLManager.h" | ||||
| #include "Poco/Net/Context.h" | ||||
| #include "Poco/Net/InvalidCertificateHandler.h" | ||||
| #include "Poco/Net/AcceptCertificateHandler.h" | ||||
|  | ||||
| #include "SMTPMailerService.h" | ||||
| #include "framework/MicroService.h" | ||||
| @@ -23,14 +20,19 @@ | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void SMTPMailerService::LoadMyConfig() { | ||||
|         MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); | ||||
|         SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); | ||||
|         SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password"); | ||||
|         Sender_ = MicroService::instance().ConfigGetString("mailer.sender"); | ||||
|         LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod"); | ||||
|         MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port"); | ||||
|         TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); | ||||
|         Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); | ||||
|         Enabled_ = MicroService::instance().ConfigGetBool("mailer.enabled",false); | ||||
|         if(Enabled_) { | ||||
|             MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); | ||||
|             SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); | ||||
|             SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password"); | ||||
|             Sender_ = MicroService::instance().ConfigGetString("mailer.sender"); | ||||
|             LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod"); | ||||
|             MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port"); | ||||
|             TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); | ||||
|             MailRetry_ = (int) MicroService::instance().ConfigGetInt("mailer.retry",2*60); | ||||
|             MailAbandon_ = (int) MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60); | ||||
|             Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     int SMTPMailerService::Start() { | ||||
| @@ -53,57 +55,46 @@ namespace OpenWifi { | ||||
|  | ||||
|     bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) { | ||||
|         std::lock_guard G(Mutex_); | ||||
|  | ||||
|         /* | ||||
|         uint64_t Now = std::time(nullptr); | ||||
|         std::string RecipientLower = Poco::toLower(Recipient); | ||||
|         auto CE = Cache_.find(RecipientLower); | ||||
|         if(CE!=Cache_.end()) { | ||||
|             // only allow messages to the same user within 2 minutes | ||||
|             if(!((CE->second.LastRequest-Now)<30 && CE->second.HowManyRequests<10)) | ||||
|                 return false; | ||||
|             if(CE->second.LastRequest-Now>30) { | ||||
|                 CE->second.LastRequest = Now; | ||||
|                 CE->second.HowManyRequests=0; | ||||
|             } else { | ||||
|                 CE->second.HowManyRequests++; | ||||
|             } | ||||
|         } else { | ||||
|             Cache_[RecipientLower] = MessageCacheEntry{.LastRequest=Now, .HowManyRequests=0}; | ||||
|         } | ||||
| */ | ||||
|         Messages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr), | ||||
|         PendingMessages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr), | ||||
|                                             .LastTry=0, | ||||
|                                             .Sent=0, | ||||
|                                             .File=Poco::File(TemplateDir_ + "/" +Name), | ||||
|                                             .Attrs=Attrs}); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     void SMTPMailerService::run() { | ||||
|  | ||||
|         Running_ = true; | ||||
|         while(Running_) { | ||||
|  | ||||
|             Poco::Thread::trySleep(10000); | ||||
|             if(!Running_) | ||||
|                 break; | ||||
|  | ||||
|             { | ||||
|                 std::lock_guard G(Mutex_); | ||||
|                 Messages_.splice(Messages_.end(),PendingMessages_); | ||||
|             } | ||||
|  | ||||
|             for(auto i=Messages_.begin();i!=Messages_.end();) { | ||||
|                 if(!Running_) | ||||
|                     break; | ||||
|                 auto Recipient = i->Attrs.find(RECIPIENT_EMAIL)->second; | ||||
|                 uint64_t Now = std::time(nullptr); | ||||
|  | ||||
|                 for(auto &i:Messages_) { | ||||
|                     if(i.Sent==0 && (i.LastTry==0 || (Now-i.LastTry)>120)) { | ||||
|                         if (SendIt(i)) { | ||||
|                             i.LastTry = i.Sent = std::time(nullptr); | ||||
|                         } else | ||||
|                             i.LastTry = std::time(nullptr); | ||||
|                 if((i->LastTry==0 || (Now-i->LastTry)>MailRetry_)) { | ||||
|                     if (SendIt(*i)) { | ||||
|                         Logger_.information(Poco::format("Attempting to deliver for mail '%s'.", Recipient)); | ||||
|                         i = Messages_.erase(i); | ||||
|                     } else { | ||||
|                         i->LastTry = Now; | ||||
|                         ++i; | ||||
|                     } | ||||
|                 } else if ((Now-i->Posted)>MailAbandon_) { | ||||
|                     Logger_.information(Poco::format("Mail for '%s' has timed out and will not be sent.", Recipient)); | ||||
|                     i = Messages_.erase(i); | ||||
|                 } else { | ||||
|                     ++i; | ||||
|                 } | ||||
|  | ||||
|                 //  Clean the list | ||||
|                 std::remove_if(Messages_.begin(),Messages_.end(),[Now](MessageEvent &E){ return (E.Sent!=0 || ((Now-E.LastTry)>(15*60)));}); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -115,10 +106,12 @@ namespace OpenWifi { | ||||
|     } | ||||
|  | ||||
|     bool SMTPMailerService::SendIt(const MessageEvent &Msg) { | ||||
|         std::string             Recipient; | ||||
|  | ||||
|         try | ||||
|         { | ||||
|             Poco::Net::MailMessage  Message; | ||||
|             std::string             Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; | ||||
|             Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; | ||||
|  | ||||
|             auto H1 = Msg.Attrs.find(SENDER); | ||||
|             std::string TheSender; | ||||
| @@ -129,7 +122,6 @@ namespace OpenWifi { | ||||
|             } | ||||
|             Message.setSender( TheSender ); | ||||
|             Logger_.information(Poco::format("Sending message to:%s from %s",Recipient,TheSender)); | ||||
|  | ||||
|             Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); | ||||
|             Message.setSubject(Msg.Attrs.find(SUBJECT)->second); | ||||
|  | ||||
| @@ -146,21 +138,26 @@ namespace OpenWifi { | ||||
|  | ||||
|             auto Logo = Msg.Attrs.find(LOGO); | ||||
|             if(Logo!=Msg.Attrs.end()) { | ||||
|                 Poco::File  LogoFile(AuthService::GetLogoAssetFileName()); | ||||
|                 std::ifstream   IF(LogoFile.path()); | ||||
|                 std::ostringstream   OS; | ||||
|                 Poco::StreamCopier::copyStream(IF, OS); | ||||
|                 Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/jpeg")); | ||||
|                 try { | ||||
|                     Poco::File          LogoFile(AuthService::GetLogoAssetFileName()); | ||||
|                     std::ifstream       IF(LogoFile.path()); | ||||
|                     std::ostringstream  OS; | ||||
|                     Poco::StreamCopier::copyStream(IF, OS); | ||||
|                     Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png")); | ||||
|                 } catch (...) { | ||||
|                     Logger_.warning(Poco::format("Cannot add '%s' logo in email",AuthService::GetLogoAssetFileName())); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             Poco::SharedPtr<Poco::Net::AcceptCertificateHandler>  ptrHandler_ = new Poco::Net::AcceptCertificateHandler(false); | ||||
|  | ||||
|             Poco::Net::SecureSMTPClientSession session(MailHost_,MailHostPort_); | ||||
|             Poco::Net::Context::Params P; | ||||
|             auto ptrContext = Poco::AutoPtr<Poco::Net::Context> | ||||
|                     (new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", | ||||
|                                                             Poco::Net::Context::VERIFY_RELAXED, 9, true, | ||||
|                                                             "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH")); | ||||
|             Poco::Net::SSLManager::instance().initializeClient(nullptr, | ||||
|                                                                &ptrHandler_, | ||||
|                                                                ptrHandler_, | ||||
|                                                                ptrContext); | ||||
|             session.login(); | ||||
|             session.startTLS(ptrContext); | ||||
| @@ -177,6 +174,9 @@ namespace OpenWifi { | ||||
|         { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         catch (const std::exception &E) { | ||||
|             Logger_.warning(Poco::format("Cannot send message to:%s, error: %s",Recipient, E.what())); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -59,8 +59,8 @@ namespace OpenWifi { | ||||
|     class SMTPMailerService : public SubSystemServer, Poco::Runnable { | ||||
|         public: | ||||
|            static SMTPMailerService *instance() { | ||||
|                static SMTPMailerService     instance; | ||||
|                return & instance; | ||||
|                static auto * instance_ = new SMTPMailerService; | ||||
|                return instance_; | ||||
|             } | ||||
|  | ||||
|             struct MessageEvent { | ||||
| @@ -71,41 +71,35 @@ namespace OpenWifi { | ||||
|                MessageAttributes    Attrs; | ||||
|             }; | ||||
|  | ||||
|             struct MessageCacheEntry { | ||||
|                uint64_t         LastRequest=0; | ||||
|                uint64_t         HowManyRequests=0; | ||||
|             }; | ||||
|  | ||||
|             void run() override; | ||||
|  | ||||
|             int Start() override; | ||||
|             void Stop() override; | ||||
|  | ||||
|             bool SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs); | ||||
|             bool SendIt(const MessageEvent &Msg); | ||||
|             void LoadMyConfig(); | ||||
|             void reinitialize(Poco::Util::Application &self) override; | ||||
|             bool Enabled() const { return Enabled_; } | ||||
|  | ||||
|         private: | ||||
|             std::string             MailHost_; | ||||
|             std::string             Sender_; | ||||
|             int                     MailHostPort_=25; | ||||
|             int                     MailRetry_=2*60; | ||||
|             int                     MailAbandon_=2*60*20; | ||||
|             std::string             SenderLoginUserName_; | ||||
|             std::string             SenderLoginPassword_; | ||||
|             std::string             LoginMethod_ = "login"; | ||||
|             std::string             LogoFileName_; | ||||
|             std::string             TemplateDir_; | ||||
|             std::list<MessageEvent> Messages_; | ||||
|             std::map<std::string,MessageCacheEntry> Cache_; | ||||
|             std::list<MessageEvent> PendingMessages_; | ||||
|             Poco::Thread            SenderThr_; | ||||
|             std::atomic_bool        Running_=false; | ||||
|             bool                    Enabled_=false; | ||||
|             Poco::Net::AcceptCertificateHandler  ptrHandler_; | ||||
|  | ||||
|             SMTPMailerService() noexcept: | ||||
|                 SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer"), | ||||
|                 ptrHandler_(false) | ||||
|                 SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer") | ||||
|             { | ||||
|                 std::string E{"SHA512"}; | ||||
|             } | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -16,12 +16,28 @@ namespace OpenWifi { | ||||
| 		StorageClass::Start(); | ||||
| 		Create_Tables(); | ||||
| 		InitializeDefaultUser(); | ||||
|  | ||||
| 		Archivercallback_ = std::make_unique<Poco::TimerCallback<Archiver>>(Archiver_,&Archiver::onTimer); | ||||
| 		Timer_.setStartInterval( 5 * 60 * 1000);  // first run in 5 minutes | ||||
| 		Timer_.setPeriodicInterval(1 * 60 * 60 * 1000); // 1 hours | ||||
| 		Timer_.start(*Archivercallback_); | ||||
|  | ||||
| 		return 0; | ||||
|     } | ||||
|  | ||||
|     void Storage::Stop() { | ||||
|         Logger_.notice("Stopping."); | ||||
|         Timer_.stop(); | ||||
|         StorageClass::Stop(); | ||||
|     } | ||||
|  | ||||
|     void Archiver::onTimer(Poco::Timer &timer) { | ||||
|         Poco::Logger &logger = Poco::Logger::get("STORAGE-ARCHIVER"); | ||||
|         logger.information("Squiggy the DB: removing old tokens."); | ||||
|         StorageService()->CleanExpiredTokens(); | ||||
|         logger.information("Squiggy the DB: removing old actionLinks."); | ||||
|         StorageService()->CleanOldActionLinks(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| // namespace | ||||
| @@ -13,6 +13,8 @@ | ||||
| #include "framework/StorageClass.h" | ||||
| #include "AuthService.h" | ||||
|  | ||||
| #include "Poco/Timer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static const std::string AllEmailTemplatesFieldsForCreation { | ||||
| @@ -27,6 +29,12 @@ namespace OpenWifi { | ||||
|  | ||||
|     }; | ||||
|  | ||||
|     class Archiver { | ||||
|     public: | ||||
|         void onTimer(Poco::Timer & timer); | ||||
|     private: | ||||
|     }; | ||||
|  | ||||
|     class Storage : public StorageClass { | ||||
|     public: | ||||
|  | ||||
| @@ -76,8 +84,8 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         static Storage *instance() { | ||||
|             static Storage instance; | ||||
|             return &instance; | ||||
|             static auto * instance_ = new Storage; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         int 	Start() override; | ||||
| @@ -104,12 +112,12 @@ namespace OpenWifi { | ||||
|         bool GetAvatar(const std::string & Admin, std::string &Id, Poco::TemporaryFile &FileName, std::string &Type, std::string & Name); | ||||
|         bool DeleteAvatar(const std::string & Admin, std::string &Id); | ||||
|  | ||||
|         bool AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut); | ||||
|         bool AddToken(std::string &UserId, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut); | ||||
|         bool RevokeToken( std::string & Token ); | ||||
|         bool IsTokenRevoked( std::string & Token ); | ||||
|         bool CleanRevokedTokens( uint64_t Oldest ); | ||||
|         bool CleanExpiredTokens(); | ||||
|         bool RevokeAllTokens( std::string & UserName ); | ||||
|         bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
|         bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate); | ||||
|  | ||||
|         /* | ||||
|          *  All ActionLinks functions | ||||
| @@ -121,6 +129,7 @@ namespace OpenWifi { | ||||
|         bool SentAction(std::string &ActionId); | ||||
|         bool GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A); | ||||
|         bool GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max=200); | ||||
|         void CleanOldActionLinks(); | ||||
|  | ||||
| 	  private: | ||||
|         int Create_Tables(); | ||||
| @@ -128,6 +137,13 @@ namespace OpenWifi { | ||||
|         int Create_AvatarTable(); | ||||
|         int Create_TokensTable(); | ||||
|         int Create_ActionLinkTable(); | ||||
|  | ||||
|         Poco::Timer                     Timer_; | ||||
|         Archiver                        Archiver_; | ||||
|         std::unique_ptr<Poco::TimerCallback<Archiver>>   Archivercallback_; | ||||
|  | ||||
|         /// This is to support a mistake that was deployed... | ||||
|         void ReplaceOldDefaultUUID(); | ||||
|    }; | ||||
|  | ||||
|     inline Storage * StorageService() { return Storage::instance(); }; | ||||
|   | ||||
							
								
								
									
										2479
									
								
								src/framework/ConfigurationValidator.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2479
									
								
								src/framework/ConfigurationValidator.cpp
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										46
									
								
								src/framework/ConfigurationValidator.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/framework/ConfigurationValidator.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-09-14. | ||||
| // | ||||
|  | ||||
| #ifndef OWPROV_CONFIGURATIONVALIDATOR_H | ||||
| #define OWPROV_CONFIGURATIONVALIDATOR_H | ||||
|  | ||||
| #include <nlohmann/json-schema.hpp> | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| using nlohmann::json; | ||||
| using nlohmann::json_schema::json_validator; | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class ConfigurationValidator : public  SubSystemServer { | ||||
|     public: | ||||
|  | ||||
|         static ConfigurationValidator *instance() { | ||||
|             if(instance_== nullptr) | ||||
|                 instance_ = new ConfigurationValidator; | ||||
|             return instance_; | ||||
|         } | ||||
|  | ||||
|         bool Validate(const std::string &C, std::string &Error); | ||||
|         static void my_format_checker(const std::string &format, const std::string &value); | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         void reinitialize(Poco::Util::Application &self) override; | ||||
|  | ||||
|     private: | ||||
|         static  ConfigurationValidator * instance_; | ||||
|         bool            Initialized_=false; | ||||
|         bool            Working_=false; | ||||
|         void            Init(); | ||||
|         std::unique_ptr<json_validator>  Validator_=std::make_unique<json_validator>(nullptr, my_format_checker); | ||||
|  | ||||
|         ConfigurationValidator(): | ||||
|             SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") { | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     inline ConfigurationValidator * ConfigurationValidator() { return ConfigurationValidator::instance(); } | ||||
|     inline bool ValidateUCentralConfiguration(const std::string &C, std::string &Error) { return ConfigurationValidator::instance()->Validate(C, Error); } | ||||
| } | ||||
|  | ||||
| #endif //OWPROV_CONFIGURATIONVALIDATOR_H | ||||
| @@ -69,6 +69,8 @@ using namespace std::chrono_literals; | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "nlohmann/json.hpp" | ||||
|  | ||||
| #include "ow_version.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     enum UNAUTHORIZED_REASON { | ||||
| @@ -80,7 +82,9 @@ namespace OpenWifi { | ||||
|         PASSWORD_INVALID, | ||||
|         INTERNAL_ERROR, | ||||
|         ACCESS_DENIED, | ||||
|         INVALID_TOKEN | ||||
|         INVALID_TOKEN, | ||||
|         EXPIRED_TOKEN, | ||||
|         RATE_LIMIT_EXCEEDED | ||||
|     }; | ||||
|  | ||||
| 	class AppServiceRegistry { | ||||
| @@ -88,8 +92,8 @@ namespace OpenWifi { | ||||
| 		inline AppServiceRegistry(); | ||||
|  | ||||
| 		static AppServiceRegistry & instance() { | ||||
| 			static AppServiceRegistry instance; | ||||
| 			return instance; | ||||
| 		    static AppServiceRegistry *instance_= new AppServiceRegistry; | ||||
| 			return *instance_; | ||||
| 		} | ||||
|  | ||||
| 		inline ~AppServiceRegistry() { | ||||
| @@ -1433,8 +1437,8 @@ namespace OpenWifi { | ||||
| 	    }; | ||||
|  | ||||
| 	    static RESTAPI_RateLimiter *instance() { | ||||
| 	        static RESTAPI_RateLimiter instance; | ||||
| 	        return &instance; | ||||
| 	        static RESTAPI_RateLimiter * instance_ = new RESTAPI_RateLimiter; | ||||
| 	        return instance_; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() final { return 0;}; | ||||
| @@ -1444,18 +1448,18 @@ namespace OpenWifi { | ||||
| 	        Poco::URI   uri(R.getURI()); | ||||
| 	        auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); | ||||
| 	        auto E = Cache_.get(H); | ||||
| 	        const auto p1 = std::chrono::system_clock::now(); | ||||
| 	        auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(p1.time_since_epoch()).count(); | ||||
| 	        auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); | ||||
| 	        if(E.isNull()) { | ||||
| 	            Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1}); | ||||
| 	            Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString())); | ||||
| 	            return false; | ||||
| 	        } | ||||
| 	        if((Now-E->Start)<Period) { | ||||
| 	            E->Count++; | ||||
| 	            Cache_.update(H,E); | ||||
| 	            if(E->Count > MaxCalls) | ||||
| 	            if(E->Count > MaxCalls) { | ||||
| 	                Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString())); | ||||
| 	                return true; | ||||
| 	            } | ||||
| 	            return false; | ||||
| 	        } | ||||
| 	        E->Start = Now; | ||||
| @@ -1523,20 +1527,23 @@ namespace OpenWifi { | ||||
| 	            Request = &RequestIn; | ||||
| 	            Response = &ResponseIn; | ||||
|  | ||||
| 	            if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) | ||||
| 	                return; | ||||
| 	            if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) { | ||||
| 	                return UnAuthorized("Rate limit exceeded.",RATE_LIMIT_EXCEEDED); | ||||
| 	            } | ||||
|  | ||||
| 	            if (!ContinueProcessing()) | ||||
| 	                return; | ||||
|  | ||||
| 	            if (AlwaysAuthorize_ && !IsAuthorized()) { | ||||
| 	                return; | ||||
| 	            bool Expired=false; | ||||
| 	            if (AlwaysAuthorize_ && !IsAuthorized(Expired)) { | ||||
| 	                if(Expired) | ||||
| 	                    return UnAuthorized(RESTAPI::Errors::ExpiredToken, EXPIRED_TOKEN); | ||||
| 	                return UnAuthorized(RESTAPI::Errors::InvalidCredentials, ACCESS_DENIED); | ||||
| 	            } | ||||
|  | ||||
| 	            std::string Reason; | ||||
| 	            if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { | ||||
|                     UnAuthorized(Reason, ACCESS_DENIED); | ||||
|                     return; | ||||
|                     return UnAuthorized(Reason, ACCESS_DENIED); | ||||
| 	            } | ||||
|  | ||||
| 	            ParseParameters(); | ||||
| @@ -1874,7 +1881,7 @@ namespace OpenWifi { | ||||
| 	        return true; | ||||
| 	    } | ||||
|  | ||||
| 	    inline bool IsAuthorized(); | ||||
| 	    inline bool IsAuthorized(bool & Expired); | ||||
|  | ||||
| 	        inline void ReturnObject(Poco::JSON::Object &Object) { | ||||
| 	            PrepareResponse(); | ||||
| @@ -1895,7 +1902,7 @@ namespace OpenWifi { | ||||
| 	            QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, ""); | ||||
| 	            QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0); | ||||
| 	            QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0); | ||||
| 	            QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 1); | ||||
| 	            QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0); | ||||
| 	            QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); | ||||
| 	            QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); | ||||
| 	            QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, ""); | ||||
| @@ -1907,7 +1914,7 @@ namespace OpenWifi { | ||||
| 	            QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false); | ||||
|  | ||||
| 	            if(QB_.Offset<1) | ||||
| 	                QB_.Offset=1; | ||||
| 	                QB_.Offset=0; | ||||
| 	            return true; | ||||
| 	        } | ||||
|  | ||||
| @@ -2076,6 +2083,50 @@ namespace OpenWifi { | ||||
| 	    Poco::JSON::Object      Body_; | ||||
| 	}; | ||||
|  | ||||
|     class KafkaProducer : public Poco::Runnable { | ||||
|     public: | ||||
|         inline void run(); | ||||
|         void Start() { | ||||
|             if(!Running_) { | ||||
|                 Running_=true; | ||||
|                 Worker_.start(*this); | ||||
|             } | ||||
|         } | ||||
|         void Stop() { | ||||
|             if(Running_) { | ||||
|                 Running_=false; | ||||
|                 Worker_.wakeUp(); | ||||
|                 Worker_.join(); | ||||
|             } | ||||
|         } | ||||
|     private: | ||||
|         std::mutex          Mutex_; | ||||
|         Poco::Thread        Worker_; | ||||
|         std::atomic_bool    Running_=false; | ||||
|     }; | ||||
|  | ||||
|     class KafkaConsumer : public Poco::Runnable { | ||||
|     public: | ||||
|         inline void run(); | ||||
|         void Start() { | ||||
|             if(!Running_) { | ||||
|                 Running_=true; | ||||
|                 Worker_.start(*this); | ||||
|             } | ||||
|         } | ||||
|         void Stop() { | ||||
|             if(Running_) { | ||||
|                 Running_=false; | ||||
|                 Worker_.wakeUp(); | ||||
|                 Worker_.join(); | ||||
|             } | ||||
|         } | ||||
|     private: | ||||
|         std::mutex          Mutex_; | ||||
|         Poco::Thread        Worker_; | ||||
|         std::atomic_bool    Running_=false; | ||||
|     }; | ||||
|  | ||||
| 	class KafkaManager : public SubSystemServer { | ||||
| 	public: | ||||
| 	    struct KMessage { | ||||
| @@ -2084,33 +2135,32 @@ namespace OpenWifi { | ||||
| 	        PayLoad; | ||||
| 	    }; | ||||
|  | ||||
| 	    friend class KafkaConsumer; | ||||
| 	    friend class KafkaProducer; | ||||
|  | ||||
| 	    inline void initialize(Poco::Util::Application & self) override; | ||||
|  | ||||
| 	    static KafkaManager *instance() { | ||||
| 	        static KafkaManager instance; | ||||
| 	        return &instance; | ||||
| 	        static KafkaManager * instance_ = new KafkaManager; | ||||
| 	        return instance_; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override { | ||||
| 	        if(!KafkaEnabled_) | ||||
| 	            return 0; | ||||
| 	        ProducerThr_ = std::make_unique<std::thread>([this]() { this->ProducerThr(); }); | ||||
| 	        ConsumerThr_ = std::make_unique<std::thread>([this]() { this->ConsumerThr(); }); | ||||
| 	        ConsumerThr_.Start(); | ||||
| 	        ProducerThr_.Start(); | ||||
| 	        return 0; | ||||
| 	    } | ||||
|  | ||||
| 	    inline void Stop() override { | ||||
| 	        if(KafkaEnabled_) { | ||||
| 	            ProducerRunning_ = ConsumerRunning_ = false; | ||||
| 	            ProducerThr_->join(); | ||||
| 	            ConsumerThr_->join(); | ||||
| 	            ProducerThr_.Stop(); | ||||
| 	            ConsumerThr_.Stop(); | ||||
| 	            return; | ||||
| 	        } | ||||
| 	    } | ||||
|  | ||||
| 	    inline void ProducerThr(); | ||||
| 	    inline void ConsumerThr(); | ||||
|  | ||||
| 	    inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true  ) { | ||||
| 	        if(KafkaEnabled_) { | ||||
| 	            std::lock_guard G(Mutex_); | ||||
| @@ -2163,18 +2213,13 @@ namespace OpenWifi { | ||||
| 	    // void WakeUp(); | ||||
|  | ||||
| 	private: | ||||
| 	    std::mutex 						ProducerMutex_; | ||||
| 	    std::mutex						ConsumerMutex_; | ||||
| 	    bool 							KafkaEnabled_ = false; | ||||
| 	    std::atomic_bool 				ProducerRunning_ = false; | ||||
| 	    std::atomic_bool 				ConsumerRunning_ = false; | ||||
| 	    std::queue<KMessage>			Queue_; | ||||
| 	    std::string 					SystemInfoWrapper_; | ||||
| 	    std::unique_ptr<std::thread>	ConsumerThr_; | ||||
| 	    std::unique_ptr<std::thread>	ProducerThr_; | ||||
| 	    int                       		FunctionId_=1; | ||||
| 	    Types::NotifyTable        		Notifiers_; | ||||
| 	    std::unique_ptr<cppkafka::Configuration>    Config_; | ||||
| 	    KafkaProducer                   ProducerThr_; | ||||
| 	    KafkaConsumer                   ConsumerThr_; | ||||
|  | ||||
| 	    inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { | ||||
| 	        Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); | ||||
| @@ -2199,8 +2244,8 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	    static AuthClient *instance() { | ||||
| 	        static AuthClient instance; | ||||
| 	        return &instance; | ||||
| 	        static AuthClient * instance_ = new AuthClient; | ||||
| 	        return instance_; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override { | ||||
| @@ -2208,25 +2253,20 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	    inline void Stop() override { | ||||
| 	        Cache_.clear(); | ||||
| 	    } | ||||
|  | ||||
| 	    inline void RemovedCachedToken(const std::string &Token) { | ||||
| 	        std::lock_guard	G(Mutex_); | ||||
| 	        UserCache_.erase(Token); | ||||
| 	        Cache_.remove(Token); | ||||
| 	    } | ||||
|  | ||||
| 	    inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { | ||||
| 	        return ((T.expires_in_+T.created_)<std::time(nullptr)); | ||||
| 	    } | ||||
|  | ||||
| 	    inline bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) { | ||||
| 	        std::lock_guard G(Mutex_); | ||||
|  | ||||
| 	        auto User = UserCache_.find(SessionToken); | ||||
| 	        if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { | ||||
| 	            UInfo = User->second; | ||||
| 	            return true; | ||||
| 	        } else { | ||||
| 	    inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) { | ||||
| 	        try { | ||||
| 	            Types::StringPairVec QueryData; | ||||
| 	            QueryData.push_back(std::make_pair("token",SessionToken)); | ||||
| 	            OpenAPIRequestGet	Req(    uSERVICE_SECURITY, | ||||
| @@ -2236,49 +2276,39 @@ namespace OpenWifi { | ||||
| 	            Poco::JSON::Object::Ptr Response; | ||||
| 	            if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { | ||||
| 	                if(Response->has("tokenInfo") && Response->has("userInfo")) { | ||||
| 	                    SecurityObjects::UserInfoAndPolicy	P; | ||||
| 	                    P.from_json(Response); | ||||
| 	                    UserCache_[SessionToken] = P; | ||||
| 	                    UInfo = P; | ||||
| 	                    UInfo.from_json(Response); | ||||
| 	                    if(IsTokenExpired(UInfo.webtoken)) { | ||||
| 	                        Expired = true; | ||||
| 	                        return false; | ||||
| 	                    } | ||||
| 	                    Expired = false; | ||||
| 	                    Cache_.update(SessionToken, UInfo); | ||||
| 	                    return true; | ||||
| 	                } | ||||
| 	                return true; | ||||
| 	            } | ||||
| 	        } catch (...) { | ||||
|  | ||||
| 	        } | ||||
| 	        Expired = false; | ||||
| 	        return false; | ||||
| 	    } | ||||
|  | ||||
| 	    inline bool IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) { | ||||
| 	        std::lock_guard G(Mutex_); | ||||
|  | ||||
| 	        auto User = UserCache_.find(SessionToken); | ||||
| 	        if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) { | ||||
| 	            UInfo = User->second; | ||||
| 	            return true; | ||||
| 	        } else { | ||||
| 	            Types::StringPairVec QueryData; | ||||
| 	            QueryData.push_back(std::make_pair("token",SessionToken)); | ||||
| 	            OpenAPIRequestGet	Req(uSERVICE_SECURITY, | ||||
|                                          "/api/v1/validateToken", | ||||
|                                          QueryData, | ||||
|                                          5000); | ||||
| 	            Poco::JSON::Object::Ptr Response; | ||||
| 	            if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { | ||||
| 	                if(Response->has("tokenInfo") && Response->has("userInfo")) { | ||||
| 	                    SecurityObjects::UserInfoAndPolicy	P; | ||||
| 	                    P.from_json(Response); | ||||
| 	                    UserCache_[SessionToken] = P; | ||||
| 	                    UInfo = P; | ||||
| 	                } | ||||
| 	                return true; | ||||
|         inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) { | ||||
| 	        auto User = Cache_.get(SessionToken); | ||||
| 	        if(!User.isNull()) { | ||||
| 	            if(IsTokenExpired(User->webtoken)) { | ||||
| 	                Expired = true; | ||||
| 	                return false; | ||||
| 	            } | ||||
|  | ||||
| 	            Expired = false; | ||||
|                 UInfo = *User; | ||||
|                 return true; | ||||
| 	        } | ||||
| 	        return false; | ||||
| 	        return RetrieveTokenInformation(SessionToken, UInfo, Expired); | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    OpenWifi::SecurityObjects::UserInfoCache 		UserCache_; | ||||
| 	    Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy>      Cache_{1024,1200000 }; | ||||
| 	}; | ||||
|  | ||||
| 	inline AuthClient * AuthClient() { return AuthClient::instance(); } | ||||
| @@ -2338,14 +2368,14 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	    static ALBHealthCheckServer *instance() { | ||||
| 	        static ALBHealthCheckServer instance; | ||||
| 	        return &instance; | ||||
| 	        static ALBHealthCheckServer * instance = new ALBHealthCheckServer; | ||||
| 	        return instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override; | ||||
|  | ||||
| 	    inline void Stop() override { | ||||
| 	        if(Server_) | ||||
| 	        if(Running_) | ||||
| 	            Server_->stop(); | ||||
| 	    } | ||||
|  | ||||
| @@ -2353,6 +2383,7 @@ namespace OpenWifi { | ||||
| 	    std::unique_ptr<Poco::Net::HTTPServer>   	Server_; | ||||
| 	    std::unique_ptr<Poco::Net::ServerSocket> 	Socket_; | ||||
| 	    int                                     	Port_ = 0; | ||||
| 	    std::atomic_bool                            Running_=false; | ||||
| 	}; | ||||
|  | ||||
| 	inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } | ||||
| @@ -2364,23 +2395,24 @@ namespace OpenWifi { | ||||
|                                                             Poco::Logger & L, RESTAPI_GenericServer & S); | ||||
|  | ||||
|  | ||||
| 	class RESTAPI_server : public SubSystemServer { | ||||
| 	class RESTAPI_ExtServer : public SubSystemServer { | ||||
| 	public: | ||||
| 	    static RESTAPI_server *instance() { | ||||
| 	        static RESTAPI_server instance; | ||||
| 	        return &instance; | ||||
| 	    static RESTAPI_ExtServer *instance() { | ||||
| 	        static RESTAPI_ExtServer *instance_ = new RESTAPI_ExtServer; | ||||
| 	        return instance_; | ||||
| 	    } | ||||
| 	    int Start() override; | ||||
| 	    inline void Stop() override { | ||||
| 	        Logger_.information("Stopping "); | ||||
| 	        for( const auto & svr : RESTServers_ ) | ||||
| 	            svr->stop(); | ||||
| 	        Pool_.joinAll(); | ||||
| 	        RESTServers_.clear(); | ||||
|  | ||||
| 	    } | ||||
|  | ||||
| 	    inline void reinitialize(Poco::Util::Application &self) override; | ||||
|  | ||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) { | ||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path, uint64_t Id) { | ||||
| 	        RESTAPIHandler::BindingMap Bindings; | ||||
| 	        return RESTAPI_external_server(Path, Bindings, Logger_, Server_); | ||||
| 	    } | ||||
| @@ -2390,19 +2422,19 @@ namespace OpenWifi { | ||||
| 	    Poco::ThreadPool	    Pool_; | ||||
| 	    RESTAPI_GenericServer   Server_; | ||||
|  | ||||
| 	    RESTAPI_server() noexcept: | ||||
| 	    RESTAPI_ExtServer() noexcept: | ||||
| 	    SubSystemServer("RESTAPIServer", "RESTAPIServer", "openwifi.restapi") | ||||
|             { | ||||
|             } | ||||
| 	}; | ||||
|  | ||||
| 	inline RESTAPI_server * RESTAPI_server() { return RESTAPI_server::instance(); }; | ||||
| 	inline RESTAPI_ExtServer * RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); }; | ||||
|  | ||||
| 	class RequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	public: | ||||
| 	    RequestHandlerFactory(RESTAPI_GenericServer & Server) : | ||||
| 	    Logger_(RESTAPI_server::instance()->Logger()), | ||||
| 	    Server_(Server) | ||||
| 	  	explicit ExtRequestHandlerFactory(RESTAPI_GenericServer & Server) : | ||||
| 			Logger_(RESTAPI_ExtServer::instance()->Logger()), | ||||
| 			Server_(Server) | ||||
| 	    { | ||||
|  | ||||
| 	    } | ||||
| @@ -2410,15 +2442,16 @@ namespace OpenWifi { | ||||
| 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | ||||
| 	        Poco::URI uri(Request.getURI()); | ||||
| 	        auto *Path = uri.getPath().c_str(); | ||||
| 	        return RESTAPI_server()->CallServer(Path); | ||||
| 	        return RESTAPI_ExtServer()->CallServer(Path, TransactionId_++); | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    Poco::Logger            &Logger_; | ||||
| 	    RESTAPI_GenericServer   &Server_; | ||||
| 		static inline std::atomic_uint64_t  TransactionId_ = 1; | ||||
| 	    Poco::Logger            			&Logger_; | ||||
| 	    RESTAPI_GenericServer   			&Server_; | ||||
| 	}; | ||||
|  | ||||
| 	inline int RESTAPI_server::Start() { | ||||
| 	inline int RESTAPI_ExtServer::Start() { | ||||
| 	    Logger_.information("Starting."); | ||||
| 	    Server_.InitLogging(); | ||||
|  | ||||
| @@ -2432,12 +2465,12 @@ namespace OpenWifi { | ||||
| 	        if(!Svr.RootCA().empty()) | ||||
| 	            Svr.LogCas(Logger_); | ||||
|  | ||||
| 	        auto Params = new Poco::Net::HTTPServerParams; | ||||
| 	        Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; | ||||
| 	        Params->setMaxThreads(50); | ||||
| 	        Params->setMaxQueued(200); | ||||
| 	        Params->setKeepAlive(true); | ||||
|  | ||||
| 	        auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new RequestHandlerFactory(Server_), Pool_, Sock, Params); | ||||
| 	        auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory(Server_), Pool_, Sock, Params); | ||||
| 	        NewServer->start(); | ||||
| 	        RESTServers_.push_back(std::move(NewServer)); | ||||
| 	    } | ||||
| @@ -2445,12 +2478,12 @@ namespace OpenWifi { | ||||
| 	    return 0; | ||||
| 	} | ||||
|  | ||||
| 	class RESTAPI_InternalServer : public SubSystemServer { | ||||
| 	class RESTAPI_IntServer : public SubSystemServer { | ||||
|  | ||||
| 	public: | ||||
| 	    static RESTAPI_InternalServer *instance() { | ||||
| 	        static RESTAPI_InternalServer instance; | ||||
| 	        return &instance; | ||||
| 	    static RESTAPI_IntServer *instance() { | ||||
| 	        static RESTAPI_IntServer *instance_ = new RESTAPI_IntServer; | ||||
| 	        return instance_; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override; | ||||
| @@ -2458,12 +2491,13 @@ namespace OpenWifi { | ||||
| 	        Logger_.information("Stopping "); | ||||
| 	        for( const auto & svr : RESTServers_ ) | ||||
| 	            svr->stop(); | ||||
| 	        RESTServers_.clear(); | ||||
| 			Pool_.joinAll(); | ||||
| 			RESTServers_.clear(); | ||||
| 	    } | ||||
|  | ||||
| 	    inline void reinitialize(Poco::Util::Application &self) override; | ||||
|  | ||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) { | ||||
| 	    inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path, uint64_t Id) { | ||||
| 	        RESTAPIHandler::BindingMap Bindings; | ||||
| 	        return RESTAPI_internal_server(Path, Bindings, Logger_, Server_); | ||||
| 	    } | ||||
| @@ -2472,31 +2506,31 @@ namespace OpenWifi { | ||||
| 	    Poco::ThreadPool	    Pool_; | ||||
| 	    RESTAPI_GenericServer   Server_; | ||||
|  | ||||
| 	    RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi") | ||||
| 	    RESTAPI_IntServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi") | ||||
| 	    { | ||||
| 	    } | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); }; | ||||
| 	inline RESTAPI_IntServer * RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); }; | ||||
|  | ||||
| 	class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| 	public: | ||||
| 	    InternalRequestHandlerFactory(RESTAPI_GenericServer & Server) : | ||||
| 	    Logger_(RESTAPI_InternalServer()->Logger()), | ||||
| 	  	explicit IntRequestHandlerFactory(RESTAPI_GenericServer & Server) : | ||||
| 	    Logger_(RESTAPI_IntServer()->Logger()), | ||||
| 	    Server_(Server){} | ||||
|  | ||||
| 	    inline Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override { | ||||
| 	        Poco::URI uri(Request.getURI()); | ||||
| 	        auto *Path = uri.getPath().c_str(); | ||||
| 	        return RESTAPI_InternalServer()->CallServer(Path); | ||||
| 	        return RESTAPI_IntServer()->CallServer(Path, TransactionId_++); | ||||
| 	    } | ||||
| 	private: | ||||
| 		static inline std::atomic_uint64_t  TransactionId_ = 1; | ||||
| 	    Poco::Logger    & Logger_; | ||||
| 	    RESTAPI_GenericServer   &Server_; | ||||
| 	    RESTAPI_GenericServer   & Server_; | ||||
| 	}; | ||||
|  | ||||
| 	inline int RESTAPI_InternalServer::Start() { | ||||
| 	inline int RESTAPI_IntServer::Start() { | ||||
| 	    Logger_.information("Starting."); | ||||
| 	    Server_.InitLogging(); | ||||
|  | ||||
| @@ -2514,7 +2548,7 @@ namespace OpenWifi { | ||||
| 	        Params->setMaxQueued(200); | ||||
| 	        Params->setKeepAlive(true); | ||||
|  | ||||
| 	        auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new InternalRequestHandlerFactory(Server_), Pool_, Sock, Params); | ||||
| 	        auto NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory(Server_), Pool_, Sock, Params); | ||||
| 	        NewServer->start(); | ||||
| 	        RESTServers_.push_back(std::move(NewServer)); | ||||
| 	    } | ||||
| @@ -2554,11 +2588,13 @@ namespace OpenWifi { | ||||
| 			DAEMON_BUS_TIMER(BusTimer), | ||||
| 			SubSystems_(std::move(Subsystems)) { | ||||
| 		    instance_ = this; | ||||
| 		    RandomEngine_.seed(std::chrono::steady_clock::now().time_since_epoch().count()); | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] std::string Version() { return Version_; } | ||||
| 		[[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; } | ||||
| 		[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } | ||||
| 		[[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; } | ||||
| 		[[nodiscard]] bool Debug() const { return DebugMode_; } | ||||
| 		[[nodiscard]] uint64_t ID() const { return ID_; } | ||||
| 		[[nodiscard]] std::string Hash() const { return MyHash_; }; | ||||
| @@ -2571,6 +2607,13 @@ namespace OpenWifi { | ||||
| 		static inline uint64_t GetPID() { return Poco::Process::id(); }; | ||||
| 		[[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; }; | ||||
| 		[[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;}; | ||||
| 		[[nodiscard]] inline uint64_t Random(uint64_t ceiling) { | ||||
| 		    return (RandomEngine_() % ceiling); | ||||
| 		} | ||||
|  | ||||
| 		[[nodiscard]] inline uint64_t Random(uint64_t min, uint64_t max) { | ||||
| 		    return ((RandomEngine_() % (max-min)) + min); | ||||
| 		} | ||||
|  | ||||
| 		inline void Exit(int Reason); | ||||
| 		inline void BusMessageReceived(const std::string &Key, const std::string & Message); | ||||
| @@ -2592,7 +2635,7 @@ namespace OpenWifi { | ||||
| 		inline void InitializeSubSystemServers(); | ||||
| 		inline void StartSubSystemServers(); | ||||
| 		inline void StopSubSystemServers(); | ||||
| 		[[nodiscard]] inline std::string CreateUUID(); | ||||
| 		[[nodiscard]] static inline std::string CreateUUID(); | ||||
| 		inline bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); | ||||
| 		inline void Reload(const std::string &Sub); | ||||
| 		inline Types::StringVec GetSubSystems() const; | ||||
| @@ -2622,9 +2665,10 @@ namespace OpenWifi { | ||||
| 		std::string                 ConfigFileName_; | ||||
| 		Poco::UUIDGenerator         UUIDGenerator_; | ||||
| 		uint64_t                    ID_ = 1; | ||||
| 		Poco::SharedPtr<Poco::Crypto::RSAKey>	AppKey_ = nullptr; | ||||
| 		Poco::SharedPtr<Poco::Crypto::RSAKey>	AppKey_; | ||||
| 		bool                        DebugMode_ = false; | ||||
| 		std::string 				DataDir_; | ||||
| 		std::string                 WWWAssetsDir_; | ||||
| 		SubSystemVec			    SubSystems_; | ||||
| 		Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); | ||||
| 		Poco::Crypto::Cipher        * Cipher_ = nullptr; | ||||
| @@ -2634,9 +2678,10 @@ namespace OpenWifi { | ||||
| 		std::string 				MyPrivateEndPoint_; | ||||
| 		std::string 				MyPublicEndPoint_; | ||||
| 		std::string                 UIURI_; | ||||
| 		std::string 				Version_{std::string(APP_VERSION) + "("+ BUILD_NUMBER + ")"}; | ||||
| 		std::string 				Version_{ OW_VERSION::VERSION + "("+ OW_VERSION::BUILD + ")" + " - " + OW_VERSION::HASH }; | ||||
| 		BusEventManager				BusEventManager_; | ||||
| 		std::mutex 					InfraMutex_; | ||||
| 		std::default_random_engine  RandomEngine_; | ||||
|  | ||||
| 		std::string DAEMON_PROPERTIES_FILENAME; | ||||
| 		std::string DAEMON_ROOT_ENV_VAR; | ||||
| @@ -2784,8 +2829,8 @@ namespace OpenWifi { | ||||
| 	    // add the default services | ||||
| 	    SubSystems_.push_back(KafkaManager()); | ||||
| 	    SubSystems_.push_back(ALBHealthCheckServer()); | ||||
| 	    SubSystems_.push_back(RESTAPI_server()); | ||||
| 	    SubSystems_.push_back(RESTAPI_InternalServer()); | ||||
| 	    SubSystems_.push_back(RESTAPI_ExtServer()); | ||||
| 	    SubSystems_.push_back(RESTAPI_IntServer()); | ||||
|  | ||||
| 	    Poco::Net::initializeSSL(); | ||||
| 	    Poco::Net::HTTPStreamFactory::registerFactory(); | ||||
| @@ -2813,6 +2858,9 @@ namespace OpenWifi { | ||||
| 	            logger().log(E); | ||||
| 	        } | ||||
| 	    } | ||||
| 	    WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets",""); | ||||
| 	    if(WWWAssetsDir_.empty()) | ||||
| 	        WWWAssetsDir_ = DataDir_; | ||||
|  | ||||
| 	    LoadMyConfig(); | ||||
|  | ||||
| @@ -2919,13 +2967,42 @@ namespace OpenWifi { | ||||
|  | ||||
| 	inline void MicroService::StopSubSystemServers() { | ||||
| 	    BusEventManager_.Stop(); | ||||
| 	    for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) | ||||
| 	        (*i)->Stop(); | ||||
| 	    for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) { | ||||
| 			(*i)->Stop(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	[[nodiscard]] inline std::string MicroService::CreateUUID() { | ||||
| 	    return UUIDGenerator_.create().toString(); | ||||
| 	} | ||||
|         static std::random_device              rd; | ||||
|         static std::mt19937_64                 gen(rd()); | ||||
|         static std::uniform_int_distribution<> dis(0, 15); | ||||
|         static std::uniform_int_distribution<> dis2(8, 11); | ||||
|  | ||||
|         std::stringstream ss; | ||||
|         int i; | ||||
|         ss << std::hex; | ||||
|         for (i = 0; i < 8; i++) { | ||||
|             ss << dis(gen); | ||||
|         } | ||||
|         ss << "-"; | ||||
|         for (i = 0; i < 4; i++) { | ||||
|             ss << dis(gen); | ||||
|         } | ||||
|         ss << "-4"; | ||||
|         for (i = 0; i < 3; i++) { | ||||
|             ss << dis(gen); | ||||
|         } | ||||
|         ss << "-"; | ||||
|         ss << dis2(gen); | ||||
|         for (i = 0; i < 3; i++) { | ||||
|             ss << dis(gen); | ||||
|         } | ||||
|         ss << "-"; | ||||
|         for (i = 0; i < 12; i++) { | ||||
|             ss << dis(gen); | ||||
|         }; | ||||
|         return ss.str(); | ||||
|     } | ||||
|  | ||||
| 	inline bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { | ||||
| 	    try { | ||||
| @@ -2938,7 +3015,6 @@ namespace OpenWifi { | ||||
| 	            } | ||||
| 	            return true; | ||||
| 	        } else { | ||||
| 	            // std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl; | ||||
| 	            for (auto i : SubSystems_) { | ||||
| 	                if (Sub == Poco::toLower(i->Name())) { | ||||
| 	                    i->Logger().setLevel(P); | ||||
| @@ -3088,7 +3164,6 @@ namespace OpenWifi { | ||||
| 	        StartSubSystemServers(); | ||||
| 	        waitForTerminationRequest(); | ||||
| 	        StopSubSystemServers(); | ||||
|  | ||||
| 	        logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME)); | ||||
| 	    } | ||||
|  | ||||
| @@ -3168,6 +3243,7 @@ namespace OpenWifi { | ||||
|  | ||||
| 	inline int ALBHealthCheckServer::Start() { | ||||
| 	    if(MicroService::instance().ConfigGetBool("alb.enable",false)) { | ||||
| 	        Running_=true; | ||||
| 	        Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); | ||||
| 	        Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_); | ||||
| 	        auto Params = new Poco::Net::HTTPServerParams; | ||||
| @@ -3212,41 +3288,42 @@ namespace OpenWifi { | ||||
| 	    KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false); | ||||
| 	} | ||||
|  | ||||
| 	inline void KafkaManager::ProducerThr() { | ||||
| 	inline void KafkaProducer::run() { | ||||
| 	    cppkafka::Configuration Config({ | ||||
| 	        { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, | ||||
| 	        { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } | ||||
| 	    }); | ||||
| 	    SystemInfoWrapper_ = 	R"lit({ "system" : { "id" : )lit" + | ||||
| 	    KafkaManager()->SystemInfoWrapper_ = 	R"lit({ "system" : { "id" : )lit" + | ||||
| 	            std::to_string(MicroService::instance().ID()) + | ||||
| 	            R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() + | ||||
| 	            R"lit(" } , "payload" : )lit" ; | ||||
| 	    cppkafka::Producer	Producer(Config); | ||||
| 	    ProducerRunning_ = true; | ||||
| 	    while(ProducerRunning_) { | ||||
| 	    Running_ = true; | ||||
| 	    while(Running_) { | ||||
| 	        std::this_thread::sleep_for(std::chrono::milliseconds(200)); | ||||
| 	        try | ||||
| 	        { | ||||
| 	            std::lock_guard G(ProducerMutex_); | ||||
| 	            std::lock_guard G(Mutex_); | ||||
| 	            auto Num=0; | ||||
| 	            while (!Queue_.empty()) { | ||||
| 	                const auto M = Queue_.front(); | ||||
| 	            while (!KafkaManager()->Queue_.empty()) { | ||||
| 	                const auto M = KafkaManager()->Queue_.front(); | ||||
| 	                Producer.produce( | ||||
| 	                        cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); | ||||
| 	                Queue_.pop(); | ||||
| 	                KafkaManager()->Queue_.pop(); | ||||
| 	                Num++; | ||||
| 	            } | ||||
| 	            if(Num) | ||||
| 	                Producer.flush(); | ||||
| 	        } catch (const cppkafka::HandleException &E ) { | ||||
| 	            Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); | ||||
| 	            KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); | ||||
| 	        } catch (const Poco::Exception &E) { | ||||
| 	            Logger_.log(E); | ||||
| 	            KafkaManager()->Logger_.log(E); | ||||
| 	        } | ||||
| 	    } | ||||
| 	    Producer.flush(); | ||||
| 	} | ||||
|  | ||||
| 	inline void KafkaManager::ConsumerThr() { | ||||
| 	inline void KafkaConsumer::run() { | ||||
| 	    cppkafka::Configuration Config({ | ||||
| 	        { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, | ||||
| 	        { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }, | ||||
| @@ -3266,13 +3343,13 @@ namespace OpenWifi { | ||||
| 	    cppkafka::Consumer Consumer(Config); | ||||
| 	    Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) { | ||||
| 	        if(!partitions.empty()) { | ||||
| 	            Logger_.information(Poco::format("Partition assigned: %Lu...", | ||||
| 	            KafkaManager()->Logger_.information(Poco::format("Partition assigned: %Lu...", | ||||
|                                                  (uint64_t)partitions.front().get_partition())); | ||||
| 	        } | ||||
| 	    }); | ||||
| 	    Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { | ||||
| 	        if(!partitions.empty()) { | ||||
| 	            Logger_.information(Poco::format("Partition revocation: %Lu...", | ||||
| 	            KafkaManager()->Logger_.information(Poco::format("Partition revocation: %Lu...", | ||||
|                                                  (uint64_t)partitions.front().get_partition())); | ||||
| 	        } | ||||
| 	    }); | ||||
| @@ -3281,13 +3358,13 @@ namespace OpenWifi { | ||||
| 	    auto BatchSize = MicroService::instance().ConfigGetInt("openwifi.kafka.consumer.batchsize",20); | ||||
|  | ||||
| 	    Types::StringVec    Topics; | ||||
| 	    for(const auto &i:Notifiers_) | ||||
| 	    for(const auto &i:KafkaManager()->Notifiers_) | ||||
| 	        Topics.push_back(i.first); | ||||
|  | ||||
| 	    Consumer.subscribe(Topics); | ||||
|  | ||||
| 	    ConsumerRunning_ = true; | ||||
| 	    while(ConsumerRunning_) { | ||||
| 	    Running_ = true; | ||||
| 	    while(Running_) { | ||||
| 	        try { | ||||
| 	            std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200)); | ||||
| 	            for(auto const &Msg:MsgVec) { | ||||
| @@ -3295,14 +3372,14 @@ namespace OpenWifi { | ||||
| 	                    continue; | ||||
| 	                if (Msg.get_error()) { | ||||
| 	                    if (!Msg.is_eof()) { | ||||
| 	                        Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); | ||||
| 	                        KafkaManager()->Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); | ||||
| 	                    }if(!AutoCommit) | ||||
| 	                        Consumer.async_commit(Msg); | ||||
| 	                    continue; | ||||
| 	                } | ||||
| 	                std::lock_guard G(ConsumerMutex_); | ||||
| 	                auto It = Notifiers_.find(Msg.get_topic()); | ||||
| 	                if (It != Notifiers_.end()) { | ||||
| 	                std::lock_guard G(Mutex_); | ||||
| 	                auto It = KafkaManager()->Notifiers_.find(Msg.get_topic()); | ||||
| 	                if (It != KafkaManager()->Notifiers_.end()) { | ||||
| 	                    Types::TopicNotifyFunctionList &FL = It->second; | ||||
| 	                    std::string Key{Msg.get_key()}; | ||||
| 	                    std::string Payload{Msg.get_payload()}; | ||||
| @@ -3315,21 +3392,22 @@ namespace OpenWifi { | ||||
| 	                    Consumer.async_commit(Msg); | ||||
| 	            } | ||||
| 	        } catch (const cppkafka::HandleException &E) { | ||||
| 	            Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); | ||||
| 	            KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); | ||||
| 	        } catch (const Poco::Exception &E) { | ||||
| 	            Logger_.log(E); | ||||
| 	            KafkaManager()->Logger_.log(E); | ||||
| 	        } | ||||
| 	    } | ||||
| 	    Consumer.unsubscribe(); | ||||
| 	} | ||||
|  | ||||
| 	inline void RESTAPI_server::reinitialize(Poco::Util::Application &self) { | ||||
| 	inline void RESTAPI_ExtServer::reinitialize(Poco::Util::Application &self) { | ||||
| 	    MicroService::instance().LoadConfigurationFile(); | ||||
| 	    Logger_.information("Reinitializing."); | ||||
| 	    Stop(); | ||||
| 	    Start(); | ||||
| 	} | ||||
|  | ||||
| 	void RESTAPI_InternalServer::reinitialize(Poco::Util::Application &self) { | ||||
| 	void RESTAPI_IntServer::reinitialize(Poco::Util::Application &self) { | ||||
| 	    MicroService::instance().LoadConfigurationFile(); | ||||
| 	    Logger_.information("Reinitializing."); | ||||
| 	    Stop(); | ||||
| @@ -3540,11 +3618,9 @@ namespace OpenWifi { | ||||
|                 if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { | ||||
|                     Poco::JSON::Parser	P; | ||||
|                     ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
|                     //	                std::cout << "Response OK" << std::endl; | ||||
|                 } else { | ||||
|                     Poco::JSON::Parser	P; | ||||
|                     ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
|                     //	                std::cout << "Response: " << Response.getStatus() << std::endl; | ||||
|                 } | ||||
|                 return Response.getStatus(); | ||||
|             } | ||||
| @@ -3590,11 +3666,9 @@ namespace OpenWifi { | ||||
|                 if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { | ||||
|                     Poco::JSON::Parser	P; | ||||
|                     ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
|                     //	                std::cout << "Response OK" << std::endl; | ||||
|                 } else { | ||||
|                     Poco::JSON::Parser	P; | ||||
|                     ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); | ||||
|                     //	                std::cout << "Response: " << Response.getStatus() << std::endl; | ||||
|                 } | ||||
|                 return Response.getStatus(); | ||||
|             } | ||||
| @@ -3620,9 +3694,9 @@ namespace OpenWifi { | ||||
|     } | ||||
|  | ||||
| #ifdef    TIP_SECURITY_SERVICE | ||||
|     [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|     [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ); | ||||
| #endif | ||||
|     inline bool RESTAPIHandler::IsAuthorized() { | ||||
|     inline bool RESTAPIHandler::IsAuthorized( bool & Expired ) { | ||||
|         if(Internal_) { | ||||
|             auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); | ||||
|             if(!Allowed) { | ||||
| @@ -3652,12 +3726,12 @@ namespace OpenWifi { | ||||
|                 } | ||||
|             } | ||||
| #ifdef    TIP_SECURITY_SERVICE | ||||
|             if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_)) { | ||||
|             if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired)) { | ||||
| #else | ||||
|             if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) { | ||||
|             if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired)) { | ||||
| #endif | ||||
|                 if(Server_.LogIt(Request->getMethod(),true)) { | ||||
|                     Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s", | ||||
|                     Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s'", | ||||
|                                                UserInfo_.userinfo.email, | ||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
|                                                Request->clientAddress().toString(), | ||||
| @@ -3667,11 +3741,10 @@ namespace OpenWifi { | ||||
|                 return true; | ||||
|             } else { | ||||
|                 if(Server_.LogBadTokens(true)) { | ||||
|                     Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s", | ||||
|                     Logger_.debug(Poco::format("X-REQ-DENIED(%s): Method='%s' Path='%s'", | ||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
|                                                Request->getMethod(), Request->getURI())); | ||||
|                 } | ||||
|                 UnAuthorized("Invalid token", INVALID_TOKEN); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|   | ||||
| @@ -59,6 +59,7 @@ namespace OpenWifi::RESTAPI::Errors { | ||||
|     static const std::string UnrecognizedRequest{"Ill-formed request. Please consult documentation."}; | ||||
|     static const std::string MissingAuthenticationInformation{"Missing authentication information."}; | ||||
|     static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."}; | ||||
|     static const std::string ExpiredToken{"Token has expired, user must login."}; | ||||
| } | ||||
|  | ||||
| #endif //OWPROV_RESTAPI_ERRORS_H | ||||
|   | ||||
| @@ -2,8 +2,7 @@ | ||||
| // Created by stephane bourque on 2021-10-06. | ||||
| // | ||||
|  | ||||
| #ifndef OPENWIFI_STORAGE_H | ||||
| #define OPENWIFI_STORAGE_H | ||||
| #pragma once | ||||
|  | ||||
| #include "Poco/Data/Session.h" | ||||
| #include "Poco/Data/SessionPool.h" | ||||
| @@ -26,13 +25,6 @@ namespace OpenWifi { | ||||
|  | ||||
|     class StorageClass : public SubSystemServer { | ||||
|     public: | ||||
| /*        static StorageClass *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new StorageClass; | ||||
|             } | ||||
|             return instance_; | ||||
|         } | ||||
| */ | ||||
|         StorageClass() noexcept: | ||||
|             SubSystemServer("StorageClass", "STORAGE-SVR", "storage") | ||||
|         { | ||||
| @@ -56,18 +48,18 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         void Stop() override { | ||||
|  | ||||
|             Pool_->shutdown(); | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { | ||||
|             if(dbType_==sqlite) { | ||||
|                 return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " "; | ||||
|                 return " LIMIT " + std::to_string(From) + ", " + std::to_string(HowMany) + " "; | ||||
|             } else if(dbType_==pgsql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|             } else if(dbType_==mysql) { | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|                 return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|             } | ||||
|             return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " "; | ||||
|             return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; | ||||
|         } | ||||
|  | ||||
|         inline std::string ConvertParams(const std::string & S) const { | ||||
| @@ -96,11 +88,11 @@ namespace OpenWifi { | ||||
|         inline int Setup_PostgreSQL(); | ||||
|  | ||||
|     protected: | ||||
|         std::unique_ptr<Poco::Data::SessionPool>        	Pool_; | ||||
|         std::unique_ptr<Poco::Data::SQLite::Connector>  	SQLiteConn_; | ||||
|         std::unique_ptr<Poco::Data::PostgreSQL::Connector>  PostgresConn_; | ||||
|         std::unique_ptr<Poco::Data::MySQL::Connector>       MySQLConn_; | ||||
|         DBType                                              dbType_ = sqlite; | ||||
|     	Poco::SharedPtr<Poco::Data::SessionPool>    Pool_; | ||||
|         Poco::Data::SQLite::Connector  	            SQLiteConn_; | ||||
|         Poco::Data::PostgreSQL::Connector           PostgresConn_; | ||||
|         Poco::Data::MySQL::Connector                MySQLConn_; | ||||
|         DBType                                      dbType_ = sqlite; | ||||
|     }; | ||||
|  | ||||
| #ifdef	SMALL_BUILD | ||||
| @@ -114,9 +106,8 @@ namespace OpenWifi { | ||||
|         auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); | ||||
|         auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); | ||||
|         auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); | ||||
|         SQLiteConn_ = std::make_unique<Poco::Data::SQLite::Connector>(); | ||||
|         SQLiteConn_->registerConnector(); | ||||
|         Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime); | ||||
|         SQLiteConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 4, NumSessions, IdleTime)); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -139,9 +130,8 @@ namespace OpenWifi { | ||||
|                 ";port=" + Port + | ||||
|                 ";compress=true;auto-reconnect=true"; | ||||
|  | ||||
|         MySQLConn_ = std::make_unique<Poco::Data::MySQL::Connector>(); | ||||
|         MySQLConn_->registerConnector(); | ||||
|         Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); | ||||
|         MySQLConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(MySQLConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| @@ -166,14 +156,11 @@ namespace OpenWifi { | ||||
|                 " port=" + Port + | ||||
|                 " connect_timeout=" + ConnectionTimeout; | ||||
|  | ||||
|         PostgresConn_ = std::make_unique<Poco::Data::PostgreSQL::Connector>(); | ||||
|         PostgresConn_->registerConnector(); | ||||
|         Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime); | ||||
|         PostgresConn_.registerConnector(); | ||||
|         Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(PostgresConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); | ||||
|  | ||||
|         return 0; | ||||
|     } | ||||
| #endif | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif //OPENWIFI_STORAGE_H | ||||
|   | ||||
							
								
								
									
										13
									
								
								src/ow_version.h.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/ow_version.h.in
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-12-06. | ||||
| // | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| namespace OW_VERSION { | ||||
|     inline static const std::string VERSION{"@CMAKE_PROJECT_VERSION@"}; | ||||
|     inline static const std::string BUILD{"@BUILD_NUM@"}; | ||||
|     inline static const std::string HASH{"@GIT_HASH@"}; | ||||
| } | ||||
| @@ -183,4 +183,19 @@ namespace OpenWifi { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Storage::CleanOldActionLinks() { | ||||
|         try { | ||||
|             Poco::Data::Session     Sess = Pool_->get(); | ||||
|             Poco::Data::Statement   Delete(Sess); | ||||
|  | ||||
|             uint64_t CutOff = std::time(nullptr) - (30 * 24 * 60 * 60); | ||||
|             std::string St{"DELETE from ActionLinks where Created<=?"}; | ||||
|             Delete << ConvertParams(St), | ||||
|                 Poco::Data::Keywords::use(CutOff); | ||||
|             Delete.execute(); | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -15,7 +15,7 @@ namespace OpenWifi { | ||||
|     "RevocationDate 	BIGINT " | ||||
| */ | ||||
|  | ||||
|     bool Storage::AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) { | ||||
|     bool Storage::AddToken(std::string &UserID, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Insert(Sess); | ||||
| @@ -29,7 +29,7 @@ namespace OpenWifi { | ||||
|                 Poco::Data::Keywords::use(Token), | ||||
|                 Poco::Data::Keywords::use(RefreshToken), | ||||
|                 Poco::Data::Keywords::use(TokenType), | ||||
|                 Poco::Data::Keywords::use(UserName), | ||||
|                 Poco::Data::Keywords::use(UserID), | ||||
|                 Poco::Data::Keywords::use(Now), | ||||
|                 Poco::Data::Keywords::use(Expires), | ||||
|                 Poco::Data::Keywords::use(TimeOut), | ||||
| @@ -42,29 +42,24 @@ namespace OpenWifi { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo) { | ||||
|     bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate) { | ||||
|         try { | ||||
|  | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Select(Sess); | ||||
|  | ||||
|             uint32_t RevocationDate = 0 ; | ||||
|  | ||||
|             std::string St2{"SELECT " + AllTokensValuesForSelect + " From Tokens WHERE Token=?"}; | ||||
|             RevocationDate = 0 ; | ||||
|             std::string St2{"SELECT " + AllTokensFieldsForSelect + " From Tokens WHERE Token=?"}; | ||||
|             Select << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.access_token_), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.refresh_token_), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.token_type_), | ||||
|                 Poco::Data::Keywords::into(UInfo.userinfo.email), | ||||
|                 Poco::Data::Keywords::into(UInfo.userinfo.Id), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.created_), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.expires_in_), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.idle_timeout_), | ||||
|                 Poco::Data::Keywords::into(RevocationDate), | ||||
|                 Poco::Data::Keywords::use(Token); | ||||
|             Select.execute(); | ||||
|  | ||||
|             if(RevocationDate>0) | ||||
|                return false; | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
| @@ -116,15 +111,15 @@ namespace OpenWifi { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CleanRevokedTokens(uint64_t Oldest) { | ||||
|     bool Storage::CleanExpiredTokens() { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Delete(Sess); | ||||
|             uint64_t Now = std::time(nullptr); | ||||
|  | ||||
|             std::string St2{"DELETE From Tokens WHERE Created <= ?"}; | ||||
|             std::string St2{"DELETE From Tokens WHERE (Created+Expires) <= ?"}; | ||||
|             Delete << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::use(Oldest); | ||||
|                 Poco::Data::Keywords::use(Now); | ||||
|             Delete.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
| @@ -133,14 +128,14 @@ namespace OpenWifi { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::RevokeAllTokens(std::string & username) { | ||||
|     bool Storage::RevokeAllTokens(std::string & UserId) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Delete(Sess); | ||||
|  | ||||
|             std::string St2{"DELETE From Tokens WHERE Username=?"}; | ||||
|             Delete << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::use(username); | ||||
|             Poco::Data::Keywords::use(UserId); | ||||
|             Delete.execute(); | ||||
|             return true; | ||||
|         } catch(const Poco::Exception &E) { | ||||
|   | ||||
| @@ -80,7 +80,23 @@ namespace OpenWifi { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     std::string DefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"}; | ||||
|     std::string OldDefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"}; | ||||
|     std::string NewDefaultUseridStockUUID{"11111111-0000-0000-6666-999999999999"}; | ||||
|  | ||||
|     void Storage::ReplaceOldDefaultUUID() { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             std::string St1{"update users set id=? where id=?"}; | ||||
|  | ||||
|             Poco::Data::Statement Update(Sess); | ||||
|             Update << ConvertParams(St1), | ||||
|                 Poco::Data::Keywords::use(NewDefaultUseridStockUUID), | ||||
|                 Poco::Data::Keywords::use(OldDefaultUseridStockUUID); | ||||
|             Update.execute(); | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     //  if we do not find a default user, then we need to create one based on the | ||||
|     //  property file. We must set its flag to "must change password", this user has root privilege. | ||||
| @@ -89,12 +105,13 @@ namespace OpenWifi { | ||||
|         SecurityObjects::UserInfo   U; | ||||
|         bool DefaultUserCreated = false; | ||||
|  | ||||
|         ReplaceOldDefaultUUID(); | ||||
|         AppServiceRegistry().Get("defaultusercreated",DefaultUserCreated); | ||||
|         if(!GetUserById(DefaultUseridStockUUID,U) && !DefaultUserCreated) { | ||||
|         if(!GetUserById(NewDefaultUseridStockUUID,U) && !DefaultUserCreated) { | ||||
|             U.currentPassword = MicroService::instance().ConfigGetString("authentication.default.password",""); | ||||
|             U.lastPasswords.push_back(U.currentPassword); | ||||
|             U.email = MicroService::instance().ConfigGetString("authentication.default.username",""); | ||||
|             U.Id = DefaultUseridStockUUID; | ||||
|             U.Id = NewDefaultUseridStockUUID; | ||||
|             U.userRole = SecurityObjects::ROOT; | ||||
|             U.creationDate = std::time(nullptr); | ||||
|             U.validated = true; | ||||
| @@ -132,7 +149,7 @@ namespace OpenWifi { | ||||
|                 return false; | ||||
|  | ||||
|             if(!PasswordHashedAlready) { | ||||
|                 NewUser.Id = MicroService::instance().CreateUUID(); | ||||
|                 NewUser.Id = MicroService::CreateUUID(); | ||||
|                 NewUser.creationDate = std::time(nullptr); | ||||
|             } | ||||
|  | ||||
|   | ||||
| @@ -15,23 +15,23 @@ namespace OpenWifi { | ||||
|             "description    varchar," | ||||
|             "avatar         varchar," | ||||
|             "email          varchar," | ||||
|             "validated      int," | ||||
|             "validated      boolean," | ||||
|             "validationEmail    varchar," | ||||
|             "validationDate bigint," | ||||
|             "creationDate   bigint," | ||||
|             "validationURI  varchar," | ||||
|             "changePassword int," | ||||
|             "changePassword boolean," | ||||
|             "lastLogin      bigint," | ||||
|             "currentLoginURI    varchar," | ||||
|             "lastPasswordChange bigint," | ||||
|             "lastEmailCheck     bigint," | ||||
|             "waitingForEmailCheck   int," | ||||
|             "waitingForEmailCheck   boolean," | ||||
|             "locale             varchar," | ||||
|             "notes              text," | ||||
|             "location           varchar," | ||||
|             "owner              varchar," | ||||
|             "suspended          int," | ||||
|             "blackListed        int," | ||||
|             "suspended          boolean," | ||||
|             "blackListed        boolean," | ||||
|             "userRole           varchar," | ||||
|             "userTypeProprietaryInfo    text," | ||||
|             "securityPolicy     text," | ||||
| @@ -111,23 +111,23 @@ namespace OpenWifi { | ||||
|         std::string,    // description; | ||||
|         std::string,    // avatar; | ||||
|         std::string,    // email; | ||||
|         uint64_t,       // bool validated = false; | ||||
|         bool,       // bool validated = false; | ||||
|         std::string,    // validationEmail; | ||||
|         uint64_t,       // validationDate = 0; | ||||
|         uint64_t,       // creationDate = 0; | ||||
|         std::string,    // validationURI; | ||||
|         uint64_t,       // bool changePassword = true; | ||||
|         bool,       // bool changePassword = true; | ||||
|         uint64_t,       // lastLogin = 0; | ||||
|         std::string,    // currentLoginURI; | ||||
|         uint64_t,       // lastPasswordChange = 0; | ||||
|         uint64_t,       // lastEmailCheck = 0; | ||||
|         uint64_t,      // bool waitingForEmailCheck = false; | ||||
|         bool,      // bool waitingForEmailCheck = false; | ||||
|         std::string,    // locale; | ||||
|         std::string,    // notes; | ||||
|         std::string,    // location; | ||||
|         std::string,    // owner; | ||||
|         uint64_t,       // bool suspended = false; | ||||
|         uint64_t,       // bool blackListed = false; | ||||
|         bool,       // bool suspended = false; | ||||
|         bool,       // bool blackListed = false; | ||||
|         std::string,    // userRole; | ||||
|         std::string,    // userTypeProprietaryInfo; | ||||
|         std::string,    // securityPolicy; | ||||
|   | ||||
| @@ -35,7 +35,7 @@ fi | ||||
| token="" | ||||
| result_file=result.json | ||||
| username="tip@ucentral.com" | ||||
| password="openwifi" | ||||
| password="Snoopy99!!!" | ||||
| #username="stephb@incognito.com" | ||||
| #password="Snoopy98!" | ||||
| browser_list=(firefox sensible-browser xdg-open w3m links links2 lynx youtube-dl) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user