mirror of
				https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
				synced 2025-10-30 18:27:49 +00:00 
			
		
		
		
	Compare commits
	
		
			45 Commits
		
	
	
		
			v2.3.0-RC2
			...
			feature/wi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 05d06fce53 | ||
|   | eaac1f1625 | ||
|   | c5f4c067bb | ||
|   | 31a9e4564b | ||
|   | a9affc29bb | ||
|   | 65fc0a1d10 | ||
|   | 82c01ce438 | ||
|   | 5f900883e8 | ||
|   | e97b8e64be | ||
|   | 6c90c75708 | ||
|   | a3d86c7cf9 | ||
|   | 50b6ac9522 | ||
|   | 15b947a34d | ||
|   | 160bd00a99 | ||
|   | 3c7daa537a | ||
|   | c5bab1d749 | ||
|   | 96c3244be0 | ||
|   | 7e4b515f60 | ||
|   | a63f80e497 | ||
|   | 2eae6cc73c | ||
|   | 96f215b3c2 | ||
|   | 9551384358 | ||
|   | b21c5c5e00 | ||
|   | 031d35256c | ||
|   | 5738fa47bb | ||
|   | fe17650333 | ||
|   | 7636568fb4 | ||
|   | c0ef77eb53 | ||
|   | 00742a5d0a | ||
|   | a96f673380 | ||
|   | 53ecdb471e | ||
|   | f80a0c5007 | ||
|   | 9e7d7ba67d | ||
|   | b508c0d054 | ||
|   | 79788dab44 | ||
|   | 8dec946c45 | ||
|   | 43ea5ac424 | ||
|   | 328ff158cb | ||
|   | 2b89d843c3 | ||
|   | 45a50483be | ||
|   | c8ae94a062 | ||
|   | 7b19143d6f | ||
|   | bc0c889098 | ||
|   | 6f8f81866f | ||
|   | f213c99816 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -18,3 +18,4 @@ _deps | ||||
| *.csr | ||||
| /cmake-build/ | ||||
| /smake-build-debug/ | ||||
| test_scripts/curl/result.json | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(owsec VERSION 2.3.0) | ||||
| project(owsec VERSION 2.4.0) | ||||
|  | ||||
| set(CMAKE_CXX_STANDARD 17) | ||||
|  | ||||
| @@ -42,7 +42,7 @@ find_package(Boost REQUIRED system) | ||||
| find_package(OpenSSL REQUIRED) | ||||
| find_package(ZLIB REQUIRED) | ||||
| find_package(AWSSDK     REQUIRED COMPONENTS sns) | ||||
|  | ||||
| find_package(nlohmann_json  REQUIRED) | ||||
| find_package(CppKafka REQUIRED) | ||||
| find_package(PostgreSQL REQUIRED) | ||||
| find_package(MySQL REQUIRED) | ||||
| @@ -86,7 +86,11 @@ add_executable( owsec | ||||
|         src/MFAServer.cpp src/MFAServer.h | ||||
|         src/SMS_provider_aws.cpp src/SMS_provider_aws.h | ||||
|         src/SMS_provider.cpp src/SMS_provider.h | ||||
|         src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h) | ||||
|         src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h | ||||
|         src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h | ||||
|         src/storage/storage_tokens.h | ||||
|         src/ActionLinkManager.cpp src/ActionLinkManager.h | ||||
|         ) | ||||
|  | ||||
| if(NOT SMALL_BUILD) | ||||
|     target_link_libraries(owsec PUBLIC | ||||
|   | ||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -11,6 +11,8 @@ RUN apk add --update --no-cache \ | ||||
|  | ||||
| RUN git clone https://github.com/stephb9959/poco /poco | ||||
| RUN git clone https://github.com/stephb9959/cppkafka /cppkafka | ||||
| RUN git clone https://github.com/nlohmann/json /json | ||||
| RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator | ||||
| RUN git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp /aws-sdk-cpp | ||||
|  | ||||
| WORKDIR /aws-sdk-cpp | ||||
| @@ -37,6 +39,20 @@ RUN cmake .. | ||||
| RUN cmake --build . --config Release -j8 | ||||
| RUN cmake --build . --target install | ||||
|  | ||||
| WORKDIR /json | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| WORKDIR /json-schema-validator | ||||
| RUN mkdir cmake-build | ||||
| WORKDIR cmake-build | ||||
| RUN cmake .. | ||||
| RUN make | ||||
| RUN make install | ||||
|  | ||||
| ADD CMakeLists.txt build /owsec/ | ||||
| ADD cmake /owsec/cmake | ||||
| ADD src /owsec/src | ||||
| @@ -75,6 +91,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 | ||||
| ``` | ||||
| ``` | ||||
|   | ||||
| @@ -24,6 +24,9 @@ spec: | ||||
|     metadata: | ||||
|       annotations: | ||||
|         checksum/config: {{ include "owsec.config" . | sha256sum }} | ||||
|         {{- with .Values.podAnnotations }} | ||||
|         {{- toYaml . | nindent 8 }} | ||||
|         {{- end }} | ||||
|       labels: | ||||
|         app.kubernetes.io/name: {{ include "owsec.name" . }} | ||||
|         app.kubernetes.io/instance: {{ .Release.Name }} | ||||
|   | ||||
| @@ -8,7 +8,7 @@ fullnameOverride: "" | ||||
| images: | ||||
|   owsec: | ||||
|     repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec | ||||
|     tag: v2.3.0-RC2 | ||||
|     tag: main | ||||
|     pullPolicy: Always | ||||
| #    regcred: | ||||
| #      registry: tip-tip-wlan-cloud-ucentral.jfrog.io | ||||
| @@ -95,6 +95,8 @@ tolerations: [] | ||||
|  | ||||
| affinity: {} | ||||
|  | ||||
| podAnnotations: {} | ||||
|  | ||||
| persistence: | ||||
|   enabled: true | ||||
|   # storageClassName: "-" | ||||
|   | ||||
| @@ -51,6 +51,16 @@ components: | ||||
|             properties: | ||||
|               ErrorCode: | ||||
|                 type: integer | ||||
|                 enum: | ||||
|                   - 0     # Success | ||||
|                   - 1     # PASSWORD_CHANGE_REQUIRED, | ||||
|                   - 2     # INVALID_CREDENTIALS, | ||||
|                   - 3     # PASSWORD_ALREADY_USED, | ||||
|                   - 4     # USERNAME_PENDING_VERIFICATION, | ||||
|                   - 5     # PASSWORD_INVALID, | ||||
|                   - 6     # INTERNAL_ERROR, | ||||
|                   - 7     # ACCESS_DENIED, | ||||
|                   - 8     # INVALID_TOKEN | ||||
|               ErrorDetails: | ||||
|                 type: string | ||||
|               ErrorDescription: | ||||
|   | ||||
							
								
								
									
										68
									
								
								src/ActionLinkManager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/ActionLinkManager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #include "ActionLinkManager.h" | ||||
| #include "StorageService.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     int ActionLinkManager::Start() { | ||||
|         if(!Running_) | ||||
|             Thr_.start(*this); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     void ActionLinkManager::Stop() { | ||||
|         if(Running_) { | ||||
|             Running_ = false; | ||||
|             Thr_.wakeUp(); | ||||
|             Thr_.join(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void ActionLinkManager::run() { | ||||
|         Running_ = true ; | ||||
|  | ||||
|         while(Running_) { | ||||
|             Poco::Thread::trySleep(2000); | ||||
|             if(!Running_) | ||||
|                 break; | ||||
|             std::vector<SecurityObjects::ActionLink>    Links; | ||||
|             { | ||||
|                 std::lock_guard G(Mutex_); | ||||
|                 StorageService()->GetActions(Links); | ||||
|             } | ||||
|  | ||||
|             if(Links.empty()) | ||||
|                 continue; | ||||
|  | ||||
|             for(auto &i:Links) { | ||||
|                 if(!Running_) | ||||
|                     break; | ||||
|  | ||||
|                 SecurityObjects::UserInfo UInfo; | ||||
|                 if(!StorageService()->GetUserById(i.userId,UInfo)) { | ||||
|                     StorageService()->CancelAction(i.id); | ||||
|                     continue; | ||||
|                 } | ||||
|  | ||||
|                 if(i.action==OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD) { | ||||
|                     if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::FORGOT_PASSWORD)) { | ||||
|                         Logger_.information(Poco::format("Send password reset link to %s",UInfo.email)); | ||||
|                     } | ||||
|                     StorageService()->SentAction(i.id); | ||||
|                 } else if (i.action==OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL) { | ||||
|                     if(AuthService::SendEmailToUser(i.id, UInfo.email, AuthService::EMAIL_VERIFICATION)) { | ||||
|                         Logger_.information(Poco::format("Send email verification link to %s",UInfo.email)); | ||||
|                     } | ||||
|                     StorageService()->SentAction(i.id); | ||||
|                 } else { | ||||
|                     StorageService()->SentAction(i.id); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										41
									
								
								src/ActionLinkManager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/ActionLinkManager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #ifndef OWSEC_ACTIONLINKMANAGER_H | ||||
| #define OWSEC_ACTIONLINKMANAGER_H | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class ActionLinkManager : public SubSystemServer, Poco::Runnable { | ||||
|     public: | ||||
|  | ||||
|         enum Actions { | ||||
|             FORGOT_PASSWORD, | ||||
|             VERIFY_EMAIL | ||||
|         }; | ||||
|  | ||||
|         static ActionLinkManager * instance() { | ||||
|             static ActionLinkManager instance; | ||||
|             return &instance; | ||||
|         } | ||||
|  | ||||
|         int Start() final; | ||||
|         void Stop() final; | ||||
|         void run(); | ||||
|  | ||||
|     private: | ||||
|         Poco::Thread        Thr_; | ||||
|         std::atomic_bool    Running_ = false; | ||||
|  | ||||
|         ActionLinkManager() noexcept: | ||||
|             SubSystemServer("ActionLinkManager", "ACTION-SVR", "action.server") | ||||
|                 { | ||||
|                 } | ||||
|     }; | ||||
|     inline ActionLinkManager * ActionLinkManager() { return ActionLinkManager::instance(); } | ||||
| } | ||||
|  | ||||
| #endif //OWSEC_ACTIONLINKMANAGER_H | ||||
| @@ -11,6 +11,7 @@ | ||||
| #include "Poco/Net/OAuth20Credentials.h" | ||||
| #include "Poco/JWT/Token.h" | ||||
| #include "Poco/JWT/Signer.h" | ||||
| #include "Poco/StringTokenizer.h" | ||||
|  | ||||
| #include "framework/MicroService.h" | ||||
| #include "StorageService.h" | ||||
| @@ -21,7 +22,6 @@ | ||||
| #include "MFAServer.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class AuthService *AuthService::instance_ = nullptr; | ||||
|  | ||||
|     AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) { | ||||
| 		switch (C) { | ||||
| @@ -46,10 +46,6 @@ namespace OpenWifi { | ||||
| 		Signer_.setRSAKey(MicroService::instance().Key()); | ||||
| 		Signer_.addAllAlgorithms(); | ||||
| 		Logger_.notice("Starting..."); | ||||
|         Secure_ = MicroService::instance().ConfigGetBool("authentication.enabled",true); | ||||
|         DefaultPassword_ = MicroService::instance().ConfigGetString("authentication.default.password",""); | ||||
|         DefaultUserName_ = MicroService::instance().ConfigGetString("authentication.default.username",""); | ||||
|         Mechanism_ = MicroService::instance().ConfigGetString("authentication.service.type","internal"); | ||||
|         PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$"); | ||||
|         TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60); | ||||
|         HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5); | ||||
| @@ -62,39 +58,41 @@ namespace OpenWifi { | ||||
|  | ||||
| 	bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) | ||||
|     { | ||||
|         if(!Secure_) | ||||
|             return true; | ||||
|  | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
| 		std::string CallToken; | ||||
|  | ||||
|         std::lock_guard	Guard(Mutex_); | ||||
| 		try { | ||||
| 			Poco::Net::OAuth20Credentials Auth(Request); | ||||
|  | ||||
| 			if (Auth.getScheme() == "Bearer") { | ||||
| 				CallToken = Auth.getBearerToken(); | ||||
| 			} | ||||
| 		} catch(const Poco::Exception &E) { | ||||
| 		} | ||||
|  | ||||
| 		if(!CallToken.empty()) { | ||||
| 		    if(StorageService()->IsTokenRevoked(CallToken)) | ||||
| 		        return false; | ||||
| 		    auto Client = UserCache_.find(CallToken); | ||||
| 		    if( Client == UserCache_.end() ) | ||||
| 		        return ValidateToken(CallToken, CallToken, UInfo); | ||||
|  | ||||
| 		    if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) { | ||||
| 		        SessionToken = CallToken; | ||||
| 		        UInfo = Client->second ; | ||||
| 		        return true; | ||||
| 		    std::string CallToken; | ||||
| 		    Poco::Net::OAuth20Credentials Auth(Request); | ||||
| 		    if (Auth.getScheme() == "Bearer") { | ||||
| 		        CallToken = Auth.getBearerToken(); | ||||
| 		    } | ||||
| 		    UserCache_.erase(CallToken); | ||||
| 		    StorageService()->RevokeToken(CallToken); | ||||
| 		    return false; | ||||
| 		} | ||||
|  | ||||
| 		    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; | ||||
| 		                    return true; | ||||
| 		                } | ||||
| 		            } | ||||
| 		            return false; | ||||
| 		        } | ||||
|  | ||||
| 		        if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) { | ||||
| 		            SessionToken = CallToken; | ||||
| 		            UInfo = Client->second ; | ||||
| 		            return true; | ||||
| 		        } | ||||
|  | ||||
| 		        UserCache_.erase(Client); | ||||
| 		        StorageService()->RevokeToken(CallToken); | ||||
| 		        return false; | ||||
| 		    } | ||||
| 		} catch(const Poco::Exception &E) { | ||||
| 		    Logger_.log(E); | ||||
| 		} | ||||
| 		return false; | ||||
|     } | ||||
|  | ||||
| @@ -168,28 +166,6 @@ namespace OpenWifi { | ||||
| 		return JWT; | ||||
|     } | ||||
|  | ||||
| 	bool AuthService::ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo  ) { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
| 		try { | ||||
|             auto E = UserCache_.find(SessionToken); | ||||
|             if(E == UserCache_.end()) { | ||||
|                 if(StorageService()->GetToken(SessionToken,UInfo)) { | ||||
|                     if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) { | ||||
|                         UserCache_[UInfo.webtoken.access_token_] = UInfo; | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 UInfo = E->second; | ||||
|                 return true; | ||||
|             } | ||||
| 		} catch (const Poco::Exception &E ) { | ||||
| 			Logger_.log(E); | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     void AuthService::CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo) | ||||
|     { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
| @@ -215,35 +191,80 @@ namespace OpenWifi { | ||||
|     } | ||||
|  | ||||
|     bool AuthService::SetPassword(const std::string &NewPassword, SecurityObjects::UserInfo & UInfo) { | ||||
|         auto NewPasswordHash = ComputePasswordHash(UInfo.email, NewPassword); | ||||
|         for (auto const &i:UInfo.lastPasswords) { | ||||
|             if (i == NewPasswordHash) { | ||||
|                 return false; | ||||
|         std::lock_guard     G(Mutex_); | ||||
|  | ||||
|         Poco::toLowerInPlace(UInfo.email); | ||||
|         for (const auto &i:UInfo.lastPasswords) { | ||||
|             auto Tokens = Poco::StringTokenizer(i,"|"); | ||||
|             if(Tokens.count()==2) { | ||||
|                 const auto & Salt = Tokens[0]; | ||||
|                 for(const auto &j:UInfo.lastPasswords) { | ||||
|                     auto OldTokens = Poco::StringTokenizer(j,"|"); | ||||
|                     if(OldTokens.count()==2) { | ||||
|                         SHA2_.update(Salt+NewPassword+UInfo.email); | ||||
|                         if(OldTokens[1]==Utils::ToHex(SHA2_.digest())) | ||||
|                             return false; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 SHA2_.update(NewPassword+UInfo.email); | ||||
|                 if(Tokens[0]==Utils::ToHex(SHA2_.digest())) | ||||
|                     return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(UInfo.lastPasswords.size()==HowManyOldPassword_) { | ||||
|             UInfo.lastPasswords.erase(UInfo.lastPasswords.begin()); | ||||
|         } | ||||
|         UInfo.lastPasswords.push_back(NewPasswordHash); | ||||
|         UInfo.currentPassword = NewPasswordHash; | ||||
|  | ||||
|         auto NewHash = ComputeNewPasswordHash(UInfo.email,NewPassword); | ||||
|         UInfo.lastPasswords.push_back(NewHash); | ||||
|         UInfo.currentPassword = NewHash; | ||||
|         UInfo.changePassword = false; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     AuthService::AUTH_ERROR AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ) | ||||
|     static std::string GetMeSomeSalt() { | ||||
|         auto start = std::chrono::high_resolution_clock::now(); | ||||
|         return std::to_string(start.time_since_epoch().count()); | ||||
|     } | ||||
|  | ||||
|     std::string AuthService::ComputeNewPasswordHash(const std::string &UserName, const std::string &Password) { | ||||
|         std::string UName = Poco::trim(Poco::toLower(UserName)); | ||||
|         auto Salt = GetMeSomeSalt(); | ||||
|         SHA2_.update(Salt + Password + UName ); | ||||
|         return Salt + "|" + Utils::ToHex(SHA2_.digest()); | ||||
|     } | ||||
|  | ||||
|     bool AuthService::ValidatePasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword) { | ||||
|         std::lock_guard G(Mutex_); | ||||
|  | ||||
|         std::string UName = Poco::trim(Poco::toLower(UserName)); | ||||
|         auto Tokens = Poco::StringTokenizer(StoredPassword,"|"); | ||||
|         if(Tokens.count()==1) { | ||||
|             SHA2_.update(Password+UName); | ||||
|             if(Tokens[0]==Utils::ToHex(SHA2_.digest())) | ||||
|                 return true; | ||||
|         } else if (Tokens.count()==2) { | ||||
|             SHA2_.update(Tokens[0]+Password+UName); | ||||
|             if(Tokens[1]==Utils::ToHex(SHA2_.digest())) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ) | ||||
|     { | ||||
|         std::lock_guard		Guard(Mutex_); | ||||
|         SecurityObjects::AclTemplate	ACL; | ||||
|  | ||||
|         Poco::toLowerInPlace(UserName); | ||||
|         auto PasswordHash = ComputePasswordHash(UserName, Password); | ||||
|  | ||||
|         if(StorageService()->GetUserByEmail(UserName,UInfo.userinfo)) { | ||||
|             if(UInfo.userinfo.waitingForEmailCheck) { | ||||
|                 return USERNAME_PENDING_VERIFICATION; | ||||
|             } | ||||
|  | ||||
|             if(PasswordHash != UInfo.userinfo.currentPassword) { | ||||
|             if(!ValidatePasswordHash(UserName,Password,UInfo.userinfo.currentPassword)) { | ||||
|                 return INVALID_CREDENTIALS; | ||||
|             } | ||||
|  | ||||
| @@ -273,51 +294,31 @@ namespace OpenWifi { | ||||
|             return SUCCESS; | ||||
|         } | ||||
|  | ||||
|         if(((UserName == DefaultUserName_) && (DefaultPassword_== ComputePasswordHash(UserName,Password))) || !Secure_) | ||||
|         { | ||||
|             ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true; | ||||
|             UInfo.webtoken.acl_template_ = ACL; | ||||
|             UInfo.userinfo.email = DefaultUserName_; | ||||
|             UInfo.userinfo.currentPassword = DefaultPassword_; | ||||
|             UInfo.userinfo.name = DefaultUserName_; | ||||
|             UInfo.userinfo.userRole = SecurityObjects::ROOT; | ||||
|             CreateToken(UserName, UInfo ); | ||||
|             return SUCCESS; | ||||
|         } | ||||
|         return INVALID_CREDENTIALS; | ||||
|     } | ||||
|  | ||||
|     std::string AuthService::ComputePasswordHash(const std::string &UserName, const std::string &Password) { | ||||
|         std::string UName = Poco::trim(Poco::toLower(UserName)); | ||||
|         SHA2_.update(Password + UName); | ||||
|         return Utils::ToHex(SHA2_.digest()); | ||||
|     } | ||||
|  | ||||
|     bool AuthService::SendEmailToUser(std::string &Email, EMAIL_REASON Reason) { | ||||
|     bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) { | ||||
|         SecurityObjects::UserInfo   UInfo; | ||||
|  | ||||
|         if(StorageService()->GetUserByEmail(Email,UInfo)) { | ||||
|             switch (Reason) { | ||||
|  | ||||
|                 case FORGOT_PASSWORD: { | ||||
|                         MessageAttributes Attrs; | ||||
|  | ||||
|                         Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||
|                         Attrs[LOGO] = "logo.jpg"; | ||||
|                         Attrs[LOGO] = GetLogoAssetURI(); | ||||
|                         Attrs[SUBJECT] = "Password reset link"; | ||||
|                         Attrs[ACTION_LINK] = | ||||
|                                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + UInfo.Id ; | ||||
|                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ; | ||||
|                         SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs); | ||||
|                     } | ||||
|                     break; | ||||
|  | ||||
|                 case EMAIL_VERIFICATION: { | ||||
|                         MessageAttributes Attrs; | ||||
|  | ||||
|                         Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||
|                         Attrs[LOGO] = "logo.jpg"; | ||||
|                         Attrs[LOGO] = GetLogoAssetURI(); | ||||
|                         Attrs[SUBJECT] = "EMail Address Verification"; | ||||
|                         Attrs[ACTION_LINK] = | ||||
|                                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; | ||||
|                         Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ; | ||||
|                         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); | ||||
|                         UInfo.waitingForEmailCheck = true; | ||||
|                     } | ||||
| @@ -326,19 +327,20 @@ namespace OpenWifi { | ||||
|                 default: | ||||
|                     break; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool AuthService::VerifyEmail(SecurityObjects::UserInfo &UInfo) { | ||||
|         MessageAttributes Attrs; | ||||
|         SecurityObjects::ActionLink A; | ||||
|  | ||||
|         Attrs[RECIPIENT_EMAIL] = UInfo.email; | ||||
|         Attrs[LOGO] = "logo.jpg"; | ||||
|         Attrs[SUBJECT] = "EMail Address Verification"; | ||||
|         Attrs[ACTION_LINK] = | ||||
|                 MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + UInfo.Id ; | ||||
|         SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs); | ||||
|         A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL; | ||||
|         A.userId = UInfo.email; | ||||
|         A.id = MicroService::instance().CreateUUID(); | ||||
|         A.created = std::time(nullptr); | ||||
|         A.expires = A.created + 24*60*60; | ||||
|         StorageService()->CreateAction(A); | ||||
|         UInfo.waitingForEmailCheck = true; | ||||
|         return true; | ||||
|     } | ||||
|   | ||||
| @@ -35,16 +35,6 @@ namespace OpenWifi{ | ||||
|             CUSTOM | ||||
|         }; | ||||
|  | ||||
|         enum AUTH_ERROR { | ||||
|             SUCCESS, | ||||
|             PASSWORD_CHANGE_REQUIRED, | ||||
|             INVALID_CREDENTIALS, | ||||
|             PASSWORD_ALREADY_USED, | ||||
|             USERNAME_PENDING_VERIFICATION, | ||||
|             PASSWORD_INVALID, | ||||
|             INTERNAL_ERROR | ||||
|         }; | ||||
|  | ||||
|         enum EMAIL_REASON { | ||||
|             FORGOT_PASSWORD, | ||||
|             EMAIL_VERIFICATION | ||||
| @@ -54,19 +44,16 @@ namespace OpenWifi{ | ||||
|         static int AccessTypeToInt(ACCESS_TYPE T); | ||||
|  | ||||
|         static AuthService *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new AuthService; | ||||
|             } | ||||
|             return instance_; | ||||
|             static AuthService instance; | ||||
|             return &instance; | ||||
|         } | ||||
|  | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|  | ||||
|         [[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|         [[nodiscard]] AUTH_ERROR Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|         [[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo ); | ||||
|         void CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
|         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UserInfo  ); | ||||
|         [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo); | ||||
|         [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;}; | ||||
|         void Logout(const std::string &token, bool EraseFromCache=true); | ||||
| @@ -77,26 +64,31 @@ namespace OpenWifi{ | ||||
|         [[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request); | ||||
|         [[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type); | ||||
|         [[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type); | ||||
|         [[nodiscard]] bool ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::WebToken & UserInfo  ); | ||||
|         [[nodiscard]] std::string ComputePasswordHash(const std::string &UserName, const std::string &Password); | ||||
|  | ||||
|         [[nodiscard]] std::string ComputeNewPasswordHash(const std::string &UserName, const std::string &Password); | ||||
|         [[nodiscard]] bool ValidatePasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword); | ||||
|  | ||||
|         [[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword); | ||||
|         [[nodiscard]] std::string ResetPassword(const std::string &Admin, const std::string &UserName); | ||||
|  | ||||
|         [[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo); | ||||
|         [[nodiscard]] static bool SendEmailToUser(std::string &Email, EMAIL_REASON Reason); | ||||
|         [[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); | ||||
|  | ||||
|         [[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"; | ||||
|         } | ||||
|  | ||||
|     private: | ||||
| 		static AuthService *instance_; | ||||
| 		bool    			Secure_ = false ; | ||||
| 		std::string     	DefaultUserName_; | ||||
| 		std::string			DefaultPassword_; | ||||
| 		std::string     	Mechanism_; | ||||
| 		Poco::JWT::Signer	Signer_; | ||||
| 		Poco::SHA2Engine	SHA2_; | ||||
| 		SecurityObjects::UserInfoCache UserCache_; | ||||
|         std::string          PasswordValidationStr_; | ||||
|         std::string         PasswordValidationStr_; | ||||
| 		std::regex          PasswordValidation_; | ||||
| 		uint64_t            TokenAging_ = 30 * 24 * 60 * 60; | ||||
|         uint64_t            HowManyOldPassword_=5; | ||||
|   | ||||
| @@ -30,6 +30,7 @@ | ||||
| #include "SMTPMailerService.h" | ||||
| #include "AuthService.h" | ||||
| #include "SMSSender.h" | ||||
| #include "ActionLinkManager.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class Daemon *Daemon::instance_ = nullptr; | ||||
| @@ -44,7 +45,9 @@ namespace OpenWifi { | ||||
|                                    SubSystemVec{ | ||||
|                                            StorageService(), | ||||
|                                            SMSSender(), | ||||
|                                            ActionLinkManager(), | ||||
|                                            SMTPMailerService(), | ||||
|                                            RESTAPI_RateLimiter(), | ||||
|                                            AuthService() | ||||
|                                    }); | ||||
|         } | ||||
| @@ -53,8 +56,8 @@ namespace OpenWifi { | ||||
|  | ||||
|     void Daemon::initialize() { | ||||
|         AssetDir_ = MicroService::instance().ConfigPath("openwifi.restapi.wwwassets"); | ||||
|         AccessPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.access", "/wwwassets/access_policy.html"); | ||||
|         PasswordPolicy_ = MicroService::instance().ConfigGetString("openwifi.document.policy.password", "/wwwassets/password_policy.html"); | ||||
|         AccessPolicy_ = MicroService::instance().ConfigPath("openwifi.document.policy.access", "/wwwassets/access_policy.html"); | ||||
|         PasswordPolicy_ = MicroService::instance().ConfigPath("openwifi.document.policy.password", "/wwwassets/password_policy.html"); | ||||
|     } | ||||
|  | ||||
|     void MicroServicePostInitialization() { | ||||
|   | ||||
| @@ -6,11 +6,10 @@ | ||||
| #include "SMSSender.h" | ||||
| #include "SMTPMailerService.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "AuthService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class MFAServer * MFAServer::instance_ = nullptr; | ||||
|  | ||||
|     int MFAServer::Start() { | ||||
|         return 0; | ||||
|     } | ||||
| @@ -47,7 +46,7 @@ namespace OpenWifi { | ||||
|         if(Method=="email" && SMTPMailerService()->Enabled() && !UInfo.userinfo.email.empty()) { | ||||
|             MessageAttributes Attrs; | ||||
|             Attrs[RECIPIENT_EMAIL] = UInfo.userinfo.email; | ||||
|             Attrs[LOGO] = "logo.jpg"; | ||||
|             Attrs[LOGO] = AuthService::GetLogoAssetURI(); | ||||
|             Attrs[SUBJECT] = "Login validation code"; | ||||
|             Attrs[CHALLENGE_CODE] = Challenge; | ||||
|             return SMTPMailerService()->SendMessage(UInfo.userinfo.email, "verification_code.txt", Attrs); | ||||
|   | ||||
| @@ -24,24 +24,21 @@ namespace OpenWifi { | ||||
|         int Start() override; | ||||
|         void Stop() override; | ||||
|         static MFAServer *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new MFAServer; | ||||
|             } | ||||
|             return instance_; | ||||
|             static MFAServer instance; | ||||
|             return &instance; | ||||
|         } | ||||
|  | ||||
|         bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge); | ||||
|         bool CompleteMFAChallenge(Poco::JSON::Object::Ptr &ChallengeResponse, SecurityObjects::UserInfoAndPolicy &UInfo); | ||||
|         bool MethodEnabled(const std::string &Method); | ||||
|         static bool MethodEnabled(const std::string &Method); | ||||
|         bool ResendCode(const std::string &uuid); | ||||
|         bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); | ||||
|         static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); | ||||
|  | ||||
|         static inline std::string MakeChallenge() { | ||||
|             return std::to_string(rand() % 999999); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         static MFAServer *  instance_; | ||||
|         MFAChallengeCache   Cache_; | ||||
|         MFAServer() noexcept: | ||||
|             SubSystemServer("MFServer", "MFA-SVR", "mfa") | ||||
|   | ||||
| @@ -11,46 +11,61 @@ | ||||
| #include "Daemon.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     void RESTAPI_action_links::DoGet() { | ||||
|  | ||||
|         auto Action = GetParameter("action",""); | ||||
|         auto Id = GetParameter("id",""); | ||||
|  | ||||
|         SecurityObjects::ActionLink Link; | ||||
|         if(!StorageService()->GetActionLink(Id,Link)) | ||||
|             return DoReturnA404(); | ||||
|  | ||||
|         if(Action=="password_reset") | ||||
|             return RequestResetPassword(Id); | ||||
|             return RequestResetPassword(Link); | ||||
|         else if(Action=="email_verification") | ||||
|             return DoEmailVerification(Id); | ||||
|             return DoEmailVerification(Link); | ||||
|         else | ||||
|             return DoReturnA404(); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_action_links::DoPost() { | ||||
|         auto Action = GetParameter("action",""); | ||||
|         auto Id = GetParameter("id",""); | ||||
|  | ||||
|         Logger_.information(Poco::format("COMPLETE-PASSWORD-RESET(%s): For ID=%s", Request->clientAddress().toString(), Id)); | ||||
|         if(Action=="password_reset") | ||||
|             CompleteResetPassword(Id); | ||||
|             return CompleteResetPassword(); | ||||
|         else | ||||
|             DoReturnA404(); | ||||
|             return DoReturnA404(); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_action_links::RequestResetPassword(std::string &Id) { | ||||
|         Logger_.information(Poco::format("REQUEST-PASSWORD-RESET(%s): For ID=%s", Request->clientAddress().toString(), Id)); | ||||
|     void RESTAPI_action_links::RequestResetPassword(SecurityObjects::ActionLink &Link) { | ||||
|         Logger_.information(Poco::format("REQUEST-PASSWORD-RESET(%s): For ID=%s", Request->clientAddress().toString(), Link.userId)); | ||||
|         Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset.html"}; | ||||
|         Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|         Types::StringPairVec    FormVars{ {"UUID", Link.id}, | ||||
|                                           {"PASSWORD_VALIDATION", AuthService()->PasswordValidationExpression()}}; | ||||
|         SendHTMLFileBack(FormFile,FormVars); | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_action_links::CompleteResetPassword(std::string &Id) { | ||||
|     void RESTAPI_action_links::CompleteResetPassword() { | ||||
|         //  form has been posted... | ||||
|         RESTAPI_PartHandler PartHandler; | ||||
|         Poco::Net::HTMLForm Form(*Request, Request->stream(), PartHandler); | ||||
|         if (!Form.empty()) { | ||||
|  | ||||
|             auto Password1 = Form.get("password1","bla"); | ||||
|             auto Password2 = Form.get("password1","blu"); | ||||
|             Id = Form.get("id",""); | ||||
|             auto Id = Form.get("id",""); | ||||
|             auto Now = std::time(nullptr); | ||||
|  | ||||
|             SecurityObjects::ActionLink Link; | ||||
|             if(!StorageService()->GetActionLink(Id,Link)) | ||||
|                 return DoReturnA404(); | ||||
|  | ||||
|             if(Now > Link.expires) { | ||||
|                 StorageService()->CancelAction(Id); | ||||
|                 return DoReturnA404(); | ||||
|             } | ||||
|  | ||||
|             if(Password1!=Password2 || !AuthService()->ValidatePassword(Password2) || !AuthService()->ValidatePassword(Password1)) { | ||||
|                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
| @@ -62,7 +77,7 @@ namespace OpenWifi { | ||||
|             } | ||||
|  | ||||
|             SecurityObjects::UserInfo   UInfo; | ||||
|             if(!StorageService()->GetUserById(Id,UInfo)) { | ||||
|             if(!StorageService()->GetUserById(Link.userId,UInfo)) { | ||||
|                 Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_error.html"}; | ||||
|                 Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|                                                   {"ERROR_TEXT", "This request does not contain a valid user ID. Please contact your system administrator."}}; | ||||
| @@ -82,37 +97,45 @@ namespace OpenWifi { | ||||
|                                                   {"ERROR_TEXT", "You cannot reuse one of your recent passwords."}}; | ||||
|                 return SendHTMLFileBack(FormFile,FormVars); | ||||
|             } | ||||
|             StorageService()->UpdateUserInfo(UInfo.email,Id,UInfo); | ||||
|             StorageService()->UpdateUserInfo(UInfo.email,Link.userId,UInfo); | ||||
|             Poco::File  FormFile{ Daemon()->AssetDir() + "/password_reset_success.html"}; | ||||
|             Types::StringPairVec    FormVars{ {"UUID", Id}, | ||||
|                                               {"USERNAME", UInfo.email}, | ||||
|                                               {"ACTION_LINK",MicroService::instance().GetUIURI()}}; | ||||
|             StorageService()->CompleteAction(Id); | ||||
|             SendHTMLFileBack(FormFile,FormVars); | ||||
|         } else { | ||||
|             DoReturnA404(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void RESTAPI_action_links::DoEmailVerification(std::string &Id) { | ||||
|         SecurityObjects::UserInfo UInfo; | ||||
|     void RESTAPI_action_links::DoEmailVerification(SecurityObjects::ActionLink &Link) { | ||||
|         auto Now = std::time(nullptr); | ||||
|  | ||||
|         Logger_.information(Poco::format("EMAIL-VERIFICATION(%s): For ID=%s", Request->clientAddress().toString(), Id)); | ||||
|         if (!StorageService()->GetUserById(Id, UInfo)) { | ||||
|             Types::StringPairVec FormVars{{"UUID",       Id}, | ||||
|         if(Now > Link.expires) { | ||||
|             StorageService()->CancelAction(Link.id); | ||||
|             return DoReturnA404(); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfo UInfo; | ||||
|         if (!StorageService()->GetUserById(Link.userId, UInfo)) { | ||||
|             Types::StringPairVec FormVars{{"UUID",       Link.id}, | ||||
|                                           {"ERROR_TEXT", "This does not appear to be a valid email verification link.."}}; | ||||
|             Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_error.html"}; | ||||
|             return SendHTMLFileBack(FormFile, FormVars); | ||||
|         } | ||||
|  | ||||
|         Logger_.information(Poco::format("EMAIL-VERIFICATION(%s): For ID=%s", Request->clientAddress().toString(), UInfo.email)); | ||||
|         UInfo.waitingForEmailCheck = false; | ||||
|         UInfo.validated = true; | ||||
|         UInfo.lastEmailCheck = std::time(nullptr); | ||||
|         UInfo.validationDate = std::time(nullptr); | ||||
|         StorageService()->UpdateUserInfo(UInfo.email, Id, UInfo); | ||||
|         Types::StringPairVec FormVars{{"UUID",     Id}, | ||||
|         StorageService()->UpdateUserInfo(UInfo.email, Link.userId, UInfo); | ||||
|         Types::StringPairVec FormVars{{"UUID",     Link.id}, | ||||
|                                       {"USERNAME", UInfo.email}, | ||||
|                                       {"ACTION_LINK",MicroService::instance().GetUIURI()}}; | ||||
|         Poco::File FormFile{Daemon()->AssetDir() + "/email_verification_success.html"}; | ||||
|         StorageService()->CompleteAction(Link.id); | ||||
|         SendHTMLFileBack(FormFile, FormVars); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -19,11 +19,12 @@ namespace OpenWifi { | ||||
|                                         Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
|                                         Server, | ||||
|                                         Internal, | ||||
|                                         false) {} | ||||
|                                         false, | ||||
|                                         true, RateLimit{.Interval=1000,.MaxCalls=5}) {} | ||||
|         static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; }; | ||||
|         void RequestResetPassword(std::string &Id); | ||||
|         void CompleteResetPassword(std::string &Id); | ||||
|         void DoEmailVerification(std::string &Id); | ||||
|         void RequestResetPassword(SecurityObjects::ActionLink &Link); | ||||
|         void CompleteResetPassword(); | ||||
|         void DoEmailVerification(SecurityObjects::ActionLink &Link); | ||||
|         void DoReturnA404(); | ||||
|  | ||||
|         void DoGet() final; | ||||
|   | ||||
| @@ -14,11 +14,12 @@ | ||||
| #include "MFAServer.h" | ||||
| #include "framework/RESTAPI_protocol.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "StorageService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
| 	void RESTAPI_oauth2Handler::DoGet() { | ||||
|         if (!IsAuthorized()) { | ||||
|             return UnAuthorized("Not authorized."); | ||||
|             return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation); | ||||
|         } | ||||
|         bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false); | ||||
|         if(GetMe) { | ||||
| @@ -27,7 +28,7 @@ namespace OpenWifi { | ||||
|             UserInfo_.userinfo.to_json(Me); | ||||
|             return ReturnObject(Me); | ||||
|         } | ||||
|         BadRequest("Ill-formed request. Please consult documentation."); | ||||
|         BadRequest(RESTAPI::Errors::UnrecognizedRequest); | ||||
| 	} | ||||
|  | ||||
|     void RESTAPI_oauth2Handler::DoDelete() { | ||||
| @@ -63,31 +64,46 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD,false)) { | ||||
|             //  Send an email to the userId | ||||
|             Logger_.information(Poco::format("FORGOTTEN-PASSWORD(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|             SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|             if(AuthService::SendEmailToUser(userId,AuthService::FORGOT_PASSWORD)) | ||||
|                 Logger_.information(Poco::format("Send password reset link to %s",userId)); | ||||
|             UInfo.webtoken.userMustChangePassword=true; | ||||
|             Poco::JSON::Object ReturnObj; | ||||
|             UInfo.webtoken.to_json(ReturnObj); | ||||
|             return ReturnObject(ReturnObj); | ||||
|             SecurityObjects::UserInfo UInfo1; | ||||
|             auto UserExists = StorageService()->GetUserByEmail(userId,UInfo1); | ||||
|             if(UserExists) { | ||||
|                 Logger_.information(Poco::format("FORGOTTEN-PASSWORD(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|                 SecurityObjects::ActionLink NewLink; | ||||
|  | ||||
|                 NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; | ||||
|                 NewLink.id = MicroService::instance().CreateUUID(); | ||||
|                 NewLink.userId = UInfo1.Id; | ||||
|                 NewLink.created = std::time(nullptr); | ||||
|                 NewLink.expires = NewLink.created + (24*60*60); | ||||
|                 StorageService()->CreateAction(NewLink); | ||||
|  | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } else { | ||||
|                 Poco::JSON::Object ReturnObj; | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 UInfo.webtoken.userMustChangePassword = true; | ||||
|                 UInfo.webtoken.to_json(ReturnObj); | ||||
|                 return ReturnObject(ReturnObj); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) { | ||||
|             Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|             if(Obj->has("uuid")) { | ||||
|                 auto uuid = Obj->get("uuid").toString(); | ||||
|             if(Obj->has(RESTAPI::Protocol::UUID)) { | ||||
|                 auto uuid = Obj->get(RESTAPI::Protocol::UUID).toString(); | ||||
|                 if(MFAServer().ResendCode(uuid)) | ||||
|                     return OK(); | ||||
|                 return UnAuthorized("Unrecognized credentials (username/password)."); | ||||
|             } | ||||
|             return UnAuthorized("Unrecognized credentials (username/password)."); | ||||
|             return UnAuthorized(RESTAPI::Errors::InvalidCredentials); | ||||
|         } | ||||
|  | ||||
|         if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) { | ||||
|             Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId)); | ||||
|             if(Obj->has("uuid")) { | ||||
|             if(Obj->has(RESTAPI::Protocol::UUID)) { | ||||
|                 SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|                 if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) { | ||||
|                     Poco::JSON::Object ReturnObj; | ||||
| @@ -95,29 +111,36 @@ namespace OpenWifi { | ||||
|                     return ReturnObject(ReturnObj); | ||||
|                 } | ||||
|             } | ||||
|             return UnAuthorized("Unrecognized credentials (username/password)."); | ||||
|             return UnAuthorized(RESTAPI::Errors::InvalidCredentials); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfoAndPolicy UInfo; | ||||
|         auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo); | ||||
|         if (Code==AuthService::SUCCESS) { | ||||
|         if (Code==SUCCESS) { | ||||
|             Poco::JSON::Object ReturnObj; | ||||
|             if(AuthService()->RequiresMFA(UInfo)) { | ||||
|                 if(MFAServer().StartMFAChallenge(UInfo, ReturnObj)) { | ||||
|                     return ReturnObject(ReturnObj); | ||||
|                 } | ||||
|                 Logger_.warning("MFA Seems ot be broken. Please fix. Disabling MFA checking for now."); | ||||
|                 Logger_.warning("MFA Seems to be broken. Please fix. Disabling MFA checking for now."); | ||||
|             } | ||||
|             UInfo.webtoken.to_json(ReturnObj); | ||||
|             return ReturnObject(ReturnObj); | ||||
|         } else { | ||||
|  | ||||
|             switch(Code) { | ||||
|                 case AuthService::INVALID_CREDENTIALS: return UnAuthorized("Unrecognized credentials (username/password)."); break; | ||||
|                 case AuthService::PASSWORD_INVALID: return UnAuthorized("Invalid password."); break; | ||||
|                 case AuthService::PASSWORD_ALREADY_USED: return UnAuthorized("Password already used previously."); break; | ||||
|                 case AuthService::USERNAME_PENDING_VERIFICATION: return UnAuthorized("User access pending email verification."); break; | ||||
|                 case AuthService::PASSWORD_CHANGE_REQUIRED: return UnAuthorized("Password change expected."); break; | ||||
|                 default: return UnAuthorized("Unrecognized credentials (username/password)."); break; | ||||
|                 case INVALID_CREDENTIALS: | ||||
|                     return UnAuthorized(RESTAPI::Errors::InvalidCredentials, Code); | ||||
|                 case PASSWORD_INVALID: | ||||
|                     return UnAuthorized(RESTAPI::Errors::InvalidPassword, Code); | ||||
|                 case PASSWORD_ALREADY_USED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PasswordRejected, Code); | ||||
|                 case USERNAME_PENDING_VERIFICATION: | ||||
|                     return UnAuthorized(RESTAPI::Errors::UserPendingVerification, Code); | ||||
|                 case PASSWORD_CHANGE_REQUIRED: | ||||
|                     return UnAuthorized(RESTAPI::Errors::PasswordMustBeChanged, Code); | ||||
|                 default: | ||||
|                     return UnAuthorized(RESTAPI::Errors::InvalidCredentials); break; | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -21,7 +21,7 @@ namespace OpenWifi { | ||||
|                                                       Poco::Net::HTTPRequest::HTTP_GET, | ||||
| 													  Poco::Net::HTTPRequest::HTTP_OPTIONS}, | ||||
| 													  Server, | ||||
| 													  Internal, false) {} | ||||
| 													  Internal, false, true , RateLimit{.Interval=2000,.MaxCalls=5}) {} | ||||
| 		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; | ||||
|   | ||||
| @@ -25,7 +25,11 @@ namespace OpenWifi { | ||||
|         } else if(!StorageService()->GetUserById(Id,UInfo)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         Poco::JSON::Object  UserInfoObject; | ||||
|         UInfo.currentPassword.clear(); | ||||
|         UInfo.lastPasswords.clear(); | ||||
|         UInfo.oauthType.clear(); | ||||
|         UInfo.to_json(UserInfoObject); | ||||
|         ReturnObject(UserInfoObject); | ||||
|     } | ||||
| @@ -36,17 +40,30 @@ 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) { | ||||
|             return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|         } | ||||
|  | ||||
|         if(!StorageService()->DeleteUser(UserInfo_.userinfo.email,Id)) { | ||||
|             return NotFound(); | ||||
|         } | ||||
|  | ||||
|         if(AuthService()->DeleteUserFromCache(UInfo.email)) | ||||
|             ; | ||||
|         if(AuthService()->DeleteUserFromCache(UInfo.email)) { | ||||
|             // nothing to do | ||||
|         } | ||||
|         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)); | ||||
| @@ -66,6 +83,14 @@ namespace OpenWifi { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidUserRole); | ||||
|         } | ||||
|  | ||||
|         if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) { | ||||
|             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)) { | ||||
|             return BadRequest(RESTAPI::Errors::InvalidEmailAddress); | ||||
| @@ -115,6 +140,14 @@ 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); | ||||
|         } | ||||
|  | ||||
|         SecurityObjects::UserInfo   NewUser; | ||||
|         auto RawObject = ParseStream(); | ||||
|         if(!NewUser.from_json(RawObject)) { | ||||
| @@ -136,8 +169,19 @@ namespace OpenWifi { | ||||
|         AssignIfPresent(RawObject,"suspended", Existing.suspended); | ||||
|         AssignIfPresent(RawObject,"blackListed", Existing.blackListed); | ||||
|  | ||||
|         if(RawObject->has("userRole")) | ||||
|             Existing.userRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()); | ||||
|         if(RawObject->has("userRole")) { | ||||
|             auto NewRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString()); | ||||
|             if(NewRole!=Existing.userRole) { | ||||
|                 if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && NewRole==SecurityObjects::ROOT) { | ||||
|                     return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|                 } | ||||
|                 if(Id==UserInfo_.userinfo.Id) { | ||||
|                     return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); | ||||
|                 } | ||||
|                 Existing.userRole = NewRole; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if(RawObject->has("notes")) { | ||||
|             SecurityObjects::NoteInfoVec NIV; | ||||
|             NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString()); | ||||
| @@ -161,26 +205,27 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         if(RawObject->has("userTypeProprietaryInfo")) { | ||||
|             bool ChangingMFA = NewUser.userTypeProprietaryInfo.mfa.enabled && !Existing.userTypeProprietaryInfo.mfa.enabled; | ||||
|  | ||||
|             Existing.userTypeProprietaryInfo.mfa.enabled = NewUser.userTypeProprietaryInfo.mfa.enabled; | ||||
|             if(NewUser.userTypeProprietaryInfo.mfa.method=="sms") { | ||||
|  | ||||
|             auto PropInfo = RawObject->get("userTypeProprietaryInfo"); | ||||
|             auto PInfo = PropInfo.extract<Poco::JSON::Object::Ptr>(); | ||||
|  | ||||
|             if(PInfo->isArray("mobiles")) { | ||||
|                 Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles; | ||||
|             } | ||||
|  | ||||
|             if(ChangingMFA && !NewUser.userTypeProprietaryInfo.mobiles.empty() && !SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)){ | ||||
|                 return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|             } | ||||
|  | ||||
|             if(NewUser.userTypeProprietaryInfo.mfa.method=="sms" && Existing.userTypeProprietaryInfo.mobiles.empty()) { | ||||
|                 return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|             } | ||||
|  | ||||
|             if(NewUser.userTypeProprietaryInfo.mfa.method=="email") { | ||||
|                 Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; | ||||
|                 auto MobileStruct = RawObject->get("userTypeProprietaryInfo"); | ||||
|                 auto Info = MobileStruct.extract<Poco::JSON::Object::Ptr>(); | ||||
|                 if(Info->isArray("mobiles")) { | ||||
|                     Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles; | ||||
|                 } | ||||
|                 if(!NewUser.userTypeProprietaryInfo.mobiles.empty() && !SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)){ | ||||
|                     return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|                 } | ||||
|                 if(NewUser.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mobiles.empty()) { | ||||
|                     return BadRequest(RESTAPI::Errors::NeedMobileNumber); | ||||
|                 } | ||||
|             } else if(NewUser.userTypeProprietaryInfo.mfa.method=="email") { | ||||
|                 Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; | ||||
|             } else { | ||||
|                 if(NewUser.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mfa.method.empty()) { | ||||
|                     return BadRequest(RESTAPI::Errors::BadMFAMethod); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -16,11 +16,14 @@ namespace OpenWifi { | ||||
|             Poco::JSON::Array ArrayObj; | ||||
|             Poco::JSON::Object Answer; | ||||
|             if (StorageService()->GetUsers(QB_.Offset, QB_.Limit, Users)) { | ||||
|                 for (const auto &i : Users) { | ||||
|                 for (auto &i : Users) { | ||||
|                     Poco::JSON::Object Obj; | ||||
|                     if (IdOnly) { | ||||
|                         ArrayObj.add(i.Id); | ||||
|                     } else { | ||||
|                         i.currentPassword.clear(); | ||||
|                         i.lastPasswords.clear(); | ||||
|                         i.oauthType.clear(); | ||||
|                         i.to_json(Obj); | ||||
|                         ArrayObj.add(Obj); | ||||
|                     } | ||||
| @@ -38,6 +41,9 @@ namespace OpenWifi { | ||||
|                     if (IdOnly) { | ||||
|                         ArrayObj.add(UInfo.Id); | ||||
|                     } else { | ||||
|                         UInfo.currentPassword.clear(); | ||||
|                         UInfo.lastPasswords.clear(); | ||||
|                         UInfo.oauthType.clear(); | ||||
|                         UInfo.to_json(Obj); | ||||
|                         ArrayObj.add(Obj); | ||||
|                     } | ||||
|   | ||||
| @@ -68,7 +68,7 @@ namespace OpenWifi::GWObjects { | ||||
| #endif | ||||
| 	} | ||||
|  | ||||
| 	bool Device::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool Device::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"serialNumber",SerialNumber); | ||||
| 			field_from_json(Obj,"deviceType",DeviceType); | ||||
| @@ -147,7 +147,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"attachFile", AttachDate); | ||||
| 	} | ||||
|  | ||||
| 	bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool DefaultConfiguration::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"name",Name); | ||||
| 			field_from_json(Obj,"configuration",Configuration); | ||||
| @@ -166,7 +166,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		field_to_json(Obj,"created", created); | ||||
| 	} | ||||
|  | ||||
| 	bool BlackListedDevice::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool BlackListedDevice::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"serialNumber",serialNumber); | ||||
| 			field_from_json(Obj,"author",author); | ||||
|   | ||||
| @@ -59,7 +59,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		std::string DevicePassword; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		void to_json_with_status(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 		void Print() const; | ||||
| 	}; | ||||
|  | ||||
| @@ -116,7 +116,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		uint64_t 	Created; | ||||
| 		uint64_t 	LastModified; | ||||
| 		void 		to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool 		from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool 		from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct CommandDetails { | ||||
| @@ -147,7 +147,7 @@ namespace OpenWifi::GWObjects { | ||||
| 		std::string author; | ||||
| 		uint64_t created; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct RttySessionDetails { | ||||
|   | ||||
| @@ -10,27 +10,30 @@ | ||||
| #include "RESTAPI_ProvObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| using OpenWifi::RESTAPI_utils::field_to_json; | ||||
| using OpenWifi::RESTAPI_utils::field_from_json; | ||||
|  | ||||
| namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void ObjectInfo::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json(Obj,"id",id); | ||||
|         RESTAPI_utils::field_to_json(Obj,"name",name); | ||||
|         RESTAPI_utils::field_to_json(Obj,"description",description); | ||||
|         RESTAPI_utils::field_to_json(Obj,"created",created); | ||||
|         RESTAPI_utils::field_to_json(Obj,"modified",modified); | ||||
|         RESTAPI_utils::field_to_json(Obj,"notes",notes); | ||||
|         RESTAPI_utils::field_to_json(Obj,"tags",tags); | ||||
|         field_to_json(Obj,"id",id); | ||||
|         field_to_json(Obj,"name",name); | ||||
|         field_to_json(Obj,"description",description); | ||||
|         field_to_json(Obj,"created",created); | ||||
|         field_to_json(Obj,"modified",modified); | ||||
|         field_to_json(Obj,"notes",notes); | ||||
|         field_to_json(Obj,"tags",tags); | ||||
|     } | ||||
|  | ||||
|     bool ObjectInfo::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json(Obj,"id",id); | ||||
|             RESTAPI_utils::field_from_json(Obj,"name",name); | ||||
|             RESTAPI_utils::field_from_json(Obj,"description",description); | ||||
|             RESTAPI_utils::field_from_json(Obj,"created",created); | ||||
|             RESTAPI_utils::field_from_json(Obj,"modified",modified); | ||||
|             RESTAPI_utils::field_from_json(Obj,"notes",notes); | ||||
|             RESTAPI_utils::field_from_json(Obj,"tags",tags); | ||||
|             field_from_json(Obj,"id",id); | ||||
|             field_from_json(Obj,"name",name); | ||||
|             field_from_json(Obj,"description",description); | ||||
|             field_from_json(Obj,"created",created); | ||||
|             field_from_json(Obj,"modified",modified); | ||||
|             field_from_json(Obj,"notes",notes); | ||||
|             field_from_json(Obj,"tags",tags); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -39,18 +42,18 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void ManagementPolicyEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json( Obj,"users",users); | ||||
|         RESTAPI_utils::field_to_json( Obj,"resources",resources); | ||||
|         RESTAPI_utils::field_to_json( Obj,"access",access); | ||||
|         RESTAPI_utils::field_to_json( Obj,"policy",policy); | ||||
|         field_to_json( Obj,"users",users); | ||||
|         field_to_json( Obj,"resources",resources); | ||||
|         field_to_json( Obj,"access",access); | ||||
|         field_to_json( Obj,"policy",policy); | ||||
|     } | ||||
|  | ||||
|     bool ManagementPolicyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"users",users); | ||||
|             RESTAPI_utils::field_from_json( Obj,"resources",resources); | ||||
|             RESTAPI_utils::field_from_json( Obj,"access",access); | ||||
|             RESTAPI_utils::field_from_json( Obj,"policy",policy); | ||||
|             field_from_json( Obj,"users",users); | ||||
|             field_from_json( Obj,"resources",resources); | ||||
|             field_from_json( Obj,"access",access); | ||||
|             field_from_json( Obj,"policy",policy); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -60,17 +63,17 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void ManagementPolicy::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||
|         RESTAPI_utils::field_to_json(Obj, "inUse", inUse); | ||||
|         RESTAPI_utils::field_to_json(Obj, "entity", entity); | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|         field_to_json(Obj, "inUse", inUse); | ||||
|         field_to_json(Obj, "entity", entity); | ||||
|     } | ||||
|  | ||||
|     bool ManagementPolicy::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json(Obj, "entries", entries); | ||||
|             RESTAPI_utils::field_from_json(Obj, "inUse", inUse); | ||||
|             RESTAPI_utils::field_from_json(Obj, "entity", entity); | ||||
|             field_from_json(Obj, "entries", entries); | ||||
|             field_from_json(Obj, "inUse", inUse); | ||||
|             field_from_json(Obj, "entity", entity); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -80,31 +83,31 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void Entity::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||
|         RESTAPI_utils::field_to_json( Obj,"venues",venues); | ||||
|         RESTAPI_utils::field_to_json( Obj,"children",children); | ||||
|         RESTAPI_utils::field_to_json( Obj,"contacts",contacts); | ||||
|         RESTAPI_utils::field_to_json( Obj,"locations",locations); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         RESTAPI_utils::field_to_json( Obj,"devices",devices); | ||||
|         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||
|         RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"venues",venues); | ||||
|         field_to_json( Obj,"children",children); | ||||
|         field_to_json( Obj,"contacts",contacts); | ||||
|         field_to_json( Obj,"locations",locations); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"devices",devices); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"sourceIP",sourceIP); | ||||
|     } | ||||
|  | ||||
|     bool Entity::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||
|             RESTAPI_utils::field_from_json( Obj,"venues",venues); | ||||
|             RESTAPI_utils::field_from_json( Obj,"children",children); | ||||
|             RESTAPI_utils::field_from_json( Obj,"contacts",contacts); | ||||
|             RESTAPI_utils::field_from_json( Obj,"locations",locations); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             RESTAPI_utils::field_from_json( Obj,"devices",devices); | ||||
|             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||
|             RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"venues",venues); | ||||
|             field_from_json( Obj,"children",children); | ||||
|             field_from_json( Obj,"contacts",contacts); | ||||
|             field_from_json( Obj,"locations",locations); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"devices",devices); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -113,14 +116,14 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void DiGraphEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||
|         RESTAPI_utils::field_to_json( Obj,"child",child); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"child",child); | ||||
|     } | ||||
|  | ||||
|     bool DiGraphEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||
|             RESTAPI_utils::field_from_json( Obj,"child",child); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"child",child); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
| @@ -130,37 +133,37 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void Venue::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||
|         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||
|         RESTAPI_utils::field_to_json( Obj,"children",children); | ||||
|         RESTAPI_utils::field_to_json( Obj,"devices",devices); | ||||
|         RESTAPI_utils::field_to_json( Obj,"topology",topology); | ||||
|         RESTAPI_utils::field_to_json( Obj,"parent",parent); | ||||
|         RESTAPI_utils::field_to_json( Obj,"design",design); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         RESTAPI_utils::field_to_json( Obj,"contact",contact); | ||||
|         RESTAPI_utils::field_to_json( Obj,"location",location); | ||||
|         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||
|         RESTAPI_utils::field_to_json( Obj,"sourceIP",sourceIP); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"children",children); | ||||
|         field_to_json( Obj,"devices",devices); | ||||
|         field_to_json( Obj,"topology",topology); | ||||
|         field_to_json( Obj,"parent",parent); | ||||
|         field_to_json( Obj,"design",design); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"contact",contact); | ||||
|         field_to_json( Obj,"location",location); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"sourceIP",sourceIP); | ||||
|     } | ||||
|  | ||||
|     bool Venue::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||
|             RESTAPI_utils::field_from_json( Obj,"children",children); | ||||
|             RESTAPI_utils::field_from_json( Obj,"devices",devices); | ||||
|             RESTAPI_utils::field_from_json( Obj,"topology",topology); | ||||
|             RESTAPI_utils::field_from_json( Obj,"parent",parent); | ||||
|             RESTAPI_utils::field_from_json( Obj,"design",design); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             RESTAPI_utils::field_from_json( Obj,"contact",contact); | ||||
|             RESTAPI_utils::field_from_json( Obj,"location",location); | ||||
|             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||
|             RESTAPI_utils::field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"children",children); | ||||
|             field_from_json( Obj,"devices",devices); | ||||
|             field_from_json( Obj,"topology",topology); | ||||
|             field_from_json( Obj,"parent",parent); | ||||
|             field_from_json( Obj,"design",design); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"contact",contact); | ||||
|             field_from_json( Obj,"location",location); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"sourceIP",sourceIP); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
| @@ -169,16 +172,16 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void UserInfoDigest::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json( Obj,"id",id); | ||||
|         RESTAPI_utils::field_to_json( Obj,"entity",loginId); | ||||
|         RESTAPI_utils::field_to_json( Obj,"children",userType); | ||||
|         field_to_json( Obj,"id",id); | ||||
|         field_to_json( Obj,"entity",loginId); | ||||
|         field_to_json( Obj,"children",userType); | ||||
|     } | ||||
|  | ||||
|     bool UserInfoDigest::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"id",id); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",loginId); | ||||
|             RESTAPI_utils::field_from_json( Obj,"children",userType); | ||||
|             field_from_json( Obj,"id",id); | ||||
|             field_from_json( Obj,"entity",loginId); | ||||
|             field_from_json( Obj,"children",userType); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|         } | ||||
| @@ -187,17 +190,17 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void ManagementRole::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         RESTAPI_utils::field_to_json( Obj,"users",users); | ||||
|         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"users",users); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|     } | ||||
|  | ||||
|     bool ManagementRole::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             RESTAPI_utils::field_from_json( Obj,"users",users); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"users",users); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|         } | ||||
| @@ -206,39 +209,39 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void Location::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"type",OpenWifi::ProvObjects::to_string(type)); | ||||
|         RESTAPI_utils::field_to_json( Obj,"buildingName",buildingName); | ||||
|         RESTAPI_utils::field_to_json( Obj,"addressLines",addressLines); | ||||
|         RESTAPI_utils::field_to_json( Obj,"city",city); | ||||
|         RESTAPI_utils::field_to_json( Obj,"state",state); | ||||
|         RESTAPI_utils::field_to_json( Obj,"postal",postal); | ||||
|         RESTAPI_utils::field_to_json( Obj,"country",country); | ||||
|         RESTAPI_utils::field_to_json( Obj,"phones",phones); | ||||
|         RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); | ||||
|         RESTAPI_utils::field_to_json( Obj,"geoCode",geoCode); | ||||
|         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||
|         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"type",OpenWifi::ProvObjects::to_string(type)); | ||||
|         field_to_json( Obj,"buildingName",buildingName); | ||||
|         field_to_json( Obj,"addressLines",addressLines); | ||||
|         field_to_json( Obj,"city",city); | ||||
|         field_to_json( Obj,"state",state); | ||||
|         field_to_json( Obj,"postal",postal); | ||||
|         field_to_json( Obj,"country",country); | ||||
|         field_to_json( Obj,"phones",phones); | ||||
|         field_to_json( Obj,"mobiles",mobiles); | ||||
|         field_to_json( Obj,"geoCode",geoCode); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool Location::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             std::string tmp_type; | ||||
|             RESTAPI_utils::field_from_json( Obj,"type", tmp_type); | ||||
|             field_from_json( Obj,"type", tmp_type); | ||||
|             type = location_from_string(tmp_type); | ||||
|             RESTAPI_utils::field_from_json( Obj,"buildingName",buildingName); | ||||
|             RESTAPI_utils::field_from_json( Obj,"addressLines",addressLines); | ||||
|             RESTAPI_utils::field_from_json( Obj,"city",city); | ||||
|             RESTAPI_utils::field_from_json( Obj,"state",state); | ||||
|             RESTAPI_utils::field_from_json( Obj,"postal",postal); | ||||
|             RESTAPI_utils::field_from_json( Obj,"country",country); | ||||
|             RESTAPI_utils::field_from_json( Obj,"phones",phones); | ||||
|             RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); | ||||
|             RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); | ||||
|             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"buildingName",buildingName); | ||||
|             field_from_json( Obj,"addressLines",addressLines); | ||||
|             field_from_json( Obj,"city",city); | ||||
|             field_from_json( Obj,"state",state); | ||||
|             field_from_json( Obj,"postal",postal); | ||||
|             field_from_json( Obj,"country",country); | ||||
|             field_from_json( Obj,"phones",phones); | ||||
|             field_from_json( Obj,"mobiles",mobiles); | ||||
|             field_from_json( Obj,"geoCode",geoCode); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
| @@ -248,43 +251,43 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void Contact::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"type", to_string(type)); | ||||
|         RESTAPI_utils::field_to_json( Obj,"title",title); | ||||
|         RESTAPI_utils::field_to_json( Obj,"salutation",salutation); | ||||
|         RESTAPI_utils::field_to_json( Obj,"firstname",firstname); | ||||
|         RESTAPI_utils::field_to_json( Obj,"lastname",lastname); | ||||
|         RESTAPI_utils::field_to_json( Obj,"initials",initials); | ||||
|         RESTAPI_utils::field_to_json( Obj,"visual",visual); | ||||
|         RESTAPI_utils::field_to_json( Obj,"mobiles",mobiles); | ||||
|         RESTAPI_utils::field_to_json( Obj,"phones",phones); | ||||
|         RESTAPI_utils::field_to_json( Obj,"primaryEmail",primaryEmail); | ||||
|         RESTAPI_utils::field_to_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|         RESTAPI_utils::field_to_json( Obj,"accessPIN",accessPIN); | ||||
|         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||
|         RESTAPI_utils::field_to_json( Obj,"entity",entity); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"type", to_string(type)); | ||||
|         field_to_json( Obj,"title",title); | ||||
|         field_to_json( Obj,"salutation",salutation); | ||||
|         field_to_json( Obj,"firstname",firstname); | ||||
|         field_to_json( Obj,"lastname",lastname); | ||||
|         field_to_json( Obj,"initials",initials); | ||||
|         field_to_json( Obj,"visual",visual); | ||||
|         field_to_json( Obj,"mobiles",mobiles); | ||||
|         field_to_json( Obj,"phones",phones); | ||||
|         field_to_json( Obj,"primaryEmail",primaryEmail); | ||||
|         field_to_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|         field_to_json( Obj,"accessPIN",accessPIN); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool Contact::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             std::string tmp_type; | ||||
|             RESTAPI_utils::field_from_json( Obj,"type", tmp_type); | ||||
|             field_from_json( Obj,"type", tmp_type); | ||||
|             type = contact_from_string(tmp_type); | ||||
|             RESTAPI_utils::field_from_json( Obj,"title",title); | ||||
|             RESTAPI_utils::field_from_json( Obj,"salutation",salutation); | ||||
|             RESTAPI_utils::field_from_json( Obj,"firstname",firstname); | ||||
|             RESTAPI_utils::field_from_json( Obj,"lastname",lastname); | ||||
|             RESTAPI_utils::field_from_json( Obj,"initials",initials); | ||||
|             RESTAPI_utils::field_from_json( Obj,"visual",visual); | ||||
|             RESTAPI_utils::field_from_json( Obj,"mobiles",mobiles); | ||||
|             RESTAPI_utils::field_from_json( Obj,"phones",phones); | ||||
|             RESTAPI_utils::field_from_json( Obj,"primaryEmail",primaryEmail); | ||||
|             RESTAPI_utils::field_from_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|             RESTAPI_utils::field_from_json( Obj,"accessPIN",accessPIN); | ||||
|             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"title",title); | ||||
|             field_from_json( Obj,"salutation",salutation); | ||||
|             field_from_json( Obj,"firstname",firstname); | ||||
|             field_from_json( Obj,"lastname",lastname); | ||||
|             field_from_json( Obj,"initials",initials); | ||||
|             field_from_json( Obj,"visual",visual); | ||||
|             field_from_json( Obj,"mobiles",mobiles); | ||||
|             field_from_json( Obj,"phones",phones); | ||||
|             field_from_json( Obj,"primaryEmail",primaryEmail); | ||||
|             field_from_json( Obj,"secondaryEmail",secondaryEmail); | ||||
|             field_from_json( Obj,"accessPIN",accessPIN); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch (...) { | ||||
|  | ||||
| @@ -294,35 +297,35 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void InventoryTag::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json(Obj, "serialNumber", serialNumber); | ||||
|         RESTAPI_utils::field_to_json(Obj, "venue", venue); | ||||
|         RESTAPI_utils::field_to_json(Obj, "entity", entity); | ||||
|         RESTAPI_utils::field_to_json(Obj, "subscriber", subscriber); | ||||
|         RESTAPI_utils::field_to_json(Obj, "deviceType", deviceType); | ||||
|         RESTAPI_utils::field_to_json(Obj, "qrCode", qrCode); | ||||
|         RESTAPI_utils::field_to_json(Obj, "geoCode", geoCode); | ||||
|         RESTAPI_utils::field_to_json(Obj, "location", location); | ||||
|         RESTAPI_utils::field_to_json(Obj, "contact", contact); | ||||
|         RESTAPI_utils::field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json(Obj, "serialNumber", serialNumber); | ||||
|         field_to_json(Obj, "venue", venue); | ||||
|         field_to_json(Obj, "entity", entity); | ||||
|         field_to_json(Obj, "subscriber", subscriber); | ||||
|         field_to_json(Obj, "deviceType", deviceType); | ||||
|         field_to_json(Obj, "qrCode", qrCode); | ||||
|         field_to_json(Obj, "geoCode", geoCode); | ||||
|         field_to_json(Obj, "location", location); | ||||
|         field_to_json(Obj, "contact", contact); | ||||
|         field_to_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|     } | ||||
|  | ||||
|     bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json( Obj,"serialNumber",serialNumber); | ||||
|             RESTAPI_utils::field_from_json( Obj,"venue",venue); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entity",entity); | ||||
|             RESTAPI_utils::field_from_json( Obj,"subscriber",subscriber); | ||||
|             RESTAPI_utils::field_from_json( Obj,"deviceType",deviceType); | ||||
|             RESTAPI_utils::field_from_json(Obj, "qrCode", qrCode); | ||||
|             RESTAPI_utils::field_from_json( Obj,"geoCode",geoCode); | ||||
|             RESTAPI_utils::field_from_json( Obj,"location",location); | ||||
|             RESTAPI_utils::field_from_json( Obj,"contact",contact); | ||||
|             RESTAPI_utils::field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"serialNumber",serialNumber); | ||||
|             field_from_json( Obj,"venue",venue); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"subscriber",subscriber); | ||||
|             field_from_json( Obj,"deviceType",deviceType); | ||||
|             field_from_json(Obj, "qrCode", qrCode); | ||||
|             field_from_json( Obj,"geoCode",geoCode); | ||||
|             field_from_json( Obj,"location",location); | ||||
|             field_from_json( Obj,"contact",contact); | ||||
|             field_from_json( Obj,"deviceConfiguration",deviceConfiguration); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -331,18 +334,18 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void DeviceConfigurationElement::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json( Obj,"name", name); | ||||
|         RESTAPI_utils::field_to_json( Obj,"description", description); | ||||
|         RESTAPI_utils::field_to_json( Obj,"weight", weight); | ||||
|         RESTAPI_utils::field_to_json( Obj,"configuration", configuration); | ||||
|         field_to_json( Obj,"name", name); | ||||
|         field_to_json( Obj,"description", description); | ||||
|         field_to_json( Obj,"weight", weight); | ||||
|         field_to_json( Obj,"configuration", configuration); | ||||
|     } | ||||
|  | ||||
|     bool DeviceConfigurationElement::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"name",name); | ||||
|             RESTAPI_utils::field_from_json( Obj,"description",description); | ||||
|             RESTAPI_utils::field_from_json( Obj,"weight",weight); | ||||
|             RESTAPI_utils::field_from_json( Obj,"configuration",configuration); | ||||
|             field_from_json( Obj,"name",name); | ||||
|             field_from_json( Obj,"description",description); | ||||
|             field_from_json( Obj,"weight",weight); | ||||
|             field_from_json( Obj,"configuration",configuration); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -352,27 +355,27 @@ namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     void DeviceConfiguration::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         RESTAPI_utils::field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         RESTAPI_utils::field_to_json( Obj,"deviceTypes",deviceTypes); | ||||
|         RESTAPI_utils::field_to_json( Obj,"configuration",configuration); | ||||
|         RESTAPI_utils::field_to_json( Obj,"inUse",inUse); | ||||
|         RESTAPI_utils::field_to_json( Obj,"variables",variables); | ||||
|         RESTAPI_utils::field_to_json( Obj,"rrm",rrm); | ||||
|         RESTAPI_utils::field_to_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|         RESTAPI_utils::field_to_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|         field_to_json( Obj,"managementPolicy",managementPolicy); | ||||
|         field_to_json( Obj,"deviceTypes",deviceTypes); | ||||
|         field_to_json( Obj,"configuration",configuration); | ||||
|         field_to_json( Obj,"inUse",inUse); | ||||
|         field_to_json( Obj,"variables",variables); | ||||
|         field_to_json( Obj,"rrm",rrm); | ||||
|         field_to_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|         field_to_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|     } | ||||
|  | ||||
|     bool DeviceConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             RESTAPI_utils::field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             RESTAPI_utils::field_from_json( Obj,"deviceTypes",deviceTypes); | ||||
|             RESTAPI_utils::field_from_json( Obj,"configuration",configuration); | ||||
|             RESTAPI_utils::field_from_json( Obj,"inUse",inUse); | ||||
|             RESTAPI_utils::field_from_json( Obj,"variables",variables); | ||||
|             RESTAPI_utils::field_from_json( Obj,"rrm",rrm); | ||||
|             RESTAPI_utils::field_from_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|             RESTAPI_utils::field_from_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|             field_from_json( Obj,"managementPolicy",managementPolicy); | ||||
|             field_from_json( Obj,"deviceTypes",deviceTypes); | ||||
|             field_from_json( Obj,"configuration",configuration); | ||||
|             field_from_json( Obj,"inUse",inUse); | ||||
|             field_from_json( Obj,"variables",variables); | ||||
|             field_from_json( Obj,"rrm",rrm); | ||||
|             field_from_json( Obj,"firmwareUpgrade",firmwareUpgrade); | ||||
|             field_from_json( Obj,"firmwareRCOnly",firmwareRCOnly); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -381,8 +384,8 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void Report::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json(Obj, "snapshot", snapShot); | ||||
|         RESTAPI_utils::field_to_json(Obj, "devices", tenants); | ||||
|         field_to_json(Obj, "snapshot", snapShot); | ||||
|         field_to_json(Obj, "devices", tenants); | ||||
|     }; | ||||
|  | ||||
|     void Report::reset() { | ||||
| @@ -390,16 +393,16 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntry::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json(Obj, "uuid", uuid); | ||||
|         RESTAPI_utils::field_to_json(Obj, "name", name); | ||||
|         RESTAPI_utils::field_to_json(Obj, "description", description); | ||||
|         field_to_json(Obj, "uuid", uuid); | ||||
|         field_to_json(Obj, "name", name); | ||||
|         field_to_json(Obj, "description", description); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntry::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"uuid",uuid); | ||||
|             RESTAPI_utils::field_from_json( Obj,"name",name); | ||||
|             RESTAPI_utils::field_from_json( Obj,"description",description); | ||||
|             field_from_json( Obj,"uuid",uuid); | ||||
|             field_from_json( Obj,"name",name); | ||||
|             field_from_json( Obj,"description",description); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -408,14 +411,14 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntryList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json(Obj, "type", type); | ||||
|         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||
|         field_to_json(Obj, "type", type); | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"type",type); | ||||
|             RESTAPI_utils::field_from_json( Obj,"entries",entries); | ||||
|             field_from_json( Obj,"type",type); | ||||
|             field_from_json( Obj,"entries",entries); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -424,12 +427,94 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     void ExpandedUseEntryMapList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         RESTAPI_utils::field_to_json(Obj, "entries", entries); | ||||
|         field_to_json(Obj, "entries", entries); | ||||
|     } | ||||
|  | ||||
|     bool ExpandedUseEntryMapList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             RESTAPI_utils::field_from_json( Obj,"entries",entries); | ||||
|             field_from_json( Obj,"entries",entries); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void UserList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "list", list); | ||||
|     } | ||||
|  | ||||
|     bool UserList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "list", list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void ObjectACL::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "users", users); | ||||
|         field_to_json(Obj, "access", access); | ||||
|     } | ||||
|  | ||||
|     bool ObjectACL::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "users", users); | ||||
|             field_from_json(Obj, "access", access); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     void ObjectACLList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json(Obj, "list", list); | ||||
|     } | ||||
|  | ||||
|     bool ObjectACLList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj, "list", list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void Map::to_json(Poco::JSON::Object &Obj) const { | ||||
|         info.to_json(Obj); | ||||
|         field_to_json( Obj,"data",data); | ||||
|         field_to_json( Obj,"entity",entity); | ||||
|         field_to_json( Obj,"creator",creator); | ||||
|         field_to_json( Obj,"visibility",visibility); | ||||
|         field_to_json( Obj,"access",access); | ||||
|     } | ||||
|  | ||||
|     bool Map::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             info.from_json(Obj); | ||||
|             field_from_json( Obj,"data",data); | ||||
|             field_from_json( Obj,"entity",entity); | ||||
|             field_from_json( Obj,"creator",creator); | ||||
|             field_from_json( Obj,"visibility",visibility); | ||||
|             field_from_json( Obj,"access",access); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     void MapList::to_json(Poco::JSON::Object &Obj) const { | ||||
|         field_to_json( Obj,"list",list); | ||||
|     } | ||||
|  | ||||
|     bool MapList::from_json(const Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json( Obj,"list",list); | ||||
|             return true; | ||||
|         } catch(...) { | ||||
|  | ||||
| @@ -438,13 +523,47 @@ namespace OpenWifi::ProvObjects { | ||||
|     } | ||||
|  | ||||
|     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = std::time(nullptr); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|         if(O->has("description")) | ||||
|  | ||||
|         if(I.name.empty()) | ||||
|             return false; | ||||
|  | ||||
|        if(O->has("description")) | ||||
|             I.description = O->get("description").toString(); | ||||
|         SecurityObjects::MergeNotes(O,U,I.notes); | ||||
|         I.modified = std::time(nullptr); | ||||
|         SecurityObjects::NoteInfoVec N; | ||||
|         for(auto &i:I.notes) { | ||||
|             if(i.note.empty()) | ||||
|                 continue; | ||||
|             N.push_back(SecurityObjects::NoteInfo{.created=Now,.createdBy=U.email,.note=i.note}); | ||||
|         } | ||||
|         I.modified = Now; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) { | ||||
|         uint64_t Now = std::time(nullptr); | ||||
|         if(O->has("name")) | ||||
|             I.name = O->get("name").toString(); | ||||
|  | ||||
|         if(I.name.empty()) | ||||
|             return false; | ||||
|  | ||||
|         if(O->has("description")) | ||||
|             I.description = O->get("description").toString(); | ||||
|  | ||||
|         SecurityObjects::NoteInfoVec N; | ||||
|         for(auto &i:I.notes) { | ||||
|             if(i.note.empty()) | ||||
|                 continue; | ||||
|             N.push_back(SecurityObjects::NoteInfo{.created=Now,.createdBy=U.email,.note=i.note}); | ||||
|         } | ||||
|         I.notes = N; | ||||
|         I.modified = I.created = Now; | ||||
|         I.id = MicroService::instance().CreateUUID(); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -15,6 +15,13 @@ | ||||
|  | ||||
| namespace OpenWifi::ProvObjects { | ||||
|  | ||||
|     enum FIRMWARE_UPGRADE_RULES { | ||||
|         dont_upgrade, | ||||
|         upgrade_inherit, | ||||
|         upgrade_release_only, | ||||
|         upgrade_latest | ||||
|     }; | ||||
|  | ||||
|     struct ObjectInfo { | ||||
|         Types::UUID_t   id; | ||||
|         std::string     name; | ||||
| @@ -317,7 +324,50 @@ namespace OpenWifi::ProvObjects { | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct UserList { | ||||
|         std::vector<std::string>    list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ObjectACL { | ||||
|         UserList        users; | ||||
|         std::string     access; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct ObjectACLList { | ||||
|         std::vector<ObjectACL>  list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct Map { | ||||
|         ObjectInfo          info; | ||||
|         std::string         data; | ||||
|         std::string         entity; | ||||
|         std::string         creator; | ||||
|         std::string         visibility; | ||||
|         ObjectACLList       access; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     struct MapList { | ||||
|         std::vector<Map>    list; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(const Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
|     bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||
|     bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I); | ||||
|  | ||||
| }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -138,7 +138,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    field_to_json(Obj,"primary", primary); | ||||
| 	} | ||||
|  | ||||
| 	bool MobilePhoneNumber::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool MobilePhoneNumber::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 	    try { | ||||
| 	        field_from_json(Obj,"number",number); | ||||
| 	        field_from_json(Obj,"verified",verified); | ||||
| @@ -155,7 +155,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    field_to_json(Obj,"method", method); | ||||
| 	} | ||||
|  | ||||
| 	bool MfaAuthInfo::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool MfaAuthInfo::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 	    try { | ||||
| 	        field_from_json(Obj,"enabled",enabled); | ||||
| 	        field_from_json(Obj,"method",method); | ||||
| @@ -171,7 +171,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    field_to_json(Obj, "mfa", mfa); | ||||
| 	} | ||||
|  | ||||
| 	bool UserLoginLoginExtensions::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool UserLoginLoginExtensions::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 	    try { | ||||
| 	        field_from_json(Obj,"mobiles",mobiles); | ||||
| 	        field_from_json(Obj,"mfa",mfa); | ||||
| @@ -189,7 +189,7 @@ namespace OpenWifi::SecurityObjects { | ||||
|         field_to_json(Obj, "method", method); | ||||
|     } | ||||
|  | ||||
|     bool MFAChallengeRequest::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
|     bool MFAChallengeRequest::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 	    try { | ||||
| 	        field_from_json(Obj,"uuid",uuid); | ||||
| 	        field_from_json(Obj,"question",question); | ||||
| @@ -208,7 +208,7 @@ namespace OpenWifi::SecurityObjects { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     bool MFAChallengeResponse::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
|     bool MFAChallengeResponse::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
|         try { | ||||
|             field_from_json(Obj,"uuid",uuid); | ||||
|             field_from_json(Obj,"answer",answer); | ||||
| @@ -387,11 +387,12 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		field_to_json(Obj,"note", note); | ||||
| 	} | ||||
|  | ||||
| 	bool NoteInfo::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool NoteInfo::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"created",created); | ||||
| 			field_from_json(Obj,"createdBy",createdBy); | ||||
| 			field_from_json(Obj,"note",note); | ||||
| 			return true; | ||||
| 		} catch(...) { | ||||
|  | ||||
| 		} | ||||
| @@ -428,10 +429,11 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		field_to_json<ResourceAccessType>(Obj,"access", access, ResourceAccessTypeToString); | ||||
| 	} | ||||
|  | ||||
| 	bool ProfileAction::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool ProfileAction::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"resource",resource); | ||||
| 			field_from_json<ResourceAccessType>(Obj,"access",access,ResourceAccessTypeFromString ); | ||||
| 			return true; | ||||
| 		} catch(...) { | ||||
|  | ||||
| 		} | ||||
| @@ -447,7 +449,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		field_to_json(Obj,"notes", notes); | ||||
| 	} | ||||
|  | ||||
| 	bool SecurityProfile::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool SecurityProfile::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"id",id); | ||||
| 			field_from_json(Obj,"name",name); | ||||
| @@ -455,6 +457,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 			field_from_json(Obj,"policy",policy); | ||||
| 			field_from_json(Obj,"role",role); | ||||
| 			field_from_json(Obj,"notes",notes); | ||||
| 			return true; | ||||
| 		} catch(...) { | ||||
|  | ||||
| 		} | ||||
| @@ -465,13 +468,51 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		field_to_json(Obj, "profiles", profiles); | ||||
| 	} | ||||
|  | ||||
| 	bool SecurityProfileList::from_json(Poco::JSON::Object::Ptr Obj) { | ||||
| 	bool SecurityProfileList::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 		try { | ||||
| 			field_from_json(Obj,"profiles",profiles); | ||||
| 			return true; | ||||
| 		} catch(...) { | ||||
|  | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
|     void ActionLink::to_json(Poco::JSON::Object &Obj) const { | ||||
| 	    field_to_json(Obj,"id",id); | ||||
| 	    field_to_json(Obj,"action",action); | ||||
| 	    field_to_json(Obj,"userId",userId); | ||||
| 	    field_to_json(Obj,"actionTemplate",actionTemplate); | ||||
| 	    field_to_json(Obj,"variables",variables); | ||||
| 	    field_to_json(Obj,"locale",locale); | ||||
| 	    field_to_json(Obj,"message",message); | ||||
| 	    field_to_json(Obj,"sent",sent); | ||||
| 	    field_to_json(Obj,"created",created); | ||||
| 	    field_to_json(Obj,"expires",expires); | ||||
| 	    field_to_json(Obj,"completed",completed); | ||||
| 	    field_to_json(Obj,"canceled",canceled); | ||||
|  | ||||
| 	} | ||||
|  | ||||
|     bool ActionLink::from_json(Poco::JSON::Object::Ptr &Obj) { | ||||
| 	    try { | ||||
| 	        field_from_json(Obj,"id",id); | ||||
| 	        field_from_json(Obj,"action",action); | ||||
| 	        field_from_json(Obj,"userId",userId); | ||||
| 	        field_from_json(Obj,"actionTemplate",actionTemplate); | ||||
| 	        field_from_json(Obj,"variables",variables); | ||||
| 	        field_from_json(Obj,"locale",locale); | ||||
| 	        field_from_json(Obj,"message",message); | ||||
| 	        field_from_json(Obj,"sent",sent); | ||||
| 	        field_from_json(Obj,"created",created); | ||||
| 	        field_from_json(Obj,"expires",expires); | ||||
| 	        field_from_json(Obj,"completed",completed); | ||||
| 	        field_from_json(Obj,"canceled",canceled); | ||||
| 	        return true; | ||||
| 	    } catch(...) { | ||||
|  | ||||
| 	    } | ||||
| 	    return false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| #define UCENTRAL_RESTAPI_SECURITYOBJECTS_H | ||||
|  | ||||
| #include "Poco/JSON/Object.h" | ||||
| #include "../framework/OpenWifiTypes.h" | ||||
| #include "framework/OpenWifiTypes.h" | ||||
|  | ||||
| namespace OpenWifi::SecurityObjects { | ||||
|  | ||||
| @@ -53,7 +53,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		std::string createdBy; | ||||
| 		std::string note; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| 	typedef std::vector<NoteInfo>	NoteInfoVec; | ||||
|  | ||||
| @@ -63,7 +63,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    bool primary; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct MfaAuthInfo { | ||||
| @@ -71,7 +71,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    std::string method; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct UserLoginLoginExtensions { | ||||
| @@ -79,7 +79,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    struct MfaAuthInfo mfa; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	struct MFAChallengeRequest { | ||||
| @@ -89,7 +89,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 	    uint64_t    created; | ||||
|  | ||||
| 	    void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
|     struct MFAChallengeResponse { | ||||
| @@ -97,7 +97,7 @@ namespace OpenWifi::SecurityObjects { | ||||
|         std::string answer; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
|         bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
|         bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
|     }; | ||||
|  | ||||
| 	struct UserInfo { | ||||
| @@ -200,7 +200,7 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		std::string resource; | ||||
| 		ResourceAccessType access; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| 	typedef std::vector<ProfileAction>	ProfileActionVec; | ||||
|  | ||||
| @@ -212,14 +212,37 @@ namespace OpenWifi::SecurityObjects { | ||||
| 		std::string role; | ||||
| 		NoteInfoVec notes; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| 	typedef std::vector<SecurityProfile> SecurityProfileVec; | ||||
|  | ||||
| 	struct SecurityProfileList { | ||||
| 		SecurityProfileVec profiles; | ||||
| 		void to_json(Poco::JSON::Object &Obj) const; | ||||
| 		bool from_json(Poco::JSON::Object::Ptr Obj); | ||||
| 		bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
|  | ||||
| 	enum LinkActions { | ||||
| 	    FORGOT_PASSWORD=1, | ||||
| 	    VERIFY_EMAIL | ||||
| 	}; | ||||
|  | ||||
| 	struct ActionLink { | ||||
| 	    std::string         id; | ||||
| 	    uint64_t            action; | ||||
| 	    std::string         userId; | ||||
| 	    std::string         actionTemplate; | ||||
| 	    Types::StringPairVec variables; | ||||
| 	    std::string         locale; | ||||
| 	    std::string         message; | ||||
| 	    uint64_t            sent=0; | ||||
| 	    uint64_t            created=std::time(nullptr); | ||||
| 	    uint64_t            expires=0; | ||||
| 	    uint64_t            completed=0; | ||||
| 	    uint64_t            canceled=0; | ||||
|  | ||||
|         void to_json(Poco::JSON::Object &Obj) const; | ||||
| 	    bool from_json(Poco::JSON::Object::Ptr &Obj); | ||||
| 	}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,6 @@ | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     class SMSSender * SMSSender::instance_ = nullptr; | ||||
|  | ||||
|     int SMSSender::Start() { | ||||
|         Provider_ = MicroService::instance().ConfigGetString("sms.provider","aws"); | ||||
|   | ||||
| @@ -25,10 +25,8 @@ namespace OpenWifi { | ||||
|     class SMSSender : public SubSystemServer { | ||||
|         public: | ||||
|             static SMSSender *instance() { | ||||
|                 if (instance_ == nullptr) { | ||||
|                     instance_ = new SMSSender; | ||||
|                 } | ||||
|                 return instance_; | ||||
|                 static SMSSender instance; | ||||
|                 return &instance; | ||||
|             } | ||||
|  | ||||
|             int  Start() final; | ||||
| @@ -39,7 +37,6 @@ namespace OpenWifi { | ||||
|             bool IsNumberValid(const std::string &Number, const std::string &UserName); | ||||
|             [[nodiscard]] bool Send(const std::string &PhoneNumber, const std::string &Message); | ||||
|         private: | ||||
|             static SMSSender * instance_; | ||||
|             std::string         Provider_; | ||||
|             bool                Enabled_=false; | ||||
|             std::vector<SMSValidationCacheEntry>    Cache_; | ||||
|   | ||||
| @@ -18,11 +18,10 @@ | ||||
|  | ||||
| #include "SMTPMailerService.h" | ||||
| #include "framework/MicroService.h" | ||||
| #include "AuthService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class SMTPMailerService * SMTPMailerService::instance_ = nullptr; | ||||
|  | ||||
|     void SMTPMailerService::LoadMyConfig() { | ||||
|         MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); | ||||
|         SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); | ||||
| @@ -147,7 +146,7 @@ namespace OpenWifi { | ||||
|  | ||||
|             auto Logo = Msg.Attrs.find(LOGO); | ||||
|             if(Logo!=Msg.Attrs.end()) { | ||||
|                 Poco::File  LogoFile(TemplateDir_ + "/" + Logo->second); | ||||
|                 Poco::File  LogoFile(AuthService::GetLogoAssetFileName()); | ||||
|                 std::ifstream   IF(LogoFile.path()); | ||||
|                 std::ostringstream   OS; | ||||
|                 Poco::StreamCopier::copyStream(IF, OS); | ||||
|   | ||||
| @@ -59,10 +59,8 @@ namespace OpenWifi { | ||||
|     class SMTPMailerService : public SubSystemServer, Poco::Runnable { | ||||
|         public: | ||||
|            static SMTPMailerService *instance() { | ||||
|                 if (instance_ == nullptr) { | ||||
|                     instance_ = new SMTPMailerService; | ||||
|                 } | ||||
|                 return instance_; | ||||
|                static SMTPMailerService     instance; | ||||
|                return & instance; | ||||
|             } | ||||
|  | ||||
|             struct MessageEvent { | ||||
| @@ -88,7 +86,6 @@ namespace OpenWifi { | ||||
|             void reinitialize(Poco::Util::Application &self) override; | ||||
|             bool Enabled() const { return Enabled_; } | ||||
|         private: | ||||
|             static SMTPMailerService * instance_; | ||||
|             std::string             MailHost_; | ||||
|             std::string             Sender_; | ||||
|             int                     MailHostPort_=25; | ||||
|   | ||||
| @@ -10,13 +10,12 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     class Storage *Storage::instance_ = nullptr; | ||||
|  | ||||
|     int Storage::Start() { | ||||
| 		std::lock_guard		Guard(Mutex_); | ||||
|  | ||||
| 		StorageClass::Start(); | ||||
| 		Create_Tables(); | ||||
| 		InitializeDefaultUser(); | ||||
| 		return 0; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -15,34 +15,6 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static const std::string AllActionLinksFieldsForSelect { | ||||
|             "Id, " | ||||
|             "Action," | ||||
|             "UserId," | ||||
|             "template," | ||||
|             "locale," | ||||
|             "message," | ||||
|             "sent," | ||||
|             "created," | ||||
|             "expires," | ||||
|             "completed," | ||||
|             "canceled" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllActionLinksFieldsForUpdate { | ||||
|             "Id=?, " | ||||
|             "Action=?," | ||||
|             "UserId=?," | ||||
|             "template=?," | ||||
|             "locale=?," | ||||
|             "message=?," | ||||
|             "sent=?," | ||||
|             "created=?," | ||||
|             "expires=?," | ||||
|             "completed=?," | ||||
|             "canceled=?" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllEmailTemplatesFieldsForCreation { | ||||
|  | ||||
|     }; | ||||
| @@ -90,7 +62,7 @@ namespace OpenWifi { | ||||
|             return UNKNOWN; | ||||
|         } | ||||
|  | ||||
|         static const std::string from_userType(USER_TYPE U) { | ||||
|         static std::string from_userType(USER_TYPE U) { | ||||
|             switch(U) { | ||||
|                 case ROOT: return "root"; | ||||
|                 case ADMIN: return "admin"; | ||||
| @@ -104,10 +76,8 @@ namespace OpenWifi { | ||||
|         } | ||||
|  | ||||
|         static Storage *instance() { | ||||
|             if (instance_ == nullptr) { | ||||
|                 instance_ = new Storage; | ||||
|             } | ||||
|             return instance_; | ||||
|             static Storage instance; | ||||
|             return &instance; | ||||
|         } | ||||
|  | ||||
|         int 	Start() override; | ||||
| @@ -116,7 +86,8 @@ namespace OpenWifi { | ||||
|         /* | ||||
|          *  All user management functions | ||||
|          */ | ||||
|         bool CreateUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser); | ||||
|         bool InitializeDefaultUser(); | ||||
|         bool CreateUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser, bool PasswordHashedAlready = false); | ||||
|         bool GetUserByEmail(std::string & email, SecurityObjects::UserInfo & User); | ||||
|         bool GetUserById(USER_ID_TYPE & Id, SecurityObjects::UserInfo & User); | ||||
|         bool DeleteUser(const std::string & Admin, USER_ID_TYPE & Id); | ||||
| @@ -143,18 +114,20 @@ namespace OpenWifi { | ||||
|         /* | ||||
|          *  All ActionLinks functions | ||||
|          */ | ||||
|         bool CreateAction(std::string &ActionId, std::string &Action, USER_ID_TYPE & Id, Types::StringPairVec & Elements ); | ||||
|         bool CreateAction( SecurityObjects::ActionLink & A); | ||||
|         bool DeleteAction(std::string &ActionId); | ||||
|         bool CompleteAction(std::string &ActionId); | ||||
|         bool CancelAction(std::string &ActionId); | ||||
|         bool SentAction(std::string &ActionId); | ||||
|         bool GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A); | ||||
|         bool GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max=200); | ||||
|  | ||||
| 	  private: | ||||
| 		static Storage      							*instance_; | ||||
|  | ||||
|         int Create_Tables(); | ||||
|         int Create_UserTable(); | ||||
|         int Create_AvatarTable(); | ||||
|         int Create_TokensTable(); | ||||
|         int Create_ActionLinkTable(); | ||||
|    }; | ||||
|  | ||||
|     inline Storage * StorageService() { return Storage::instance(); }; | ||||
|   | ||||
| @@ -57,6 +57,7 @@ using namespace std::chrono_literals; | ||||
| #include "Poco/URI.h" | ||||
| #include "Poco/Net/HTTPSClientSession.h" | ||||
| #include "Poco/Net/NetworkInterface.h" | ||||
| #include "Poco/ExpireLRUCache.h" | ||||
|  | ||||
| #include "cppkafka/cppkafka.h" | ||||
|  | ||||
| @@ -66,6 +67,86 @@ using namespace std::chrono_literals; | ||||
| #include "framework/RESTAPI_errors.h" | ||||
| #include "framework/uCentral_Protocol.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "nlohmann/json.hpp" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     enum UNAUTHORIZED_REASON { | ||||
|         SUCCESS=0, | ||||
|         PASSWORD_CHANGE_REQUIRED, | ||||
|         INVALID_CREDENTIALS, | ||||
|         PASSWORD_ALREADY_USED, | ||||
|         USERNAME_PENDING_VERIFICATION, | ||||
|         PASSWORD_INVALID, | ||||
|         INTERNAL_ERROR, | ||||
|         ACCESS_DENIED, | ||||
|         INVALID_TOKEN | ||||
|     }; | ||||
|  | ||||
| 	class AppServiceRegistry { | ||||
| 	  public: | ||||
| 		inline AppServiceRegistry(); | ||||
|  | ||||
| 		static AppServiceRegistry & instance() { | ||||
| 			static AppServiceRegistry instance; | ||||
| 			return instance; | ||||
| 		} | ||||
|  | ||||
| 		inline ~AppServiceRegistry() { | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Save() { | ||||
| 			std::istringstream  IS( to_string(Registry_)); | ||||
| 			std::ofstream       OF; | ||||
| 			OF.open(FileName,std::ios::binary | std::ios::trunc); | ||||
| 			Poco::StreamCopier::copyStream(IS, OF); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, uint64_t Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, const std::string &Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline void Set(const char *Key, bool Value ) { | ||||
| 			Registry_[Key] = Value; | ||||
| 			Save(); | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, bool & Value ) { | ||||
| 			if(Registry_[Key].is_boolean()) { | ||||
| 				Value = Registry_[Key].get<bool>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, uint64_t & Value ) { | ||||
| 			if(Registry_[Key].is_number_unsigned()) { | ||||
| 				Value = Registry_[Key].get<uint64_t>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		inline bool Get(const char *Key, std::string & Value ) { | ||||
| 			if(Registry_[Key].is_string()) { | ||||
| 				Value = Registry_[Key].get<std::string>(); | ||||
| 				return true; | ||||
| 			} | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 	  private: | ||||
| 		std::string         FileName; | ||||
| 		nlohmann::json      Registry_; | ||||
| 	}; | ||||
| } | ||||
|  | ||||
| namespace OpenWifi::RESTAPI_utils { | ||||
|  | ||||
| @@ -263,6 +344,21 @@ namespace OpenWifi::RESTAPI_utils { | ||||
|         return OS.str(); | ||||
|     } | ||||
|  | ||||
|     inline std::string to_string(const Types::StringPairVec & ObjectArray) { | ||||
|         Poco::JSON::Array OutputArr; | ||||
|         if(ObjectArray.empty()) | ||||
|             return "[]"; | ||||
|         for(auto const &i:ObjectArray) { | ||||
|             Poco::JSON::Array InnerArray; | ||||
|             InnerArray.add(i.first); | ||||
|             InnerArray.add(i.second); | ||||
|             OutputArr.add(InnerArray); | ||||
|         } | ||||
|         std::ostringstream OS; | ||||
|         Poco::JSON::Stringifier::condense(OutputArr,OS); | ||||
|         return OS.str(); | ||||
|     } | ||||
|  | ||||
|     template<class T> std::string to_string(const std::vector<T> & ObjectArray) { | ||||
|         Poco::JSON::Array OutputArr; | ||||
|         if(ObjectArray.empty()) | ||||
| @@ -345,6 +441,27 @@ namespace OpenWifi::RESTAPI_utils { | ||||
|         return Result; | ||||
|     } | ||||
|  | ||||
|     inline Types::StringPairVec to_stringpair_array(const std::string &S) { | ||||
|         Types::StringPairVec   R; | ||||
|         if(S.empty()) | ||||
|             return R; | ||||
|         try { | ||||
|             Poco::JSON::Parser P; | ||||
|             auto Object = P.parse(S).template extract<Poco::JSON::Array::Ptr>(); | ||||
|             for (auto const &i : *Object) { | ||||
|                 auto InnerObject = i.template extract<Poco::JSON::Array::Ptr>(); | ||||
|                 if(InnerObject->size()==2) { | ||||
|                     Types::StringPair P{InnerObject->get(0).toString(), InnerObject->get(1).toString()}; | ||||
|                     R.push_back(P); | ||||
|                 } | ||||
|             } | ||||
|         } catch (...) { | ||||
|  | ||||
|         } | ||||
|  | ||||
|         return R; | ||||
|     } | ||||
|  | ||||
|     template<class T> std::vector<T> to_object_array(const std::string & ObjectString) { | ||||
|         std::vector<T>	Result; | ||||
|         if(ObjectString.empty()) | ||||
| @@ -729,16 +846,13 @@ namespace OpenWifi::Utils { | ||||
|         return Result; | ||||
|     } | ||||
|  | ||||
|     inline void SaveSystemId(uint64_t Id); | ||||
|  | ||||
|     [[nodiscard]] inline uint64_t InitializeSystemId() { | ||||
|         std::random_device	RDev; | ||||
|         std::srand(RDev()); | ||||
|         std::chrono::high_resolution_clock	Clock; | ||||
|         auto Now = Clock.now().time_since_epoch().count(); | ||||
|         auto S = (GetDefaultMacAsInt64() + std::rand() + Now)  ; | ||||
|         SaveSystemId(S); | ||||
|         std::cout << "ID: " << S << std::endl; | ||||
| 		OpenWifi::AppServiceRegistry().Set("systemid",S); | ||||
|         return S; | ||||
|     } | ||||
|  | ||||
| @@ -887,6 +1001,7 @@ namespace OpenWifi { | ||||
|     static const std::string uSERVICE_SUBCRIBER{ "owsub"}; | ||||
|     static const std::string uSERVICE_INSTALLER{ "owinst"}; | ||||
|  | ||||
|  | ||||
| 	class MyErrorHandler : public Poco::ErrorHandler { | ||||
| 	  public: | ||||
| 		explicit MyErrorHandler(Poco::Util::Application &App) : App_(App) {} | ||||
| @@ -1309,6 +1424,63 @@ namespace OpenWifi { | ||||
| 	            std::string _fileName; | ||||
|     }; | ||||
|  | ||||
| 	class RESTAPI_RateLimiter : public SubSystemServer { | ||||
| 	public: | ||||
|  | ||||
| 	    struct ClientCacheEntry { | ||||
| 	        int64_t  Start=0; | ||||
| 	        int      Count=0; | ||||
| 	    }; | ||||
|  | ||||
| 	    static RESTAPI_RateLimiter *instance() { | ||||
| 	        static RESTAPI_RateLimiter instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() final { return 0;}; | ||||
| 	    inline void Stop() final { }; | ||||
|  | ||||
| 	    inline bool IsRateLimited(const Poco::Net::HTTPServerRequest &R, int64_t Period, int64_t MaxCalls) { | ||||
| 	        Poco::URI   uri(R.getURI()); | ||||
| 	        auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); | ||||
| 	        auto E = Cache_.get(H); | ||||
| 	        const auto p1 = std::chrono::system_clock::now(); | ||||
| 	        auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(p1.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) | ||||
| 	                return true; | ||||
| 	            return false; | ||||
| 	        } | ||||
| 	        E->Start = Now; | ||||
| 	        E->Count = 1; | ||||
| 	        Cache_.update(H,E); | ||||
| 	        return false; | ||||
| 	    } | ||||
|  | ||||
| 	    inline void Clear() { | ||||
| 	        Cache_.clear(); | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    Poco::ExpireLRUCache<uint64_t,ClientCacheEntry>      Cache_{2048}; | ||||
| 	    std::hash<std::string>          str_hash; | ||||
|  | ||||
| 	    RESTAPI_RateLimiter() noexcept: | ||||
| 	    SubSystemServer("RateLimiter", "RATE-LIMITER", "rate.limiter") | ||||
| 	    { | ||||
| 	    } | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	inline RESTAPI_RateLimiter * RESTAPI_RateLimiter() { return RESTAPI_RateLimiter::instance(); } | ||||
|  | ||||
| 	class RESTAPIHandler : public Poco::Net::HTTPRequestHandler { | ||||
| 	public: | ||||
| 	    struct QueryBlock { | ||||
| @@ -1318,8 +1490,28 @@ namespace OpenWifi { | ||||
| 	    }; | ||||
| 	    typedef std::map<std::string, std::string> BindingMap; | ||||
|  | ||||
| 	    RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods, RESTAPI_GenericServer & Server, bool Internal=false, bool AlwaysAuthorize=true) | ||||
| 	    : Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Server_(Server), Internal_(Internal), AlwaysAuthorize_(AlwaysAuthorize) {} | ||||
| 	    struct RateLimit { | ||||
| 	        int64_t     Interval=1000; | ||||
| 	        int64_t     MaxCalls=10; | ||||
| 	    }; | ||||
|  | ||||
| 	    RESTAPIHandler( BindingMap map, | ||||
|                         Poco::Logger &l, | ||||
|                         std::vector<std::string> Methods, | ||||
|                         RESTAPI_GenericServer & Server, | ||||
|                         bool Internal=false, | ||||
|                         bool AlwaysAuthorize=true, | ||||
|                         bool RateLimited=false, | ||||
| 	                    const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100}) | ||||
| 	    :   Bindings_(std::move(map)), | ||||
| 	        Logger_(l), | ||||
| 	        Methods_(std::move(Methods)), | ||||
| 	        Server_(Server), | ||||
| 	        Internal_(Internal), | ||||
| 	        AlwaysAuthorize_(AlwaysAuthorize), | ||||
| 	        RateLimited_(RateLimited), | ||||
| 	        MyRates_(Profile){ | ||||
| 	    } | ||||
|  | ||||
| 	    inline bool RoleIsAuthorized(const std::string & Path, const std::string & Method, std::string & Reason) { | ||||
| 	        return true; | ||||
| @@ -1331,6 +1523,9 @@ namespace OpenWifi { | ||||
| 	            Request = &RequestIn; | ||||
| 	            Response = &ResponseIn; | ||||
|  | ||||
| 	            if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) | ||||
| 	                return; | ||||
|  | ||||
| 	            if (!ContinueProcessing()) | ||||
| 	                return; | ||||
|  | ||||
| @@ -1340,7 +1535,7 @@ namespace OpenWifi { | ||||
|  | ||||
| 	            std::string Reason; | ||||
| 	            if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { | ||||
|                     UnAuthorized(Reason); | ||||
|                     UnAuthorized(Reason, ACCESS_DENIED); | ||||
|                     return; | ||||
| 	            } | ||||
|  | ||||
| @@ -1561,10 +1756,10 @@ namespace OpenWifi { | ||||
| 	        Poco::JSON::Stringifier::stringify(ErrorObject, Answer); | ||||
| 	    } | ||||
|  | ||||
| 	    inline void UnAuthorized(const std::string & Reason = "") { | ||||
| 	    inline void UnAuthorized(const std::string & Reason = "", int Code = INVALID_CREDENTIALS ) { | ||||
| 	        PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); | ||||
| 	        Poco::JSON::Object	ErrorObject; | ||||
| 	        ErrorObject.set("ErrorCode",403); | ||||
| 	        ErrorObject.set("ErrorCode",Code); | ||||
| 	        ErrorObject.set("ErrorDetails",Request->getMethod()); | ||||
| 	        ErrorObject.set("ErrorDescription",Reason.empty() ? "No access allowed." : Reason) ; | ||||
| 	        std::ostream &Answer = Response->send(); | ||||
| @@ -1609,7 +1804,7 @@ namespace OpenWifi { | ||||
| 	        Response->set("Cache-Control", "private"); | ||||
| 	        Response->set("Pragma", "private"); | ||||
| 	        Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 	        Response->set("Content-Length", std::to_string(File.getSize())); | ||||
| 	        Response->setContentLength(File.getSize()); | ||||
| 	        AddCORS(); | ||||
| 	        Response->sendFile(File.path(),"application/octet-stream"); | ||||
| 	    } | ||||
| @@ -1647,10 +1842,10 @@ namespace OpenWifi { | ||||
|                                      const Types::StringPairVec & FormVars) { | ||||
| 	        Response->set("Pragma", "private"); | ||||
| 	        Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT"); | ||||
| 	        Response->set("Content-Length", std::to_string(File.getSize())); | ||||
| 	        AddCORS(); | ||||
| 	        auto FormContent = Utils::LoadFile(File.path()); | ||||
| 	        std::string FormContent = Utils::LoadFile(File.path()); | ||||
| 	        Utils::ReplaceVariables(FormContent, FormVars); | ||||
| 	        Response->setContentLength(FormContent.size()); | ||||
| 	        AddCORS(); | ||||
| 	        Response->setChunkedTransferEncoding(true); | ||||
| 	        Response->setContentType("text/html"); | ||||
| 	        std::ostream& ostr = Response->send(); | ||||
| @@ -1760,12 +1955,14 @@ namespace OpenWifi { | ||||
| 	        std::vector<std::string> 	Methods_; | ||||
| 	        QueryBlock					QB_; | ||||
| 	        bool                        Internal_=false; | ||||
| 	        bool                        RateLimited_=false; | ||||
| 	        bool                        QueryBlockInitialized_=false; | ||||
| 	        Poco::Net::HTTPServerRequest    *Request= nullptr; | ||||
| 	        Poco::Net::HTTPServerResponse   *Response= nullptr; | ||||
| 	        bool                        AlwaysAuthorize_=true; | ||||
| 	        Poco::JSON::Parser          IncomingParser_; | ||||
| 	        RESTAPI_GenericServer       & Server_; | ||||
| 	        RateLimit                   MyRates_; | ||||
| 	    }; | ||||
|  | ||||
| 	    class RESTAPI_UnknownRequestHandler : public RESTAPIHandler { | ||||
| @@ -1890,9 +2087,8 @@ namespace OpenWifi { | ||||
| 	    inline void initialize(Poco::Util::Application & self) override; | ||||
|  | ||||
| 	    static KafkaManager *instance() { | ||||
| 	        if(instance_== nullptr) | ||||
| 	            instance_ = new KafkaManager; | ||||
| 	        return instance_; | ||||
| 	        static KafkaManager instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override { | ||||
| @@ -1967,7 +2163,6 @@ namespace OpenWifi { | ||||
| 	    // void WakeUp(); | ||||
|  | ||||
| 	private: | ||||
| 	    static KafkaManager 			*instance_; | ||||
| 	    std::mutex 						ProducerMutex_; | ||||
| 	    std::mutex						ConsumerMutex_; | ||||
| 	    bool 							KafkaEnabled_ = false; | ||||
| @@ -1995,7 +2190,6 @@ namespace OpenWifi { | ||||
| 	}; | ||||
|  | ||||
| 	inline KafkaManager * KafkaManager() { return KafkaManager::instance(); } | ||||
| 	inline 	class KafkaManager *KafkaManager::instance_ = nullptr; | ||||
|  | ||||
| 	class AuthClient : public SubSystemServer { | ||||
| 	public: | ||||
| @@ -2005,10 +2199,8 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	    static AuthClient *instance() { | ||||
| 	        if (instance_ == nullptr) { | ||||
| 	            instance_ = new AuthClient; | ||||
| 	        } | ||||
| 	        return instance_; | ||||
| 	        static AuthClient instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override { | ||||
| @@ -2086,12 +2278,10 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    static AuthClient 					*instance_; | ||||
| 	    OpenWifi::SecurityObjects::UserInfoCache 		UserCache_; | ||||
| 	}; | ||||
|  | ||||
| 	inline AuthClient * AuthClient() { return AuthClient::instance(); } | ||||
| 	inline class AuthClient * AuthClient::instance_ = nullptr; | ||||
|  | ||||
| 	class ALBRequestHandler: public Poco::Net::HTTPRequestHandler | ||||
| 	        /// Return a HTML document with the current date and time. | ||||
| @@ -2148,10 +2338,8 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	    static ALBHealthCheckServer *instance() { | ||||
| 	        if (instance_ == nullptr) { | ||||
| 	            instance_ = new ALBHealthCheckServer; | ||||
| 	        } | ||||
| 	        return instance_; | ||||
| 	        static ALBHealthCheckServer instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override; | ||||
| @@ -2162,14 +2350,12 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    static ALBHealthCheckServer *instance_; | ||||
| 	    std::unique_ptr<Poco::Net::HTTPServer>   	Server_; | ||||
| 	    std::unique_ptr<Poco::Net::ServerSocket> 	Socket_; | ||||
| 	    int                                     	Port_ = 0; | ||||
| 	}; | ||||
|  | ||||
| 	inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } | ||||
| 	inline class ALBHealthCheckServer * ALBHealthCheckServer::instance_ = nullptr; | ||||
|  | ||||
| 	Poco::Net::HTTPRequestHandler * RESTAPI_external_server(const char *Path, RESTAPIHandler::BindingMap &Bindings, | ||||
|                                            Poco::Logger & L, RESTAPI_GenericServer & S); | ||||
| @@ -2181,10 +2367,8 @@ namespace OpenWifi { | ||||
| 	class RESTAPI_server : public SubSystemServer { | ||||
| 	public: | ||||
| 	    static RESTAPI_server *instance() { | ||||
| 	        if (instance_ == nullptr) { | ||||
| 	            instance_ = new RESTAPI_server; | ||||
| 	        } | ||||
| 	        return instance_; | ||||
| 	        static RESTAPI_server instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
| 	    int Start() override; | ||||
| 	    inline void Stop() override { | ||||
| @@ -2202,7 +2386,6 @@ namespace OpenWifi { | ||||
| 	    } | ||||
|  | ||||
| 	private: | ||||
| 	    static RESTAPI_server *instance_; | ||||
| 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||
| 	    Poco::ThreadPool	    Pool_; | ||||
| 	    RESTAPI_GenericServer   Server_; | ||||
| @@ -2235,9 +2418,6 @@ namespace OpenWifi { | ||||
| 	    RESTAPI_GenericServer   &Server_; | ||||
| 	}; | ||||
|  | ||||
|  | ||||
| 	inline class RESTAPI_server *RESTAPI_server::instance_ = nullptr; | ||||
|  | ||||
| 	inline int RESTAPI_server::Start() { | ||||
| 	    Logger_.information("Starting."); | ||||
| 	    Server_.InitLogging(); | ||||
| @@ -2269,10 +2449,8 @@ namespace OpenWifi { | ||||
|  | ||||
| 	public: | ||||
| 	    static RESTAPI_InternalServer *instance() { | ||||
| 	        if (instance_ == nullptr) { | ||||
| 	            instance_ = new RESTAPI_InternalServer; | ||||
| 	        } | ||||
| 	        return instance_; | ||||
| 	        static RESTAPI_InternalServer instance; | ||||
| 	        return &instance; | ||||
| 	    } | ||||
|  | ||||
| 	    inline int Start() override; | ||||
| @@ -2290,7 +2468,6 @@ namespace OpenWifi { | ||||
| 	        return RESTAPI_internal_server(Path, Bindings, Logger_, Server_); | ||||
| 	    } | ||||
| 	private: | ||||
| 	    static RESTAPI_InternalServer *instance_; | ||||
| 	    std::vector<std::unique_ptr<Poco::Net::HTTPServer>>   RESTServers_; | ||||
| 	    Poco::ThreadPool	    Pool_; | ||||
| 	    RESTAPI_GenericServer   Server_; | ||||
| @@ -2301,8 +2478,6 @@ namespace OpenWifi { | ||||
|  | ||||
| 	}; | ||||
|  | ||||
| 	inline class RESTAPI_InternalServer* RESTAPI_InternalServer::instance_ = nullptr; | ||||
|  | ||||
| 	inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); }; | ||||
|  | ||||
| 	class InternalRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory { | ||||
| @@ -2357,6 +2532,8 @@ namespace OpenWifi { | ||||
| 		uint64_t 		LastUpdate=0; | ||||
| 	}; | ||||
|  | ||||
|  | ||||
|  | ||||
| 	class SubSystemServer; | ||||
| 	typedef std::map<uint64_t, MicroServiceMeta>	MicroServiceMetaMap; | ||||
| 	typedef std::vector<MicroServiceMeta>			MicroServiceMetaVec; | ||||
| @@ -2918,6 +3095,22 @@ namespace OpenWifi { | ||||
| 	    return Application::EXIT_OK; | ||||
| 	} | ||||
|  | ||||
| 	AppServiceRegistry::AppServiceRegistry() { | ||||
| 		FileName = MicroService::instance().DataDir() + "/registry.json"; | ||||
| 		Poco::File F(FileName); | ||||
|  | ||||
| 		try { | ||||
| 			if(F.exists()) { | ||||
| 				std::ostringstream  OS; | ||||
| 				std::ifstream       IF(FileName); | ||||
| 				Poco::StreamCopier::copyStream(IF, OS); | ||||
| 				Registry_ = nlohmann::json::parse(OS.str()); | ||||
| 			} | ||||
| 		} catch (...) { | ||||
| 			Registry_ = nlohmann::json::parse("{}"); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	inline void SubSystemServer::initialize(Poco::Util::Application &self) { | ||||
| 	    Logger_.notice("Initializing..."); | ||||
| 	    auto i = 0; | ||||
| @@ -3178,7 +3371,8 @@ namespace OpenWifi { | ||||
| 	                        auto InsertResult = CertNames.insert(CertFileName); | ||||
| 	                        if(InsertResult.second) { | ||||
| 	                            Poco::JSON::Object  Inner; | ||||
| 	                            Inner.set("filename", CertFileName); | ||||
| 	                            Poco::Path  F(CertFileName); | ||||
| 	                            Inner.set("filename", F.getFileName()); | ||||
| 	                            Poco::Crypto::X509Certificate   C(CertFileName); | ||||
| 	                            auto ExpiresOn = C.expiresOn(); | ||||
| 	                            Inner.set("expiresOn",ExpiresOn.timestamp().epochTime()); | ||||
| @@ -3477,7 +3671,7 @@ namespace OpenWifi { | ||||
|                                                Utils::FormatIPv6(Request->clientAddress().toString()), | ||||
|                                                Request->getMethod(), Request->getURI())); | ||||
|                 } | ||||
|                 UnAuthorized(); | ||||
|                 UnAuthorized("Invalid token", INVALID_TOKEN); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
| @@ -3487,40 +3681,14 @@ namespace OpenWifi { | ||||
| } | ||||
|  | ||||
| namespace OpenWifi::Utils { | ||||
|         inline void SaveSystemId(uint64_t Id) { | ||||
|             try { | ||||
|                 std::ofstream O; | ||||
|                 O.open(MicroService::instance().DataDir() + "/system.id",std::ios::binary | std::ios::trunc); | ||||
|                 O << Id; | ||||
|                 O.close(); | ||||
|             } catch (...) | ||||
|             { | ||||
|                 std::cout << "Could not save system ID" << std::endl; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         [[nodiscard]] inline uint64_t GetSystemId() { | ||||
|             uint64_t ID=0; | ||||
|  | ||||
|             // if the system ID file exists, open and read it. | ||||
|             Poco::File	SID( MicroService::instance().DataDir() + "/system.id"); | ||||
|             try { | ||||
|                 if (SID.exists()) { | ||||
|                     std::ifstream I; | ||||
|                     I.open(SID.path()); | ||||
|                     I >> ID; | ||||
|                     I.close(); | ||||
|                     if (ID == 0) | ||||
|                         return InitializeSystemId(); | ||||
|                     return ID; | ||||
|                 } else { | ||||
|                     return InitializeSystemId(); | ||||
|                 } | ||||
|             } catch (...) { | ||||
|                 return InitializeSystemId(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 	[[nodiscard]] inline uint64_t GetSystemId() { | ||||
| 		uint64_t ID=0; | ||||
| 		if(!AppServiceRegistry().Get("systemid",ID)) { | ||||
| 			return InitializeSystemId(); | ||||
| 		} | ||||
| 		return ID; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| namespace OpenWifi::CIDR { | ||||
|  | ||||
|   | ||||
| @@ -47,13 +47,18 @@ namespace OpenWifi::RESTAPI::Errors { | ||||
|     static const std::string IdMustBe0{"To create a user, you must set the ID to 0"}; | ||||
|     static const std::string InvalidUserRole{"Invalid userRole."}; | ||||
|     static const std::string InvalidEmailAddress{"Invalid email address."}; | ||||
|     static const std::string InvalidPassword{"Invalid password."}; | ||||
|     static const std::string PasswordRejected{"Password was rejected. This maybe an old password."}; | ||||
|     static const std::string InvalidIPRanges{"Invalid IP range specifications."}; | ||||
|     static const std::string InvalidLOrderBy{"Invalid orderBy specification."}; | ||||
|     static const std::string NeedMobileNumber{"You must provide at least one validated phone number."}; | ||||
|     static const std::string BadMFAMethod{"MFA only supports sms or email."}; | ||||
|  | ||||
|     static const std::string InvalidCredentials{"Invalid credentials (username/password)."}; | ||||
|     static const std::string InvalidPassword{"Password does not conform to basic password rules."}; | ||||
|     static const std::string UserPendingVerification{"User access denied pending email verification."}; | ||||
|     static const std::string PasswordMustBeChanged{"Password must be changed."}; | ||||
|     static const std::string UnrecognizedRequest{"Ill-formed request. Please consult documentation."}; | ||||
|     static const std::string MissingAuthenticationInformation{"Missing authentication information."}; | ||||
|     static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."}; | ||||
| } | ||||
|  | ||||
| #endif //OWPROV_RESTAPI_ERRORS_H | ||||
|   | ||||
| @@ -103,8 +103,6 @@ namespace OpenWifi { | ||||
|         DBType                                              dbType_ = sqlite; | ||||
|     }; | ||||
|  | ||||
| //    inline StorageClass * Storage() { return StorageClass::instance(); } | ||||
|  | ||||
| #ifdef	SMALL_BUILD | ||||
|     int Service::Setup_MySQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||
|     int Service::Setup_PostgreSQL() { Daemon()->exit(Poco::Util::Application::EXIT_CONFIG); return 0; } | ||||
|   | ||||
							
								
								
									
										186
									
								
								src/storage/storage_actionLinks.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/storage/storage_actionLinks.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,186 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #include "storage_actionLinks.h" | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "StorageService.h" | ||||
| #include "RESTObjects/RESTAPI_SecurityObjects.h" | ||||
| #include "framework/MicroService.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     bool Convert(const ActionLinkRecord &T, SecurityObjects::ActionLink &U) { | ||||
|         U.id = T.get<0>(); | ||||
|         U.action = T.get<1>(); | ||||
|         U.userId = T.get<2>(); | ||||
|         U.actionTemplate = T.get<3>(); | ||||
|         U.variables = RESTAPI_utils::to_stringpair_array(T.get<4>()); | ||||
|         U.locale = T.get<5>(); | ||||
|         U.message = T.get<6>(); | ||||
|         U.sent = T.get<7>(); | ||||
|         U.created = T.get<8>(); | ||||
|         U.expires = T.get<9>(); | ||||
|         U.completed = T.get<10>(); | ||||
|         U.canceled = T.get<11>(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool Convert(const SecurityObjects::ActionLink &U, ActionLinkRecord &T) { | ||||
|         T.set<0>(U.id); | ||||
|         T.set<1>(U.action); | ||||
|         T.set<2>(U.userId); | ||||
|         T.set<3>(U.actionTemplate); | ||||
|         T.set<4>(RESTAPI_utils::to_string(U.variables)); | ||||
|         T.set<5>(U.locale); | ||||
|         T.set<6>(U.message); | ||||
|         T.set<7>(U.sent); | ||||
|         T.set<8>(U.created); | ||||
|         T.set<9>(U.expires); | ||||
|         T.set<10>(U.completed); | ||||
|         T.set<11>(U.canceled); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CreateAction( SecurityObjects::ActionLink & A) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Insert(Sess); | ||||
|             std::string St2{ | ||||
|                 "INSERT INTO ActionLinks (" + AllActionLinksFieldsForSelect + ") VALUES(" + AllActionLinksValuesForSelect + ")"}; | ||||
|             ActionLinkRecord AR; | ||||
|             Convert(A, AR); | ||||
|             Insert << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::use(AR); | ||||
|             Insert.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Select(Sess); | ||||
|  | ||||
|             ActionLinkRecordList ARL; | ||||
|  | ||||
|             std::string S{ | ||||
|                 "SELECT " + AllActionLinksFieldsForSelect + " From ActionLinks where sent=0 and canceled=0 and completed=0"}; | ||||
|             Select << ConvertParams(S), | ||||
|                 Poco::Data::Keywords::into(ARL); | ||||
|             Select.execute(); | ||||
|  | ||||
|             for(const auto &i:ARL) { | ||||
|                 SecurityObjects::ActionLink L; | ||||
|                 Convert(i,L); | ||||
|                 Links.emplace_back(L); | ||||
|             } | ||||
|             return true; | ||||
|  | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Select(Sess); | ||||
|  | ||||
|             ActionLinkRecord AR; | ||||
|  | ||||
|             std::string St2{ | ||||
|                 "SELECT " + AllActionLinksFieldsForSelect + " From ActionLinks where id=?"}; | ||||
|             Select << ConvertParams(St2), | ||||
|             Poco::Data::Keywords::into(AR), | ||||
|             Poco::Data::Keywords::use(ActionId); | ||||
|             Select.execute(); | ||||
|  | ||||
|             if(Select.rowsExtracted()==1) { | ||||
|                 Convert(AR, A); | ||||
|                 return true; | ||||
|             } | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::SentAction(std::string &ActionId) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Update(Sess); | ||||
|  | ||||
|             uint64_t Sent = std::time(nullptr); | ||||
|             std::string St{"UPDATE ActionLinks set Sent=? where id=?"}; | ||||
|             Update << ConvertParams(St), | ||||
|                 Poco::Data::Keywords::use(Sent), | ||||
|                 Poco::Data::Keywords::use(ActionId); | ||||
|             Update.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::DeleteAction(std::string &ActionId) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Delete(Sess); | ||||
|  | ||||
|             uint64_t Sent = std::time(nullptr); | ||||
|             std::string St{"DELETE FROM ActionLinks where id=?"}; | ||||
|             Delete << ConvertParams(St), | ||||
|                 Poco::Data::Keywords::use(ActionId); | ||||
|             Delete.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CompleteAction(std::string &ActionId) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Update(Sess); | ||||
|  | ||||
|             uint64_t completed = std::time(nullptr); | ||||
|             std::string St{"UPDATE ActionLinks set completed=? where id=?"}; | ||||
|             Update << ConvertParams(St), | ||||
|                 Poco::Data::Keywords::use(completed), | ||||
|                 Poco::Data::Keywords::use(ActionId); | ||||
|             Update.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CancelAction(std::string &ActionId) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Update(Sess); | ||||
|  | ||||
|             uint64_t canceled = std::time(nullptr); | ||||
|             std::string St{"UPDATE ActionLinks set canceled=? where id=?"}; | ||||
|             Update << ConvertParams(St), | ||||
|                 Poco::Data::Keywords::use(canceled), | ||||
|                 Poco::Data::Keywords::use(ActionId); | ||||
|             Update.execute(); | ||||
|             return true; | ||||
|         } catch (const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										79
									
								
								src/storage/storage_actionLinks.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/storage/storage_actionLinks.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #ifndef OWSEC_STORAGE_ACTIONLINKS_H | ||||
| #define OWSEC_STORAGE_ACTIONLINKS_H | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "Poco/Tuple.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|     static const std::string AllActionLinksFieldsForCreation{ | ||||
|         "Id             varchar(36)," | ||||
|         "Action         bigint," | ||||
|         "UserId         text," | ||||
|         "template       text," | ||||
|         "variables      text," | ||||
|         "locale         varchar," | ||||
|         "message        text," | ||||
|         "sent           bigint," | ||||
|         "created        bigint," | ||||
|         "expires        bigint," | ||||
|         "completed      bigint," | ||||
|         "canceled       bigint" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllActionLinksFieldsForSelect { | ||||
|         "Id, " | ||||
|         "Action," | ||||
|         "UserId," | ||||
|         "template," | ||||
|         "variables," | ||||
|         "locale," | ||||
|         "message," | ||||
|         "sent," | ||||
|         "created," | ||||
|         "expires," | ||||
|         "completed," | ||||
|         "canceled" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllActionLinksValuesForSelect{ "?,?,?,?,?,?,?,?,?,?,?,?" }; | ||||
|  | ||||
|     static const std::string AllActionLinksFieldsForUpdate { | ||||
|         "Id=?, " | ||||
|         "Action=?," | ||||
|         "UserId=?," | ||||
|         "template=?," | ||||
|         "variables=?," | ||||
|         "locale=?," | ||||
|         "message=?," | ||||
|         "sent=?," | ||||
|         "created=?," | ||||
|         "expires=?," | ||||
|         "completed=?," | ||||
|         "canceled=?" | ||||
|     }; | ||||
|  | ||||
|     typedef Poco::Tuple < | ||||
|                 std::string,    // id | ||||
|                 uint64_t,       // action | ||||
|                 std::string,    // userId | ||||
|                 std::string,    // actionTemplate | ||||
|                 std::string,    // variables | ||||
|                 std::string,    // locale | ||||
|                 std::string,    // message | ||||
|                 uint64_t,       // sent | ||||
|                 uint64_t,       // created | ||||
|                 uint64_t,       // expires | ||||
|                 uint64_t,       // completed | ||||
|                 uint64_t        // canceled | ||||
|             > ActionLinkRecord; | ||||
|     typedef std::vector <ActionLinkRecord> ActionLinkRecordList; | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| #endif //OWSEC_STORAGE_ACTIONLINKS_H | ||||
| @@ -35,8 +35,7 @@ namespace OpenWifi { | ||||
|  | ||||
|             uint64_t Now = std::time(nullptr); | ||||
|  | ||||
|             std::string St2{ | ||||
|                     "INSERT INTO Avatars (Id,Type,Created,Name,Avatar) VALUES(?,?,?,?,?)"}; | ||||
|             std::string St2{"INSERT INTO Avatars (" + AllAvatarFieldsForSelect + ") VALUES( " + AllAvatarValuesForSelect + " )"}; | ||||
|  | ||||
|             Insert << ConvertParams(St2), | ||||
|                     Poco::Data::Keywords::use(Id), | ||||
| @@ -58,13 +57,19 @@ namespace OpenWifi { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             Poco::Data::Statement Select(Sess); | ||||
|  | ||||
|             std::string St2{"SELECT Avatar, Type, Name FROM Avatars WHERE Id=?"}; | ||||
|             std::string St2{"SELECT " + AllAvatarFieldsForSelect + " FROM Avatars WHERE Id=?"}; | ||||
|  | ||||
|             Poco::Data::Statement Select2(Sess); | ||||
|  | ||||
|             std::string TId; | ||||
|             uint64_t    Created; | ||||
|  | ||||
|             Select2 << ConvertParams(St2), | ||||
|                     Poco::Data::Keywords::into(L), | ||||
|                     Poco::Data::Keywords::into(TId), | ||||
|                     Poco::Data::Keywords::into(Type), | ||||
|                     Poco::Data::Keywords::into(Created), | ||||
|                     Poco::Data::Keywords::into(Name), | ||||
|                     Poco::Data::Keywords::into(L), | ||||
|                     Poco::Data::Keywords::use(Id); | ||||
|             Select2.execute(); | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,33 @@ | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static const std::string AllAvatarFieldsForCreation_sqlite{ | ||||
|         "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|         "Type			VARCHAR, " | ||||
|         "Created 		BIGINT, " | ||||
|         "Name           VARCHAR, " | ||||
|         "Avatar     	BLOB" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllAvatarFieldsForCreation_mysql{ | ||||
|         "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|         "Type			VARCHAR, " | ||||
|         "Created 		BIGINT, " | ||||
|         "Name           VARCHAR, " | ||||
|         "Avatar     	LONGBLOB" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllAvatarFieldsForCreation_pgsql{ | ||||
|         "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|         "Type			VARCHAR, " | ||||
|         "Created 		BIGINT, " | ||||
|         "Name           VARCHAR, " | ||||
|         "Avatar     	BYTEA" | ||||
|     }; | ||||
|  | ||||
|     static const std::string AllAvatarFieldsForSelect{ " Id,Type,Created,Name,Avatar " }; | ||||
|     static const std::string AllAvatarValuesForSelect{ "?,?,?,?,?" }; | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,8 @@ | ||||
| #include "StorageService.h" | ||||
| #include "storage_users.h" | ||||
| #include "storage_avatar.h" | ||||
| #include "storage_actionLinks.h" | ||||
| #include "storage_tokens.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -12,6 +14,7 @@ namespace OpenWifi { | ||||
|         Create_UserTable(); | ||||
|         Create_AvatarTable(); | ||||
|         Create_TokensTable(); | ||||
|         Create_ActionLinkTable(); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| @@ -40,83 +43,51 @@ namespace OpenWifi { | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     int Storage::Create_ActionLinkTable() { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|  | ||||
|             Sess << "CREATE TABLE IF NOT EXISTS ActionLinks ( " | ||||
|                     + AllActionLinksFieldsForCreation + " ) ", | ||||
|             Poco::Data::Keywords::now; | ||||
|             return 0; | ||||
|         } catch(const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return 1; | ||||
|     } | ||||
|  | ||||
|     int Storage::Create_AvatarTable() { | ||||
|             try { | ||||
|                 Poco::Data::Session Sess = Pool_->get(); | ||||
|  | ||||
|                 if(dbType_==sqlite) { | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" | ||||
|                             "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|                             "Type			VARCHAR, " | ||||
|                             "Created 		BIGINT, " | ||||
|                             "Name           VARCHAR, " | ||||
|                             "Avatar     	BLOB" | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" + AllAvatarFieldsForCreation_sqlite + | ||||
|                             ") ", Poco::Data::Keywords::now; | ||||
|                 } else if(dbType_==mysql) { | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" | ||||
|                             "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|                             "Type			VARCHAR, " | ||||
|                             "Created 		BIGINT, " | ||||
|                             "Name           VARCHAR, " | ||||
|                             "Avatar     	LONGBLOB" | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" + AllAvatarFieldsForCreation_mysql + | ||||
|                             ") ", Poco::Data::Keywords::now; | ||||
|                 } else if(dbType_==pgsql) { | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" | ||||
|                             "Id			    VARCHAR(36) PRIMARY KEY, " | ||||
|                             "Type			VARCHAR, " | ||||
|                             "Created 		BIGINT, " | ||||
|                             "Name           VARCHAR, " | ||||
|                             "Avatar     	BYTEA" | ||||
|                     Sess << "CREATE TABLE IF NOT EXISTS Avatars (" + AllAvatarFieldsForCreation_pgsql + | ||||
|                             ") ", Poco::Data::Keywords::now; | ||||
|                 } | ||||
|                 return 0; | ||||
|             } catch(const Poco::Exception &E) { | ||||
|                 Logger_.log(E); | ||||
|             } | ||||
|             return 0; | ||||
|             return 1; | ||||
|         } | ||||
|  | ||||
|         int Storage::Create_TokensTable() { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|             if(dbType_==sqlite) { | ||||
|                 Sess << "CREATE TABLE IF NOT EXISTS Tokens (" | ||||
|                         "Token			    TEXT PRIMARY KEY, " | ||||
|                         "RefreshToken       TEXT, " | ||||
|                         "TokenType          TEXT, " | ||||
|                         "UserName           TEXT, " | ||||
|                         "Created 		    BIGINT, " | ||||
|                         "Expires 		    BIGINT, " | ||||
|                         "IdleTimeOut        BIGINT, " | ||||
|                         "RevocationDate 	BIGINT " | ||||
|                 Sess << "CREATE TABLE IF NOT EXISTS Tokens (" + | ||||
|                             AllTokensFieldsForCreation + | ||||
|                         ") ", Poco::Data::Keywords::now; | ||||
|             } else if(dbType_==mysql) { | ||||
|                 Sess << "CREATE TABLE IF NOT EXISTS Tokens (" | ||||
|                         "Token			    TEXT PRIMARY KEY, " | ||||
|                         "RefreshToken       TEXT, " | ||||
|                         "TokenType          TEXT, " | ||||
|                         "UserName           TEXT, " | ||||
|                         "Created 		    BIGINT, " | ||||
|                         "Expires 		    BIGINT, " | ||||
|                         "IdleTimeOut        BIGINT, " | ||||
|                         "RevocationDate 	BIGINT " | ||||
|                         ") ", Poco::Data::Keywords::now; | ||||
|             } else if(dbType_==pgsql) { | ||||
|                 Sess << "CREATE TABLE IF NOT EXISTS Tokens (" | ||||
|                         "Token			    TEXT PRIMARY KEY, " | ||||
|                         "RefreshToken       TEXT, " | ||||
|                         "TokenType          TEXT, " | ||||
|                         "UserName           TEXT, " | ||||
|                         "Created 		    BIGINT, " | ||||
|                         "Expires 		    BIGINT, " | ||||
|                         "IdleTimeOut        BIGINT, " | ||||
|                         "RevocationDate 	BIGINT " | ||||
|                         ") ", Poco::Data::Keywords::now; | ||||
|             } | ||||
|             return 0; | ||||
|         } catch(const Poco::Exception &E) { | ||||
|             Logger_.log(E); | ||||
|         } | ||||
|         return 0; | ||||
|         return 1; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
|  | ||||
| #include "../StorageService.h" | ||||
| #include "StorageService.h" | ||||
| #include "storage/storage_tokens.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
| @@ -22,7 +23,7 @@ namespace OpenWifi { | ||||
|             uint64_t Z = 0; | ||||
|  | ||||
|             std::string St2{ | ||||
|                 "INSERT INTO Tokens (Token, RefreshToken, TokenType, Username, Created, Expires, IdleTimeOut, RevocationDate) VALUES(?,?,?,?,?,?,?,?)"}; | ||||
|                 "INSERT INTO Tokens (" + AllTokensFieldsForSelect + ") VALUES(" + AllTokensValuesForSelect + ")"}; | ||||
|  | ||||
|             Insert << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::use(Token), | ||||
| @@ -49,7 +50,7 @@ namespace OpenWifi { | ||||
|  | ||||
|             uint32_t RevocationDate = 0 ; | ||||
|  | ||||
|             std::string St2{"SELECT Token, RefreshToken, TokenType, Username, Created, Expires, IdleTimeOut, RevocationDate From Tokens WHERE Token=?"}; | ||||
|             std::string St2{"SELECT " + AllTokensValuesForSelect + " From Tokens WHERE Token=?"}; | ||||
|             Select << ConvertParams(St2), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.access_token_), | ||||
|                 Poco::Data::Keywords::into(UInfo.webtoken.refresh_token_), | ||||
|   | ||||
							
								
								
									
										30
									
								
								src/storage/storage_tokens.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								src/storage/storage_tokens.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | ||||
| // | ||||
| // Created by stephane bourque on 2021-11-08. | ||||
| // | ||||
|  | ||||
| #ifndef OWSEC_STORAGE_TOKENS_H | ||||
| #define OWSEC_STORAGE_TOKENS_H | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include "Poco/Tuple.h" | ||||
|  | ||||
| namespace OpenWifi { | ||||
|  | ||||
|     static std::string AllTokensFieldsForCreation{                        "Token			    TEXT PRIMARY KEY, " | ||||
|                                                                           "RefreshToken       TEXT, " | ||||
|                                                                           "TokenType          TEXT, " | ||||
|                                                                           "UserName           TEXT, " | ||||
|                                                                           "Created 		    BIGINT, " | ||||
|                                                                           "Expires 		    BIGINT, " | ||||
|                                                                           "IdleTimeOut        BIGINT, " | ||||
|                                                                           "RevocationDate 	BIGINT " | ||||
|     }; | ||||
|     static std::string AllTokensFieldsForSelect {"Token, RefreshToken, TokenType, Username, Created, Expires, IdleTimeOut, RevocationDate"}; | ||||
|     static std::string AllTokensValuesForSelect{"?,?,?,?,?,?,?,?"}; | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif //OWSEC_STORAGE_TOKENS_H | ||||
| @@ -80,7 +80,35 @@ namespace OpenWifi { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CreateUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser) { | ||||
|     std::string DefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"}; | ||||
|  | ||||
|     //  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. | ||||
|     //  if the "DEFAULT-USER-UUID", we keep the UUID of that user. We want to hide the UUID of the default root user | ||||
|     bool Storage::InitializeDefaultUser() { | ||||
|         SecurityObjects::UserInfo   U; | ||||
|         bool DefaultUserCreated = false; | ||||
|  | ||||
|         AppServiceRegistry().Get("defaultusercreated",DefaultUserCreated); | ||||
|         if(!GetUserById(DefaultUseridStockUUID,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.userRole = SecurityObjects::ROOT; | ||||
|             U.creationDate = std::time(nullptr); | ||||
|             U.validated = true; | ||||
|             U.name = "Default User"; | ||||
|             U.description = "Default user should be deleted."; | ||||
|             U.changePassword = true; | ||||
|             CreateUser("SYSTEM",U, true); | ||||
|             AppServiceRegistry().Set("defaultusercreated",true); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool Storage::CreateUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser, bool PasswordHashedAlready ) { | ||||
|         try { | ||||
|             Poco::Data::Session Sess = Pool_->get(); | ||||
|  | ||||
| @@ -103,19 +131,23 @@ namespace OpenWifi { | ||||
|             if(!Records.empty()) | ||||
|                 return false; | ||||
|  | ||||
|             NewUser.Id = MicroService::instance().CreateUUID(); | ||||
|             NewUser.creationDate = std::time(nullptr); | ||||
|             if(!PasswordHashedAlready) { | ||||
|                 NewUser.Id = MicroService::instance().CreateUUID(); | ||||
|                 NewUser.creationDate = std::time(nullptr); | ||||
|             } | ||||
|  | ||||
|             //  if there is a password, we assume that we do not want email verification, | ||||
|             //  if there is no password, we will do email verification | ||||
|             if(NewUser.currentPassword.empty()) { | ||||
|  | ||||
|             } else { | ||||
|                 NewUser.currentPassword = AuthService()->ComputePasswordHash(NewUser.email,NewUser.currentPassword); | ||||
|                 NewUser.lastPasswords.clear(); | ||||
|                 NewUser.lastPasswords.push_back(NewUser.currentPassword); | ||||
|                 NewUser.lastPasswordChange = std::time(nullptr); | ||||
|                 NewUser.validated = true; | ||||
|                 if(!PasswordHashedAlready) { | ||||
|                     NewUser.currentPassword = AuthService()->ComputeNewPasswordHash(NewUser.email,NewUser.currentPassword); | ||||
|                     NewUser.lastPasswords.clear(); | ||||
|                     NewUser.lastPasswords.push_back(NewUser.currentPassword); | ||||
|                     NewUser.lastPasswordChange = std::time(nullptr); | ||||
|                     NewUser.validated = true; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             auto Notes = RESTAPI_utils::to_string(NewUser.notes); | ||||
|   | ||||
| @@ -105,20 +105,6 @@ namespace OpenWifi { | ||||
|             "oauthType=?, " | ||||
|             "oauthUserInfo=? "}; | ||||
|  | ||||
|     static const std::string AllActionLinksFieldsForCreation{ | ||||
|             "Id          varchar(36)," | ||||
|             "Action         varchar," | ||||
|             "UserId         varchar," | ||||
|             "template       varchar," | ||||
|             "locale         varchar," | ||||
|             "message        text," | ||||
|             "sent           bigint," | ||||
|             "created        bigint," | ||||
|             "expires        bigint," | ||||
|             "completed      bigint," | ||||
|             "canceled       bigint" | ||||
|     }; | ||||
|  | ||||
|     typedef Poco::Tuple < | ||||
|         std::string,    // Id = 0; | ||||
|         std::string,    // name; | ||||
|   | ||||
| @@ -58,8 +58,8 @@ testlogin() { | ||||
|     curl ${FLAGS} -X POST "https://${OWSEC}/api/v1/oauth2" \ | ||||
|         -H "Content-Type: application/json" \ | ||||
|         -d "$payload" > ${result_file} | ||||
|     userMustChangePassword=$(cat ${result_file} | jq -r '.userMustChangePassword') | ||||
|     if [[ ${userMustChangePassword} == "true" ]] | ||||
|     userMustChangePassword=$(cat ${result_file} | jq -r '.ErrorCode') | ||||
|     if [[ ${userMustChangePassword} == "1" ]] | ||||
|     then | ||||
|         echo "User must change password to login..." | ||||
|         if [[ "$3" == "" ]] | ||||
| @@ -78,7 +78,10 @@ testlogin() { | ||||
|         curl ${FLAGS} -X POST "https://${OWSEC}/api/v1/oauth2" \ | ||||
|                 -H "Content-Type: application/json" \ | ||||
|                 -d "$payload" > ${result_file} | ||||
|         jq < ${result_file} | ||||
|         token=$(cat ${result_file} | jq -r '.access_token') | ||||
|     else | ||||
|         payload="{ \"userId\" : \"$1\" , \"password\" : \"$2\" }" | ||||
|         token=$(curl ${FLAGS} -X POST -H "Content-Type: application/json" -d "$payload" "https://${OWSEC}/api/v1/oauth2" | jq -r '.access_token') | ||||
|     fi | ||||
|     jq < ${result_file} | ||||
| } | ||||
| @@ -372,6 +375,7 @@ case "$1" in | ||||
|     "getsubsystemnames") login; getsubsystemnames; logout ;; | ||||
|     "reloadsubsystem") login; reloadsubsystem "$2"; logout ;; | ||||
|     "systeminfo") login; systeminfo ; logout;; | ||||
|     "testburst") login; login; login; login; login; login; login; login; login; login; login; login; login; login; login; login;; | ||||
|     "help") login; help  ; logout ;; | ||||
|     *) help ;; | ||||
| esac | ||||
|   | ||||
| @@ -29,9 +29,38 @@ | ||||
|             opacity: 0.8; | ||||
|         } | ||||
|  | ||||
|         .imgcontainer { | ||||
|         .img-container { | ||||
|             width: 100%; | ||||
|             margin-top: 5%; | ||||
|             text-align: center; | ||||
|             margin: 24px 0 12px 0; | ||||
|             display: block; | ||||
|         } | ||||
|  | ||||
|         .info-card { | ||||
|             padding: 30px; | ||||
|             box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|             display: block; | ||||
|             width: 50%; | ||||
|             border: 1em; | ||||
|             background-color: white; | ||||
|             width: 40%; | ||||
|             height: auto; | ||||
|             margin-left: auto; | ||||
|             margin-right: auto; | ||||
|             margin-bottom: auto; | ||||
|             margin-top: 50px; | ||||
|             position: relative; | ||||
|         } | ||||
|          | ||||
|         .info-list { | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|  | ||||
|         .info-title { | ||||
|             padding-bottom: 20px; | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|  | ||||
|         img.avatar { | ||||
| @@ -48,6 +77,10 @@ | ||||
|             padding-top: 16px; | ||||
|         } | ||||
|  | ||||
|         body { | ||||
|             background-color: #ebedef; | ||||
|         } | ||||
|  | ||||
|         /* Change styles for span and cancel button on extra small screens */ | ||||
|         @media screen and (max-width: 300px) { | ||||
|             span.password1 { | ||||
| @@ -62,17 +95,17 @@ | ||||
| </head> | ||||
| <body> | ||||
|  | ||||
| <div class="imgcontainer"> | ||||
|     <img src="open-wifi.svg" alt="OpenWifi"> | ||||
| <div class="img-container"> | ||||
|     <img src="/wwwassets/the_logo.png" alt="OpenWifi"> | ||||
| </div> | ||||
|  | ||||
| <div> | ||||
|     <p>Site access rules:</p> | ||||
|     <ul> | ||||
| <div class="info-card"> | ||||
|     <h2 class="info-title">Site Access rules</h2> | ||||
|     <ul class="info-list"> | ||||
|         <li>Must be a TIP Member.</li> | ||||
|     </ul> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| </html> | ||||
|   | ||||
| @@ -30,8 +30,10 @@ | ||||
|         } | ||||
|  | ||||
|         .imgcontainer { | ||||
|             width: 100%; | ||||
|             margin-top: 5%; | ||||
|             text-align: center; | ||||
|             margin: 24px 0 12px 0; | ||||
|             display: block; | ||||
|         } | ||||
|  | ||||
|         img.avatar { | ||||
| @@ -43,6 +45,37 @@ | ||||
|             padding: 16px; | ||||
|         } | ||||
|  | ||||
|         .info-card { | ||||
|             padding: 30px; | ||||
|             box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|             display: block; | ||||
|             width: 50%; | ||||
|             border: 1em; | ||||
|             background-color: white; | ||||
|             width: 40%; | ||||
|             height: auto; | ||||
|             margin-left: auto; | ||||
|             margin-right: auto; | ||||
|             margin-bottom: auto; | ||||
|             margin-top: 50px; | ||||
|             position: relative; | ||||
|         } | ||||
|  | ||||
|         .info-list { | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|  | ||||
|         .info-title { | ||||
|             padding-bottom: 20px; | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|          | ||||
|         .body { | ||||
|             background-color: #ebedef; | ||||
|         } | ||||
|  | ||||
|         span.password1 { | ||||
|             float: right; | ||||
|             padding-top: 16px; | ||||
| @@ -60,16 +93,17 @@ | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
| <body class="body"> | ||||
|  | ||||
| <div class="imgcontainer"> | ||||
|     <img src="open-wifi.svg" alt="OpenWifi"> | ||||
|     <img src="/wwwassets/the_logo.png" alt="OpenWifi"> | ||||
| </div> | ||||
|  | ||||
| <div> | ||||
|     <p>Password rules:</p> | ||||
|     <ul> | ||||
|         <li>Must be at least 8 characters long.</li> | ||||
|  | ||||
| <div class="info-card"> | ||||
|     <h2 class="info-title">Password rules</h2> | ||||
|     <ul class="info-list"> | ||||
|         <li>Must be at least 8 characters long</li> | ||||
|         <li>Must contain 1 uppercase letter</li> | ||||
|         <li>Must contain 1 lowercase letter</li> | ||||
|         <li>Must contain 1 digit</li> | ||||
| @@ -78,5 +112,6 @@ | ||||
| </div> | ||||
|  | ||||
|  | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| </html> | ||||
|   | ||||
| @@ -3,8 +3,15 @@ | ||||
| <head> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <style> | ||||
|         body {font-family: Arial, Helvetica, sans-serif;} | ||||
|         form {border: 3px solid #f1f1f1;} | ||||
|         body { | ||||
|             font-family: Arial,  | ||||
|             Helvetica, sans-serif; | ||||
|             background-color: #ebedef; | ||||
|         } | ||||
|  | ||||
|         .body { | ||||
|             background-color: #ebedef; | ||||
|         } | ||||
|  | ||||
|         input[type=text], input[type=password] { | ||||
|             width: 90%; | ||||
| @@ -31,15 +38,10 @@ | ||||
|         } | ||||
|  | ||||
|         .imgcontainer { | ||||
|             width: 100%; | ||||
|             margin-top: 5%; | ||||
|             text-align: center; | ||||
|             margin: 5px 0 5px 0; | ||||
|             grid-column-start: 2; | ||||
|             grid-column-end: 2; | ||||
|         } | ||||
|  | ||||
|         .passwordlabel { | ||||
|             grid-column-start: 2; | ||||
|             grid-column-end: 2; | ||||
|             display: block; | ||||
|         } | ||||
|  | ||||
|         img.avatar { | ||||
| @@ -50,16 +52,22 @@ | ||||
|         .grid-container  { | ||||
|             display: grid; | ||||
|             grid-template-columns: 15% 70% 15%; | ||||
|             background-color: #f3db21; | ||||
|             grid-column-gap: 5px; | ||||
|             padding: 10px; | ||||
|         } | ||||
|  | ||||
|         .grid-container > div { | ||||
|             background-color: rgba(255, 255, 255, 0.8); | ||||
|             text-align: center; | ||||
|             padding: 20px 0; | ||||
|             font-size: 30px; | ||||
|             padding: 30px; | ||||
|             box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|             display: block; | ||||
|             width: 50%; | ||||
|             border: 1em; | ||||
|             background-color: white; | ||||
|             width: 40%; | ||||
|             height: auto; | ||||
|             margin-left: auto; | ||||
|             margin-right: auto; | ||||
|             margin-bottom: auto; | ||||
|             margin-top: 50px; | ||||
|             position: relative; | ||||
|         } | ||||
|  | ||||
|         .passwordtext { | ||||
| @@ -67,7 +75,13 @@ | ||||
|             margin-left: 5%; | ||||
|         } | ||||
|  | ||||
|         .password-input { | ||||
|             margin-top: 0px; | ||||
|         } | ||||
|  | ||||
|         .rulestext { | ||||
|             width: 95%; | ||||
|             margin: auto; | ||||
|             display: inline-block; | ||||
|             text-align: left; | ||||
|             text-justify: none; | ||||
| @@ -101,17 +115,18 @@ | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|  | ||||
| <div class="imgcontainer"> | ||||
|     <img src="/wwwassets/the_logo.png" alt="OpenWifi"> | ||||
| </div> | ||||
|  | ||||
| <form action="/api/v1/actionLink?action=password_reset" method="post" onsubmit="return validatePassword()"> | ||||
| <input type="hidden" id="custId" name="id" value="${UUID}"> | ||||
| <div class="grid-container"> | ||||
|  | ||||
|         <div class="imgcontainer"> | ||||
|             <img src="/wwwassets/open-wifi.svg" alt="Logo" class="logo"> | ||||
|         </div> | ||||
|  | ||||
|     <h2>Reset Password</h2> | ||||
|     <div class="passwordlabel"> | ||||
|         <label class="passwordtext" for="password1" ><b>New Password</b></label> | ||||
|         <input id="password1" type="password" placeholder="New Password" name="password1" pattern="${PASSWORD_VALIDATION}" required> | ||||
|         <input className="password-input" id="password1" type="password" placeholder="New Password" name="password1" pattern="${PASSWORD_VALIDATION}" required> | ||||
|     </div> | ||||
|     <div class="passwordlabel"> | ||||
|         <label class="passwordtext" for="password2"><b>Retype Password</b></label> | ||||
|   | ||||
| @@ -1,11 +1,107 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <title>Reset Password Failed</title> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|     <style> | ||||
|         body {font-family: Arial, Helvetica, sans-serif;} | ||||
|         form {border: 3px solid #f1f1f1;} | ||||
|  | ||||
|         input[type=text], input[type=password] { | ||||
|             width: 100%; | ||||
|             padding: 12px 20px; | ||||
|             margin: 8px 0; | ||||
|             display: inline-block; | ||||
|             border: 1px solid #ccc; | ||||
|             box-sizing: border-box; | ||||
|         } | ||||
|  | ||||
|         button { | ||||
|             background-color: #04AA6D; | ||||
|             color: white; | ||||
|             padding: 14px 20px; | ||||
|             margin: 8px 0; | ||||
|             border: none; | ||||
|             cursor: pointer; | ||||
|             width: 100%; | ||||
|         } | ||||
|  | ||||
|         button:hover { | ||||
|             opacity: 0.8; | ||||
|         } | ||||
|  | ||||
|         .imgcontainer { | ||||
|             width: 100%; | ||||
|             margin-top: 5%; | ||||
|             text-align: center; | ||||
|             display: block; | ||||
|         } | ||||
|  | ||||
|         img.avatar { | ||||
|             width: 40%; | ||||
|             border-radius: 50%; | ||||
|         } | ||||
|  | ||||
|         .container { | ||||
|             padding: 16px; | ||||
|         } | ||||
|  | ||||
|         .info-card { | ||||
|             padding: 30px; | ||||
|             box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2); | ||||
|             display: block; | ||||
|             width: 50%; | ||||
|             border: 1em; | ||||
|             background-color: white; | ||||
|             width: 40%; | ||||
|             height: auto; | ||||
|             margin-left: auto; | ||||
|             margin-right: auto; | ||||
|             margin-bottom: auto; | ||||
|             margin-top: 50px; | ||||
|             position: relative; | ||||
|         } | ||||
|  | ||||
|         .info-list { | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|  | ||||
|         .info-title { | ||||
|             padding-bottom: 20px; | ||||
|             width: 80%; | ||||
|             margin: auto; | ||||
|         } | ||||
|  | ||||
|         .body { | ||||
|             background-color: #ebedef; | ||||
|         } | ||||
|  | ||||
|         span.password1 { | ||||
|             float: right; | ||||
|             padding-top: 16px; | ||||
|         } | ||||
|  | ||||
|         /* Change styles for span and cancel button on extra small screens */ | ||||
|         @media screen and (max-width: 300px) { | ||||
|             span.password1 { | ||||
|                 display: block; | ||||
|                 float: none; | ||||
|             } | ||||
|             .cancelbtn { | ||||
|                 width: 100%; | ||||
|             } | ||||
|         } | ||||
|     </style> | ||||
| </head> | ||||
| <body> | ||||
|     <h1>Password reset failed...</h1> | ||||
| <body class="body"> | ||||
|  | ||||
| <div class="imgcontainer"> | ||||
|     <img src="/wwwassets/the_logo.png" alt="OpenWifi"> | ||||
| </div> | ||||
|  | ||||
|  | ||||
| <div class="info-card"> | ||||
|     <h1 class="info-title">Reset Password Failed</h1> | ||||
|     <div> | ||||
|         <h3>ID</h3> | ||||
|         <b>${UUID}</b> | ||||
| @@ -14,5 +110,7 @@ | ||||
|         <h3>Error</h3> | ||||
|         <b>${ERROR_TEXT}</b> | ||||
|     </div> | ||||
| </div> | ||||
|  | ||||
| </body> | ||||
| </html> | ||||
| </html> | ||||
|   | ||||
| @@ -62,8 +62,9 @@ | ||||
| </head> | ||||
| <body> | ||||
|     <div class="imgcontainer"> | ||||
|       <img src="/wwwassets/avatar.jpg" alt="Avatar" class="avatar"> | ||||
|       <img src="/wwwassets/the_logo.png" alt="Avatar" class="avatar"> | ||||
|     </div> | ||||
|  | ||||
|     <h1>Password was successfully reset</h1> | ||||
|     <div> | ||||
|         <h3>Thank you ${USERNAME} for resetting your password.</h3> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								wwwassets/the_logo.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								wwwassets/the_logo.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.8 KiB | 
		Reference in New Issue
	
	Block a user