Compare commits

..

1 Commits

Author SHA1 Message Date
Dmitry Dunaev
05d06fce53 [WIFI-5702] Add: README note on changing default password 2021-11-14 22:37:33 +03:00
37 changed files with 402 additions and 3176 deletions

View File

@@ -30,20 +30,9 @@ else()
file(WRITE build ${BUILD_NUM}) file(WRITE build ${BUILD_NUM})
endif() endif()
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE GIT_RESULT
OUTPUT_VARIABLE GIT_HASH)
if(NOT GIT_RESULT EQUAL "0")
message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}")
endif()
string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
endif()
add_definitions(-DAWS_CUSTOM_MEMORY_MANAGEMENT)
set(BUILD_SHARED_LIBS 1) set(BUILD_SHARED_LIBS 1)
add_definitions(-DAPP_VERSION="${CMAKE_PROJECT_VERSION}" -DBUILD_NUMBER="${BUILD_NUM}")
add_definitions(-DTIP_SECURITY_SERVICE="1") add_definitions(-DTIP_SECURITY_SERVICE="1")
set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_STATIC_LIBS OFF)
@@ -61,11 +50,8 @@ find_package(Poco REQUIRED COMPONENTS JSON Crypto JWT Net Util NetSSL Data DataS
include_directories(/usr/local/include /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include) include_directories(/usr/local/include /usr/local/opt/openssl/include src include/kafka /usr/local/opt/mysql-client/include)
configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY)
add_executable( owsec add_executable( owsec
build build
src/ow_version.h.in
src/framework/CountryCodes.h src/framework/CountryCodes.h
src/framework/KafkaTopics.h src/framework/KafkaTopics.h
src/framework/MicroService.h src/framework/MicroService.h
@@ -104,7 +90,7 @@ add_executable( owsec
src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h
src/storage/storage_tokens.h src/storage/storage_tokens.h
src/ActionLinkManager.cpp src/ActionLinkManager.h src/ActionLinkManager.cpp src/ActionLinkManager.h
src/ACLProcessor.h) )
if(NOT SMALL_BUILD) if(NOT SMALL_BUILD)
target_link_libraries(owsec PUBLIC target_link_libraries(owsec PUBLIC

View File

@@ -56,7 +56,6 @@ RUN make install
ADD CMakeLists.txt build /owsec/ ADD CMakeLists.txt build /owsec/
ADD cmake /owsec/cmake ADD cmake /owsec/cmake
ADD src /owsec/src ADD src /owsec/src
ADD .git /owgw/.git
WORKDIR /owsec WORKDIR /owsec
RUN mkdir cmake-build RUN mkdir cmake-build

2
build
View File

@@ -1 +1 @@
108 47

View File

@@ -8,7 +8,7 @@ fullnameOverride: ""
images: images:
owsec: owsec:
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owsec
tag: v2.4.0-RC4 tag: main
pullPolicy: Always pullPolicy: Always
# regcred: # regcred:
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io # registry: tip-tip-wlan-cloud-ucentral.jfrog.io

View File

@@ -61,8 +61,6 @@ components:
- 6 # INTERNAL_ERROR, - 6 # INTERNAL_ERROR,
- 7 # ACCESS_DENIED, - 7 # ACCESS_DENIED,
- 8 # INVALID_TOKEN - 8 # INVALID_TOKEN
- 9 # expired token
- 10 # rate limit exceeded
ErrorDetails: ErrorDetails:
type: string type: string
ErrorDescription: ErrorDescription:

View File

@@ -40,7 +40,6 @@ openwifi.system.commandchannel = /tmp/app.ucentralsec
openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem openwifi.service.key = $OWSEC_ROOT/certs/restapi-key.pem
openwifi.service.key.password = mypassword openwifi.service.key.password = mypassword
smssender.enabled = false
smssender.provider = aws smssender.provider = aws
smssender.aws.secretkey = *************************************** smssender.aws.secretkey = ***************************************
smssender.aws.accesskey = *************************************** smssender.aws.accesskey = ***************************************
@@ -54,7 +53,6 @@ smssender.aws.region = **************
# #
# Security Microservice Specific Section # Security Microservice Specific Section
# #
mailer.enabled = false
mailer.hostname = smtp.gmail.com mailer.hostname = smtp.gmail.com
mailer.username = ************************ mailer.username = ************************
mailer.password = ************************ mailer.password = ************************

View File

@@ -1,45 +0,0 @@
//
// Created by stephane bourque on 2021-11-12.
//
#ifndef OWSEC_ACLPROCESSOR_H
#define OWSEC_ACLPROCESSOR_H
#include "RESTObjects/RESTAPI_SecurityObjects.h"
namespace OpenWifi {
class ACLProcessor {
public:
enum ACL_OPS {
READ,
MODIFY,
DELETE,
CREATE
};
static inline bool Can( const SecurityObjects::UserInfo & User, const SecurityObjects::UserInfo & Target, ACL_OPS Op) {
if(User.Id == Target.Id && Op==DELETE)
return false;
if(User.userRole==SecurityObjects::ROOT)
return true;
if(User.Id == Target.Id)
return true;
if(User.userRole!=SecurityObjects::ADMIN && User.userRole!=SecurityObjects::ROOT && Op!=READ)
return false;
if(Target.userRole==SecurityObjects::ROOT && Op!=READ)
return false;
return true;
}
private:
};
}
#endif //OWSEC_ACLPROCESSOR_H

View File

@@ -18,8 +18,8 @@ namespace OpenWifi {
}; };
static ActionLinkManager * instance() { static ActionLinkManager * instance() {
static auto * instance_ = new ActionLinkManager; static ActionLinkManager instance;
return instance_; return &instance;
} }
int Start() final; int Start() final;

View File

@@ -56,10 +56,9 @@ namespace OpenWifi {
Logger_.notice("Stopping..."); Logger_.notice("Stopping...");
} }
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ) bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo )
{ {
std::lock_guard Guard(Mutex_); std::lock_guard Guard(Mutex_);
Expired = false;
try { try {
std::string CallToken; std::string CallToken;
Poco::Net::OAuth20Credentials Auth(Request); Poco::Net::OAuth20Credentials Auth(Request);
@@ -68,29 +67,27 @@ namespace OpenWifi {
} }
if(!CallToken.empty()) { if(!CallToken.empty()) {
auto Client = UserCache_.get(CallToken); if(StorageService()->IsTokenRevoked(CallToken))
if( Client.isNull() ) {
SecurityObjects::UserInfoAndPolicy UInfo2;
uint64_t RevocationDate=0;
if(StorageService()->GetToken(CallToken,UInfo2,RevocationDate)) {
if(RevocationDate!=0)
return false; return false;
Expired = (UInfo2.webtoken.created_ + UInfo2.webtoken.expires_in_) < time(nullptr); auto Client = UserCache_.find(CallToken);
if(StorageService()->GetUserById(UInfo2.userinfo.Id,UInfo.userinfo)) { if( Client == UserCache_.end() ) {
UInfo.webtoken = UInfo2.webtoken; if(StorageService()->GetToken(SessionToken,UInfo)) {
UserCache_.update(CallToken, UInfo); if(StorageService()->GetUserById(UInfo.userinfo.email,UInfo.userinfo)) {
SessionToken = CallToken; UserCache_[UInfo.webtoken.access_token_] = UInfo;
return true; return true;
} }
} }
return false; return false;
} }
if(!Expired) {
if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) {
SessionToken = CallToken; SessionToken = CallToken;
UInfo = *Client ; UInfo = Client->second ;
return true; return true;
} }
RevokeToken(CallToken);
UserCache_.erase(Client);
StorageService()->RevokeToken(CallToken);
return false; return false;
} }
} catch(const Poco::Exception &E) { } catch(const Poco::Exception &E) {
@@ -99,24 +96,16 @@ namespace OpenWifi {
return false; return false;
} }
void AuthService::RevokeToken(std::string & Token) {
UserCache_.remove(Token);
StorageService()->RevokeToken(Token);
}
bool AuthService::DeleteUserFromCache(const std::string &UserName) { bool AuthService::DeleteUserFromCache(const std::string &UserName) {
std::lock_guard Guard(Mutex_); std::lock_guard Guard(Mutex_);
std::vector<std::string> OldTokens; for(auto i=UserCache_.begin();i!=UserCache_.end();) {
if (i->second.userinfo.email==UserName) {
UserCache_.forEach([&OldTokens,UserName](const std::string &token, const SecurityObjects::UserInfoAndPolicy& O) -> void Logout(i->first, false);
{ if(O.userinfo.email==UserName) i = UserCache_.erase(i);
OldTokens.push_back(token); } else {
}); ++i;
}
for(const auto &i:OldTokens) {
Logout(i,false);
UserCache_.remove(i);
} }
return true; return true;
} }
@@ -132,6 +121,9 @@ namespace OpenWifi {
void AuthService::Logout(const std::string &token, bool EraseFromCache) { void AuthService::Logout(const std::string &token, bool EraseFromCache) {
std::lock_guard Guard(Mutex_); std::lock_guard Guard(Mutex_);
if(EraseFromCache)
UserCache_.erase(token);
try { try {
Poco::JSON::Object Obj; Poco::JSON::Object Obj;
Obj.set("event", "remove-token"); Obj.set("event", "remove-token");
@@ -140,7 +132,7 @@ namespace OpenWifi {
std::stringstream ResultText; std::stringstream ResultText;
Poco::JSON::Stringifier::stringify(Obj, ResultText); Poco::JSON::Stringifier::stringify(Obj, ResultText);
std::string Tmp{token}; std::string Tmp{token};
RevokeToken(Tmp); StorageService()->RevokeToken(Tmp);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(), KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(),
false); false);
} catch (const Poco::Exception &E) { } catch (const Poco::Exception &E) {
@@ -191,9 +183,9 @@ namespace OpenWifi {
UInfo.webtoken.username_ = UserName; UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0; UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false; UInfo.webtoken.userMustChangePassword = false;
UserCache_.update(UInfo.webtoken.access_token_,UInfo); UserCache_[UInfo.webtoken.access_token_] = UInfo;
StorageService()->SetLastLogin(UInfo.userinfo.Id); StorageService()->SetLastLogin(UInfo.userinfo.Id);
StorageService()->AddToken(UInfo.userinfo.Id, UInfo.webtoken.access_token_, StorageService()->AddToken(UInfo.webtoken.username_, UInfo.webtoken.access_token_,
UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_, UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_,
UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_); UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
} }
@@ -261,7 +253,7 @@ namespace OpenWifi {
return false; return false;
} }
UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired ) UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo )
{ {
std::lock_guard Guard(Mutex_); std::lock_guard Guard(Mutex_);
@@ -299,7 +291,6 @@ namespace OpenWifi {
UInfo.userinfo.lastLogin=std::time(nullptr); UInfo.userinfo.lastLogin=std::time(nullptr);
StorageService()->SetLastLogin(UInfo.userinfo.Id); StorageService()->SetLastLogin(UInfo.userinfo.Id);
CreateToken(UserName, UInfo ); CreateToken(UserName, UInfo );
return SUCCESS; return SUCCESS;
} }
@@ -346,7 +337,7 @@ namespace OpenWifi {
A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL; A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL;
A.userId = UInfo.email; A.userId = UInfo.email;
A.id = MicroService::CreateUUID(); A.id = MicroService::instance().CreateUUID();
A.created = std::time(nullptr); A.created = std::time(nullptr);
A.expires = A.created + 24*60*60; A.expires = A.created + 24*60*60;
StorageService()->CreateAction(A); StorageService()->CreateAction(A);
@@ -354,39 +345,16 @@ namespace OpenWifi {
return true; return true;
} }
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired) { bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo) {
std::lock_guard G(Mutex_); std::lock_guard G(Mutex_);
auto It = UserCache_.find(Token);
Expired = false; if(It==UserCache_.end())
return false;
auto Client = UserCache_.get(Token); WebToken = It->second.webtoken;
if(!Client.isNull()) { UserInfo = It->second.userinfo;
Expired = (Client->webtoken.created_ + Client->webtoken.expires_in_) < std::time(nullptr);
WebToken = Client->webtoken;
UserInfo = Client->userinfo;
return true; return true;
} }
std::string TToken{Token};
if(StorageService()->IsTokenRevoked(TToken)) {
return false;
}
// get the token from disk...
SecurityObjects::UserInfoAndPolicy UInfo;
uint64_t RevocationDate=0;
if(StorageService()->GetToken(TToken, UInfo, RevocationDate)) {
if(RevocationDate!=0)
return false;
Expired = (UInfo.webtoken.created_ + UInfo.webtoken.expires_in_) < std::time(nullptr);
if(StorageService()->GetUserById(UInfo.userinfo.Id,UInfo.userinfo)) {
WebToken = UInfo.webtoken;
UserCache_.update(UInfo.webtoken.access_token_, UInfo);
return true;
}
}
return false;
}
} // end of namespace } // end of namespace

View File

@@ -18,7 +18,6 @@
#include "Poco/SHA2Engine.h" #include "Poco/SHA2Engine.h"
#include "Poco/Crypto/DigestEngine.h" #include "Poco/Crypto/DigestEngine.h"
#include "Poco/HMACEngine.h" #include "Poco/HMACEngine.h"
#include "Poco/ExpireLRUCache.h"
#include "framework/MicroService.h" #include "framework/MicroService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h" #include "RESTObjects/RESTAPI_SecurityObjects.h"
@@ -45,15 +44,15 @@ namespace OpenWifi{
static int AccessTypeToInt(ACCESS_TYPE T); static int AccessTypeToInt(ACCESS_TYPE T);
static AuthService *instance() { static AuthService *instance() {
static auto * instance_ = new AuthService; static AuthService instance;
return instance_; return &instance;
} }
int Start() override; int Start() override;
void Stop() override; void Stop() override;
[[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired); [[nodiscard]] bool IsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo );
[[nodiscard]] UNAUTHORIZED_REASON Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ); [[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); void CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
[[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo); [[nodiscard]] bool SetPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo);
[[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;}; [[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;};
@@ -61,7 +60,8 @@ namespace OpenWifi{
bool ValidatePassword(const std::string &pwd); bool ValidatePassword(const std::string &pwd);
[[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired); [[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo);
[[nodiscard]] bool IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request);
[[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type); [[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type);
[[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type); [[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type);
@@ -75,21 +75,19 @@ namespace OpenWifi{
[[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, 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 DeleteUserFromCache(const std::string &UserName);
[[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo); [[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo);
void RevokeToken(std::string & Token);
[[nodiscard]] static inline const std::string GetLogoAssetURI() { [[nodiscard]] static inline const std::string GetLogoAssetURI() {
return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png"; return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png";
} }
[[nodiscard]] static inline const std::string GetLogoAssetFileName() { [[nodiscard]] static inline const std::string GetLogoAssetFileName() {
return MicroService::instance().WWWAssetsDir() + "/the_logo.png"; return MicroService::instance().DataDir() + "/wwwassets/the_logo.png";
} }
private: private:
Poco::JWT::Signer Signer_; Poco::JWT::Signer Signer_;
Poco::SHA2Engine SHA2_; Poco::SHA2Engine SHA2_;
Poco::ExpireLRUCache<std::string,SecurityObjects::UserInfoAndPolicy> UserCache_{2048,1200000}; SecurityObjects::UserInfoCache UserCache_;
// SecurityObjects::UserInfoCache UserCache_;
std::string PasswordValidationStr_; std::string PasswordValidationStr_;
std::regex PasswordValidation_; std::regex PasswordValidation_;
uint64_t TokenAging_ = 30 * 24 * 60 * 60; uint64_t TokenAging_ = 30 * 24 * 60 * 60;
@@ -121,8 +119,8 @@ namespace OpenWifi{
inline AuthService * AuthService() { return AuthService::instance(); } inline AuthService * AuthService() { return AuthService::instance(); }
[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired) { [[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) {
return AuthService()->IsAuthorized(Request, SessionToken, UInfo, Expired ); return AuthService()->IsAuthorized(Request, SessionToken, UInfo );
} }
} // end of namespace } // end of namespace

View File

@@ -26,7 +26,7 @@ namespace OpenWifi {
return false; return false;
std::string Challenge = MakeChallenge(); std::string Challenge = MakeChallenge();
std::string uuid = MicroService::CreateUUID(); std::string uuid = MicroService::instance().CreateUUID();
uint64_t Created = std::time(nullptr); uint64_t Created = std::time(nullptr);
ChallengeStart.set("uuid",uuid); ChallengeStart.set("uuid",uuid);
@@ -71,9 +71,8 @@ namespace OpenWifi {
auto uuid = ChallengeResponse->get("uuid").toString(); auto uuid = ChallengeResponse->get("uuid").toString();
auto Hint = Cache_.find(uuid); auto Hint = Cache_.find(uuid);
if(Hint == end(Cache_)) { if(Hint == end(Cache_))
return false; return false;
}
auto answer = ChallengeResponse->get("answer").toString(); auto answer = ChallengeResponse->get("answer").toString();
if(Hint->second.Answer!=answer) { if(Hint->second.Answer!=answer) {

View File

@@ -24,8 +24,8 @@ namespace OpenWifi {
int Start() override; int Start() override;
void Stop() override; void Stop() override;
static MFAServer *instance() { static MFAServer *instance() {
static auto * instance_ = new MFAServer; static MFAServer instance;
return instance_; return &instance;
} }
bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge); bool StartMFAChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, Poco::JSON::Object &Challenge);
@@ -35,7 +35,7 @@ namespace OpenWifi {
static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge); static bool SendChallenge(const SecurityObjects::UserInfoAndPolicy &UInfo, const std::string &Method, const std::string &Challenge);
static inline std::string MakeChallenge() { static inline std::string MakeChallenge() {
return std::to_string(MicroService::instance().Random(1,999999)); return std::to_string(rand() % 999999);
} }
private: private:

View File

@@ -20,7 +20,7 @@ namespace OpenWifi {
Server, Server,
Internal, Internal,
false, false,
true, RateLimit{.Interval=1000,.MaxCalls=10}) {} true, RateLimit{.Interval=1000,.MaxCalls=5}) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; }; static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; };
void RequestResetPassword(SecurityObjects::ActionLink &Link); void RequestResetPassword(SecurityObjects::ActionLink &Link);
void CompleteResetPassword(); void CompleteResetPassword();

View File

@@ -17,38 +17,23 @@
#include "StorageService.h" #include "StorageService.h"
namespace OpenWifi { namespace OpenWifi {
static void FilterCredentials(SecurityObjects::UserInfo & U) {
U.currentPassword.clear();
U.lastPasswords.clear();
U.oauthType.clear();
}
void RESTAPI_oauth2Handler::DoGet() { void RESTAPI_oauth2Handler::DoGet() {
bool Expired = false; if (!IsAuthorized()) {
if (!IsAuthorized(Expired)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation); return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation);
} }
bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false); bool GetMe = GetBoolParameter(RESTAPI::Protocol::ME, false);
if(GetMe) { if(GetMe) {
Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email)); Logger_.information(Poco::format("REQUEST-ME(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email));
Poco::JSON::Object Me; Poco::JSON::Object Me;
SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo; UserInfo_.userinfo.to_json(Me);
FilterCredentials(ReturnedUser);
ReturnedUser.to_json(Me);
return ReturnObject(Me); return ReturnObject(Me);
} }
BadRequest(RESTAPI::Errors::UnrecognizedRequest); BadRequest(RESTAPI::Errors::UnrecognizedRequest);
} }
void RESTAPI_oauth2Handler::DoDelete() { void RESTAPI_oauth2Handler::DoDelete() {
bool Expired = false; if (!IsAuthorized()) {
if (!IsAuthorized(Expired)) { return UnAuthorized("Not authorized.");
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation);
} }
auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "..."); auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "...");
@@ -86,7 +71,7 @@ namespace OpenWifi {
SecurityObjects::ActionLink NewLink; SecurityObjects::ActionLink NewLink;
NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD; NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD;
NewLink.id = MicroService::CreateUUID(); NewLink.id = MicroService::instance().CreateUUID();
NewLink.userId = UInfo1.Id; NewLink.userId = UInfo1.Id;
NewLink.created = std::time(nullptr); NewLink.created = std::time(nullptr);
NewLink.expires = NewLink.created + (24*60*60); NewLink.expires = NewLink.created + (24*60*60);
@@ -108,8 +93,8 @@ namespace OpenWifi {
if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) { if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) {
Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId)); Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId));
if(Obj->has("uuid")) { if(Obj->has(RESTAPI::Protocol::UUID)) {
auto uuid = Obj->get("uuid").toString(); auto uuid = Obj->get(RESTAPI::Protocol::UUID).toString();
if(MFAServer().ResendCode(uuid)) if(MFAServer().ResendCode(uuid))
return OK(); return OK();
} }
@@ -118,7 +103,7 @@ namespace OpenWifi {
if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) { if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) {
Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId)); 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; SecurityObjects::UserInfoAndPolicy UInfo;
if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) { if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) {
Poco::JSON::Object ReturnObj; Poco::JSON::Object ReturnObj;
@@ -130,8 +115,7 @@ namespace OpenWifi {
} }
SecurityObjects::UserInfoAndPolicy UInfo; SecurityObjects::UserInfoAndPolicy UInfo;
bool Expired=false; auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo);
auto Code=AuthService()->Authorize(userId, password, newPassword, UInfo, Expired);
if (Code==SUCCESS) { if (Code==SUCCESS) {
Poco::JSON::Object ReturnObj; Poco::JSON::Object ReturnObj;
if(AuthService()->RequiresMFA(UInfo)) { if(AuthService()->RequiresMFA(UInfo)) {

View File

@@ -21,7 +21,7 @@ namespace OpenWifi {
Poco::Net::HTTPRequest::HTTP_GET, Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS}, Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server, Server,
Internal, false, true , RateLimit{.Interval=1000,.MaxCalls=10}) {} 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"}; }; static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };
void DoGet() final; void DoGet() final;
void DoPost() final; void DoPost() final;

View File

@@ -7,16 +7,8 @@
#include "Poco/JSON/Parser.h" #include "Poco/JSON/Parser.h"
#include "framework/RESTAPI_errors.h" #include "framework/RESTAPI_errors.h"
#include "SMSSender.h" #include "SMSSender.h"
#include "ACLProcessor.h"
namespace OpenWifi { namespace OpenWifi {
static void FilterCredentials(SecurityObjects::UserInfo & U) {
U.currentPassword.clear();
U.lastPasswords.clear();
U.oauthType.clear();
}
void RESTAPI_user_handler::DoGet() { void RESTAPI_user_handler::DoGet() {
std::string Id = GetBinding("id", ""); std::string Id = GetBinding("id", "");
if(Id.empty()) { if(Id.empty()) {
@@ -35,7 +27,9 @@ namespace OpenWifi {
} }
Poco::JSON::Object UserInfoObject; Poco::JSON::Object UserInfoObject;
FilterCredentials(UInfo); UInfo.currentPassword.clear();
UInfo.lastPasswords.clear();
UInfo.oauthType.clear();
UInfo.to_json(UserInfoObject); UInfo.to_json(UserInfoObject);
ReturnObject(UserInfoObject); ReturnObject(UserInfoObject);
} }
@@ -46,12 +40,20 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::MissingUserID); 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; SecurityObjects::UserInfo UInfo;
if(!StorageService()->GetUserById(Id,UInfo)) { if(!StorageService()->GetUserById(Id,UInfo)) {
return NotFound(); return NotFound();
} }
if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::DELETE)) { if(UInfo.userRole==SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED); return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
} }
@@ -62,9 +64,6 @@ namespace OpenWifi {
if(AuthService()->DeleteUserFromCache(UInfo.email)) { if(AuthService()->DeleteUserFromCache(UInfo.email)) {
// nothing to do // nothing to do
} }
StorageService()->DeleteAvatar(UserInfo_.userinfo.email,Id);
Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email)); Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email));
StorageService()->RevokeAllTokens(UInfo.email); StorageService()->RevokeAllTokens(UInfo.email);
Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email)); Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email));
@@ -77,52 +76,57 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::IdMustBe0); return BadRequest(RESTAPI::Errors::IdMustBe0);
} }
SecurityObjects::UserInfo NewUser; SecurityObjects::UserInfo UInfo;
RESTAPI_utils::from_request(NewUser,*Request); RESTAPI_utils::from_request(UInfo,*Request);
if(NewUser.userRole == SecurityObjects::UNKNOWN) { if(UInfo.userRole == SecurityObjects::UNKNOWN) {
return BadRequest(RESTAPI::Errors::InvalidUserRole); return BadRequest(RESTAPI::Errors::InvalidUserRole);
} }
if(!ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) { if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED); return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
} }
Poco::toLowerInPlace(NewUser.email); if(UserInfo_.userinfo.userRole == SecurityObjects::ADMIN && UInfo.userRole == SecurityObjects::ROOT) {
if(!Utils::ValidEMailAddress(NewUser.email)) { return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
Poco::toLowerInPlace(UInfo.email);
if(!Utils::ValidEMailAddress(UInfo.email)) {
return BadRequest(RESTAPI::Errors::InvalidEmailAddress); return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
} }
if(!NewUser.currentPassword.empty()) { if(!UInfo.currentPassword.empty()) {
if(!AuthService()->ValidatePassword(NewUser.currentPassword)) { if(!AuthService()->ValidatePassword(UInfo.currentPassword)) {
return BadRequest(RESTAPI::Errors::InvalidPassword); return BadRequest(RESTAPI::Errors::InvalidPassword);
} }
} }
if(NewUser.name.empty()) if(UInfo.name.empty())
NewUser.name = NewUser.email; UInfo.name = UInfo.email;
if(!StorageService()->CreateUser(NewUser.email,NewUser)) { if(!StorageService()->CreateUser(UInfo.email,UInfo)) {
Logger_.information(Poco::format("Could not add user '%s'.",NewUser.email)); Logger_.information(Poco::format("Could not add user '%s'.",UInfo.email));
return BadRequest(RESTAPI::Errors::RecordNotCreated); return BadRequest(RESTAPI::Errors::RecordNotCreated);
} }
if(GetParameter("email_verification","false")=="true") { if(GetParameter("email_verification","false")=="true") {
if(AuthService::VerifyEmail(NewUser)) if(AuthService::VerifyEmail(UInfo))
Logger_.information(Poco::format("Verification e-mail requested for %s",NewUser.email)); Logger_.information(Poco::format("Verification e-mail requested for %s",UInfo.email));
StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,NewUser.Id,NewUser); StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,UInfo.Id,UInfo);
} }
if(!StorageService()->GetUserByEmail(NewUser.email, NewUser)) { if(!StorageService()->GetUserByEmail(UInfo.email, UInfo)) {
Logger_.information(Poco::format("User '%s' but not retrieved.",NewUser.email)); Logger_.information(Poco::format("User '%s' but not retrieved.",UInfo.email));
return NotFound(); return NotFound();
} }
Poco::JSON::Object UserInfoObject; Poco::JSON::Object UserInfoObject;
FilterCredentials(NewUser); UInfo.to_json(UserInfoObject);
NewUser.to_json(UserInfoObject);
ReturnObject(UserInfoObject); ReturnObject(UserInfoObject);
Logger_.information(Poco::format("User '%s' has been added by '%s')",NewUser.email, UserInfo_.userinfo.email));
Logger_.information(Poco::format("User '%s' has been added by '%s')",UInfo.email, UserInfo_.userinfo.email));
} }
void RESTAPI_user_handler::DoPut() { void RESTAPI_user_handler::DoPut() {
@@ -136,8 +140,12 @@ namespace OpenWifi {
return NotFound(); return NotFound();
} }
if(!ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) { if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && UserInfo_.userinfo.userRole!=SecurityObjects::ADMIN) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED); 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; SecurityObjects::UserInfo NewUser;
@@ -216,23 +224,15 @@ namespace OpenWifi {
return BadRequest(RESTAPI::Errors::NeedMobileNumber); return BadRequest(RESTAPI::Errors::NeedMobileNumber);
} }
if(!NewUser.userTypeProprietaryInfo.mfa.method.empty()) { if(NewUser.userTypeProprietaryInfo.mfa.method=="email") {
if(NewUser.userTypeProprietaryInfo.mfa.method!="email" && NewUser.userTypeProprietaryInfo.mfa.method!="sms" ) {
return BadRequest("Unknown MFA method");
}
Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method; Existing.userTypeProprietaryInfo.mfa.method=NewUser.userTypeProprietaryInfo.mfa.method;
} }
if(Existing.userTypeProprietaryInfo.mfa.enabled && Existing.userTypeProprietaryInfo.mfa.method.empty()) {
return BadRequest("Illegal MFA method");
}
} }
if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) { if(StorageService()->UpdateUserInfo(UserInfo_.userinfo.email,Id,Existing)) {
SecurityObjects::UserInfo NewUserInfo; SecurityObjects::UserInfo NewUserInfo;
StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo); StorageService()->GetUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
Poco::JSON::Object ModifiedObject; Poco::JSON::Object ModifiedObject;
FilterCredentials(NewUserInfo);
NewUserInfo.to_json(ModifiedObject); NewUserInfo.to_json(ModifiedObject);
return ReturnObject(ModifiedObject); return ReturnObject(ModifiedObject);
} }

View File

@@ -13,8 +13,7 @@ namespace OpenWifi {
if (i.first == "token") { if (i.first == "token") {
// can we find this token? // can we find this token?
SecurityObjects::UserInfoAndPolicy SecObj; SecurityObjects::UserInfoAndPolicy SecObj;
bool Expired = false; if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo)) {
if (AuthService()->IsValidToken(i.second, SecObj.webtoken, SecObj.userinfo, Expired)) {
Poco::JSON::Object Obj; Poco::JSON::Object Obj;
SecObj.to_json(Obj); SecObj.to_json(Obj);
return ReturnObject(Obj); return ReturnObject(Obj);

View File

@@ -562,7 +562,7 @@ namespace OpenWifi::ProvObjects {
} }
I.notes = N; I.notes = N;
I.modified = I.created = Now; I.modified = I.created = Now;
I.id = MicroService::CreateUUID(); I.id = MicroService::instance().CreateUUID();
return true; return true;
} }

View File

@@ -59,15 +59,15 @@ namespace OpenWifi::SecurityObjects {
struct MobilePhoneNumber { struct MobilePhoneNumber {
std::string number; std::string number;
bool verified = false; bool verified;
bool primary = false; bool primary;
void to_json(Poco::JSON::Object &Obj) const; 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 { struct MfaAuthInfo {
bool enabled = false; bool enabled;
std::string method; std::string method;
void to_json(Poco::JSON::Object &Obj) const; void to_json(Poco::JSON::Object &Obj) const;
@@ -86,7 +86,7 @@ namespace OpenWifi::SecurityObjects {
std::string uuid; std::string uuid;
std::string question; std::string question;
std::string method; std::string method;
uint64_t created = std::time(nullptr); uint64_t created;
void to_json(Poco::JSON::Object &Obj) const; void to_json(Poco::JSON::Object &Obj) const;
bool from_json(Poco::JSON::Object::Ptr &Obj); bool from_json(Poco::JSON::Object::Ptr &Obj);

View File

@@ -16,16 +16,13 @@
namespace OpenWifi { namespace OpenWifi {
int SMSSender::Start() { int SMSSender::Start() {
Enabled_ = MicroService::instance().ConfigGetBool("smssender.enabled",false); Provider_ = MicroService::instance().ConfigGetString("sms.provider","aws");
if(Enabled_) {
Provider_ = MicroService::instance().ConfigGetString("smssender.provider","aws");
if(Provider_=="aws") { if(Provider_=="aws") {
ProviderImpl_ = std::make_unique<SMS_provider_aws>(Logger_); ProviderImpl_ = std::make_unique<SMS_provider_aws>(Logger_);
} else if(Provider_=="twilio") { } else if(Provider_=="twilio") {
ProviderImpl_ = std::make_unique<SMS_provider_twilio>(Logger_); ProviderImpl_ = std::make_unique<SMS_provider_twilio>(Logger_);
} }
Enabled_ = ProviderImpl_->Initialize(); Enabled_ = ProviderImpl_->Initialize();
}
return 0; return 0;
} }

View File

@@ -18,15 +18,15 @@ namespace OpenWifi {
std::string Number; std::string Number;
std::string Code; std::string Code;
std::string UserName; std::string UserName;
uint64_t Created = std::time(nullptr); uint64_t Created;
bool Validated = false; bool Validated=false;
}; };
class SMSSender : public SubSystemServer { class SMSSender : public SubSystemServer {
public: public:
static SMSSender *instance() { static SMSSender *instance() {
static auto *instance_ = new SMSSender; static SMSSender instance;
return instance_; return &instance;
} }
int Start() final; int Start() final;

View File

@@ -43,7 +43,6 @@ namespace OpenWifi {
if(!Running_) if(!Running_)
return false; return false;
try {
Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_); Aws::SNS::SNSClient sns(AwsCreds_,AwsConfig_);
Aws::SNS::Model::PublishRequest psms_req; Aws::SNS::Model::PublishRequest psms_req;
psms_req.SetMessage(Message.c_str()); psms_req.SetMessage(Message.c_str());
@@ -57,11 +56,6 @@ namespace OpenWifi {
std::string ErrMsg{psms_out.GetError().GetMessage()}; std::string ErrMsg{psms_out.GetError().GetMessage()};
Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg)); Logger_.debug(Poco::format("SMS NOT sent to %s: %s",PhoneNumber, ErrMsg));
return false; return false;
} catch (...) {
}
Logger_.debug(Poco::format("SMS NOT sent to %s: failure in SMS service",PhoneNumber));
return false;
} }
} }

View File

@@ -9,9 +9,12 @@
#include "Poco/Net/SMTPClientSession.h" #include "Poco/Net/SMTPClientSession.h"
#include "Poco/Net/SecureSMTPClientSession.h" #include "Poco/Net/SecureSMTPClientSession.h"
#include "Poco/Net/StringPartSource.h" #include "Poco/Net/StringPartSource.h"
#include "Poco/Path.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include "Poco/Net/SSLManager.h" #include "Poco/Net/SSLManager.h"
#include "Poco/Net/Context.h" #include "Poco/Net/Context.h"
#include "Poco/Net/InvalidCertificateHandler.h"
#include "Poco/Net/AcceptCertificateHandler.h"
#include "SMTPMailerService.h" #include "SMTPMailerService.h"
#include "framework/MicroService.h" #include "framework/MicroService.h"
@@ -20,8 +23,6 @@
namespace OpenWifi { namespace OpenWifi {
void SMTPMailerService::LoadMyConfig() { void SMTPMailerService::LoadMyConfig() {
Enabled_ = MicroService::instance().ConfigGetBool("mailer.enabled",false);
if(Enabled_) {
MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname"); MailHost_ = MicroService::instance().ConfigGetString("mailer.hostname");
SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username"); SenderLoginUserName_ = MicroService::instance().ConfigGetString("mailer.username");
SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password"); SenderLoginPassword_ = MicroService::instance().ConfigGetString("mailer.password");
@@ -29,11 +30,8 @@ namespace OpenWifi {
LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod"); LoginMethod_ = MicroService::instance().ConfigGetString("mailer.loginmethod");
MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port"); MailHostPort_ = (int) MicroService::instance().ConfigGetInt("mailer.port");
TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir()); TemplateDir_ = MicroService::instance().ConfigPath("mailer.templates", MicroService::instance().DataDir());
MailRetry_ = (int) MicroService::instance().ConfigGetInt("mailer.retry",2*60);
MailAbandon_ = (int) MicroService::instance().ConfigGetInt("mailer.abandon",2*60*60);
Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty()); Enabled_ = (!MailHost_.empty() && !SenderLoginPassword_.empty() && !SenderLoginUserName_.empty());
} }
}
int SMTPMailerService::Start() { int SMTPMailerService::Start() {
LoadMyConfig(); LoadMyConfig();
@@ -55,46 +53,57 @@ namespace OpenWifi {
bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) { bool SMTPMailerService::SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs) {
std::lock_guard G(Mutex_); std::lock_guard G(Mutex_);
PendingMessages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr),
/*
uint64_t Now = std::time(nullptr);
std::string RecipientLower = Poco::toLower(Recipient);
auto CE = Cache_.find(RecipientLower);
if(CE!=Cache_.end()) {
// only allow messages to the same user within 2 minutes
if(!((CE->second.LastRequest-Now)<30 && CE->second.HowManyRequests<10))
return false;
if(CE->second.LastRequest-Now>30) {
CE->second.LastRequest = Now;
CE->second.HowManyRequests=0;
} else {
CE->second.HowManyRequests++;
}
} else {
Cache_[RecipientLower] = MessageCacheEntry{.LastRequest=Now, .HowManyRequests=0};
}
*/
Messages_.push_back(MessageEvent{.Posted=(uint64_t )std::time(nullptr),
.LastTry=0, .LastTry=0,
.Sent=0, .Sent=0,
.File=Poco::File(TemplateDir_ + "/" +Name), .File=Poco::File(TemplateDir_ + "/" +Name),
.Attrs=Attrs}); .Attrs=Attrs});
return true; return true;
} }
void SMTPMailerService::run() { void SMTPMailerService::run() {
Running_ = true; Running_ = true;
while(Running_) { while(Running_) {
Poco::Thread::trySleep(10000); Poco::Thread::trySleep(10000);
if(!Running_) if(!Running_)
break; break;
{ {
std::lock_guard G(Mutex_); std::lock_guard G(Mutex_);
Messages_.splice(Messages_.end(),PendingMessages_);
uint64_t Now = std::time(nullptr);
for(auto &i:Messages_) {
if(i.Sent==0 && (i.LastTry==0 || (Now-i.LastTry)>120)) {
if (SendIt(i)) {
i.LastTry = i.Sent = std::time(nullptr);
} else
i.LastTry = std::time(nullptr);
}
} }
for(auto i=Messages_.begin();i!=Messages_.end();) { // Clean the list
if(!Running_) std::remove_if(Messages_.begin(),Messages_.end(),[Now](MessageEvent &E){ return (E.Sent!=0 || ((Now-E.LastTry)>(15*60)));});
break;
auto Recipient = i->Attrs.find(RECIPIENT_EMAIL)->second;
uint64_t Now = std::time(nullptr);
if((i->LastTry==0 || (Now-i->LastTry)>MailRetry_)) {
if (SendIt(*i)) {
Logger_.information(Poco::format("Attempting to deliver for mail '%s'.", Recipient));
i = Messages_.erase(i);
} else {
i->LastTry = Now;
++i;
}
} else if ((Now-i->Posted)>MailAbandon_) {
Logger_.information(Poco::format("Mail for '%s' has timed out and will not be sent.", Recipient));
i = Messages_.erase(i);
} else {
++i;
}
} }
} }
} }
@@ -106,12 +115,10 @@ namespace OpenWifi {
} }
bool SMTPMailerService::SendIt(const MessageEvent &Msg) { bool SMTPMailerService::SendIt(const MessageEvent &Msg) {
std::string Recipient;
try try
{ {
Poco::Net::MailMessage Message; Poco::Net::MailMessage Message;
Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second; std::string Recipient = Msg.Attrs.find(RECIPIENT_EMAIL)->second;
auto H1 = Msg.Attrs.find(SENDER); auto H1 = Msg.Attrs.find(SENDER);
std::string TheSender; std::string TheSender;
@@ -122,6 +129,7 @@ namespace OpenWifi {
} }
Message.setSender( TheSender ); Message.setSender( TheSender );
Logger_.information(Poco::format("Sending message to:%s from %s",Recipient,TheSender)); Logger_.information(Poco::format("Sending message to:%s from %s",Recipient,TheSender));
Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient)); Message.addRecipient(Poco::Net::MailRecipient(Poco::Net::MailRecipient::PRIMARY_RECIPIENT, Recipient));
Message.setSubject(Msg.Attrs.find(SUBJECT)->second); Message.setSubject(Msg.Attrs.find(SUBJECT)->second);
@@ -138,26 +146,21 @@ namespace OpenWifi {
auto Logo = Msg.Attrs.find(LOGO); auto Logo = Msg.Attrs.find(LOGO);
if(Logo!=Msg.Attrs.end()) { if(Logo!=Msg.Attrs.end()) {
try {
Poco::File LogoFile(AuthService::GetLogoAssetFileName()); Poco::File LogoFile(AuthService::GetLogoAssetFileName());
std::ifstream IF(LogoFile.path()); std::ifstream IF(LogoFile.path());
std::ostringstream OS; std::ostringstream OS;
Poco::StreamCopier::copyStream(IF, OS); Poco::StreamCopier::copyStream(IF, OS);
Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/png")); Message.addAttachment("logo", new Poco::Net::StringPartSource(OS.str(), "image/jpeg"));
} catch (...) {
Logger_.warning(Poco::format("Cannot add '%s' logo in email",AuthService::GetLogoAssetFileName()));
} }
}
Poco::SharedPtr<Poco::Net::AcceptCertificateHandler> ptrHandler_ = new Poco::Net::AcceptCertificateHandler(false);
Poco::Net::SecureSMTPClientSession session(MailHost_,MailHostPort_); Poco::Net::SecureSMTPClientSession session(MailHost_,MailHostPort_);
Poco::Net::Context::Params P;
auto ptrContext = Poco::AutoPtr<Poco::Net::Context> auto ptrContext = Poco::AutoPtr<Poco::Net::Context>
(new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "", (new Poco::Net::Context(Poco::Net::Context::CLIENT_USE, "", "", "",
Poco::Net::Context::VERIFY_RELAXED, 9, true, Poco::Net::Context::VERIFY_RELAXED, 9, true,
"ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH")); "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"));
Poco::Net::SSLManager::instance().initializeClient(nullptr, Poco::Net::SSLManager::instance().initializeClient(nullptr,
ptrHandler_, &ptrHandler_,
ptrContext); ptrContext);
session.login(); session.login();
session.startTLS(ptrContext); session.startTLS(ptrContext);
@@ -174,9 +177,6 @@ namespace OpenWifi {
{ {
Logger_.log(E); Logger_.log(E);
} }
catch (const std::exception &E) {
Logger_.warning(Poco::format("Cannot send message to:%s, error: %s",Recipient, E.what()));
}
return false; return false;
} }

View File

@@ -59,8 +59,8 @@ namespace OpenWifi {
class SMTPMailerService : public SubSystemServer, Poco::Runnable { class SMTPMailerService : public SubSystemServer, Poco::Runnable {
public: public:
static SMTPMailerService *instance() { static SMTPMailerService *instance() {
static auto * instance_ = new SMTPMailerService; static SMTPMailerService instance;
return instance_; return & instance;
} }
struct MessageEvent { struct MessageEvent {
@@ -71,35 +71,41 @@ namespace OpenWifi {
MessageAttributes Attrs; MessageAttributes Attrs;
}; };
struct MessageCacheEntry {
uint64_t LastRequest=0;
uint64_t HowManyRequests=0;
};
void run() override; void run() override;
int Start() override; int Start() override;
void Stop() override; void Stop() override;
bool SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs); bool SendMessage(const std::string &Recipient, const std::string &Name, const MessageAttributes &Attrs);
bool SendIt(const MessageEvent &Msg); bool SendIt(const MessageEvent &Msg);
void LoadMyConfig(); void LoadMyConfig();
void reinitialize(Poco::Util::Application &self) override; void reinitialize(Poco::Util::Application &self) override;
bool Enabled() const { return Enabled_; } bool Enabled() const { return Enabled_; }
private: private:
std::string MailHost_; std::string MailHost_;
std::string Sender_; std::string Sender_;
int MailHostPort_=25; int MailHostPort_=25;
int MailRetry_=2*60;
int MailAbandon_=2*60*20;
std::string SenderLoginUserName_; std::string SenderLoginUserName_;
std::string SenderLoginPassword_; std::string SenderLoginPassword_;
std::string LoginMethod_ = "login"; std::string LoginMethod_ = "login";
std::string LogoFileName_;
std::string TemplateDir_; std::string TemplateDir_;
std::list<MessageEvent> Messages_; std::list<MessageEvent> Messages_;
std::list<MessageEvent> PendingMessages_; std::map<std::string,MessageCacheEntry> Cache_;
Poco::Thread SenderThr_; Poco::Thread SenderThr_;
std::atomic_bool Running_=false; std::atomic_bool Running_=false;
bool Enabled_=false; bool Enabled_=false;
Poco::Net::AcceptCertificateHandler ptrHandler_;
SMTPMailerService() noexcept: SMTPMailerService() noexcept:
SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer") SubSystemServer("SMTPMailer", "MAILER-SVR", "smtpmailer"),
ptrHandler_(false)
{ {
std::string E{"SHA512"};
} }
}; };

View File

@@ -16,28 +16,12 @@ namespace OpenWifi {
StorageClass::Start(); StorageClass::Start();
Create_Tables(); Create_Tables();
InitializeDefaultUser(); InitializeDefaultUser();
Archivercallback_ = std::make_unique<Poco::TimerCallback<Archiver>>(Archiver_,&Archiver::onTimer);
Timer_.setStartInterval( 5 * 60 * 1000); // first run in 5 minutes
Timer_.setPeriodicInterval(1 * 60 * 60 * 1000); // 1 hours
Timer_.start(*Archivercallback_);
return 0; return 0;
} }
void Storage::Stop() { void Storage::Stop() {
Logger_.notice("Stopping."); Logger_.notice("Stopping.");
Timer_.stop();
StorageClass::Stop(); StorageClass::Stop();
} }
void Archiver::onTimer(Poco::Timer &timer) {
Poco::Logger &logger = Poco::Logger::get("STORAGE-ARCHIVER");
logger.information("Squiggy the DB: removing old tokens.");
StorageService()->CleanExpiredTokens();
logger.information("Squiggy the DB: removing old actionLinks.");
StorageService()->CleanOldActionLinks();
}
} }
// namespace // namespace

View File

@@ -13,8 +13,6 @@
#include "framework/StorageClass.h" #include "framework/StorageClass.h"
#include "AuthService.h" #include "AuthService.h"
#include "Poco/Timer.h"
namespace OpenWifi { namespace OpenWifi {
static const std::string AllEmailTemplatesFieldsForCreation { static const std::string AllEmailTemplatesFieldsForCreation {
@@ -29,12 +27,6 @@ namespace OpenWifi {
}; };
class Archiver {
public:
void onTimer(Poco::Timer & timer);
private:
};
class Storage : public StorageClass { class Storage : public StorageClass {
public: public:
@@ -84,8 +76,8 @@ namespace OpenWifi {
} }
static Storage *instance() { static Storage *instance() {
static auto * instance_ = new Storage; static Storage instance;
return instance_; return &instance;
} }
int Start() override; int Start() override;
@@ -112,12 +104,12 @@ namespace OpenWifi {
bool GetAvatar(const std::string & Admin, std::string &Id, Poco::TemporaryFile &FileName, std::string &Type, std::string & Name); bool GetAvatar(const std::string & Admin, std::string &Id, Poco::TemporaryFile &FileName, std::string &Type, std::string & Name);
bool DeleteAvatar(const std::string & Admin, std::string &Id); bool DeleteAvatar(const std::string & Admin, std::string &Id);
bool AddToken(std::string &UserId, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut); bool AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut);
bool RevokeToken( std::string & Token ); bool RevokeToken( std::string & Token );
bool IsTokenRevoked( std::string & Token ); bool IsTokenRevoked( std::string & Token );
bool CleanExpiredTokens(); bool CleanRevokedTokens( uint64_t Oldest );
bool RevokeAllTokens( std::string & UserName ); bool RevokeAllTokens( std::string & UserName );
bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate); bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo);
/* /*
* All ActionLinks functions * All ActionLinks functions
@@ -129,7 +121,6 @@ namespace OpenWifi {
bool SentAction(std::string &ActionId); bool SentAction(std::string &ActionId);
bool GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A); bool GetActionLink(std::string &ActionId, SecurityObjects::ActionLink &A);
bool GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max=200); bool GetActions(std::vector<SecurityObjects::ActionLink> &Links, uint64_t Max=200);
void CleanOldActionLinks();
private: private:
int Create_Tables(); int Create_Tables();
@@ -137,13 +128,6 @@ namespace OpenWifi {
int Create_AvatarTable(); int Create_AvatarTable();
int Create_TokensTable(); int Create_TokensTable();
int Create_ActionLinkTable(); int Create_ActionLinkTable();
Poco::Timer Timer_;
Archiver Archiver_;
std::unique_ptr<Poco::TimerCallback<Archiver>> Archivercallback_;
/// This is to support a mistake that was deployed...
void ReplaceOldDefaultUUID();
}; };
inline Storage * StorageService() { return Storage::instance(); }; inline Storage * StorageService() { return Storage::instance(); };

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
//
// Created by stephane bourque on 2021-09-14.
//
#ifndef OWPROV_CONFIGURATIONVALIDATOR_H
#define OWPROV_CONFIGURATIONVALIDATOR_H
#include <nlohmann/json-schema.hpp>
#include "framework/MicroService.h"
using nlohmann::json;
using nlohmann::json_schema::json_validator;
namespace OpenWifi {
class ConfigurationValidator : public SubSystemServer {
public:
static ConfigurationValidator *instance() {
if(instance_== nullptr)
instance_ = new ConfigurationValidator;
return instance_;
}
bool Validate(const std::string &C, std::string &Error);
static void my_format_checker(const std::string &format, const std::string &value);
int Start() override;
void Stop() override;
void reinitialize(Poco::Util::Application &self) override;
private:
static ConfigurationValidator * instance_;
bool Initialized_=false;
bool Working_=false;
void Init();
std::unique_ptr<json_validator> Validator_=std::make_unique<json_validator>(nullptr, my_format_checker);
ConfigurationValidator():
SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") {
}
};
inline ConfigurationValidator * ConfigurationValidator() { return ConfigurationValidator::instance(); }
inline bool ValidateUCentralConfiguration(const std::string &C, std::string &Error) { return ConfigurationValidator::instance()->Validate(C, Error); }
}
#endif //OWPROV_CONFIGURATIONVALIDATOR_H

View File

@@ -69,8 +69,6 @@ using namespace std::chrono_literals;
#include "RESTObjects/RESTAPI_SecurityObjects.h" #include "RESTObjects/RESTAPI_SecurityObjects.h"
#include "nlohmann/json.hpp" #include "nlohmann/json.hpp"
#include "ow_version.h"
namespace OpenWifi { namespace OpenWifi {
enum UNAUTHORIZED_REASON { enum UNAUTHORIZED_REASON {
@@ -82,9 +80,7 @@ namespace OpenWifi {
PASSWORD_INVALID, PASSWORD_INVALID,
INTERNAL_ERROR, INTERNAL_ERROR,
ACCESS_DENIED, ACCESS_DENIED,
INVALID_TOKEN, INVALID_TOKEN
EXPIRED_TOKEN,
RATE_LIMIT_EXCEEDED
}; };
class AppServiceRegistry { class AppServiceRegistry {
@@ -92,8 +88,8 @@ namespace OpenWifi {
inline AppServiceRegistry(); inline AppServiceRegistry();
static AppServiceRegistry & instance() { static AppServiceRegistry & instance() {
static AppServiceRegistry *instance_= new AppServiceRegistry; static AppServiceRegistry instance;
return *instance_; return instance;
} }
inline ~AppServiceRegistry() { inline ~AppServiceRegistry() {
@@ -1437,8 +1433,8 @@ namespace OpenWifi {
}; };
static RESTAPI_RateLimiter *instance() { static RESTAPI_RateLimiter *instance() {
static RESTAPI_RateLimiter * instance_ = new RESTAPI_RateLimiter; static RESTAPI_RateLimiter instance;
return instance_; return &instance;
} }
inline int Start() final { return 0;}; inline int Start() final { return 0;};
@@ -1448,18 +1444,18 @@ namespace OpenWifi {
Poco::URI uri(R.getURI()); Poco::URI uri(R.getURI());
auto H = str_hash(uri.getPath() + R.clientAddress().host().toString()); auto H = str_hash(uri.getPath() + R.clientAddress().host().toString());
auto E = Cache_.get(H); auto E = Cache_.get(H);
auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); 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()) { if(E.isNull()) {
Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1}); Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1});
Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString()));
return false; return false;
} }
if((Now-E->Start)<Period) { if((Now-E->Start)<Period) {
E->Count++; E->Count++;
Cache_.update(H,E); Cache_.update(H,E);
if(E->Count > MaxCalls) { if(E->Count > MaxCalls)
Logger_.warning(Poco::format("RATE-LIMIT-EXCEEDED: from '%s'", R.clientAddress().toString()));
return true; return true;
}
return false; return false;
} }
E->Start = Now; E->Start = Now;
@@ -1527,23 +1523,20 @@ namespace OpenWifi {
Request = &RequestIn; Request = &RequestIn;
Response = &ResponseIn; Response = &ResponseIn;
if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) { if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls))
return UnAuthorized("Rate limit exceeded.",RATE_LIMIT_EXCEEDED); return;
}
if (!ContinueProcessing()) if (!ContinueProcessing())
return; return;
bool Expired=false; if (AlwaysAuthorize_ && !IsAuthorized()) {
if (AlwaysAuthorize_ && !IsAuthorized(Expired)) { return;
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken, EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::InvalidCredentials, ACCESS_DENIED);
} }
std::string Reason; std::string Reason;
if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) { if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) {
return UnAuthorized(Reason, ACCESS_DENIED); UnAuthorized(Reason, ACCESS_DENIED);
return;
} }
ParseParameters(); ParseParameters();
@@ -1881,7 +1874,7 @@ namespace OpenWifi {
return true; return true;
} }
inline bool IsAuthorized(bool & Expired); inline bool IsAuthorized();
inline void ReturnObject(Poco::JSON::Object &Object) { inline void ReturnObject(Poco::JSON::Object &Object) {
PrepareResponse(); PrepareResponse();
@@ -1902,7 +1895,7 @@ namespace OpenWifi {
QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, ""); QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0); QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0);
QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0); QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0);
QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0); QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 1);
QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100); QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100);
QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, ""); QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, "");
QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, ""); QB_.Select = GetParameter(RESTAPI::Protocol::SELECT, "");
@@ -1914,7 +1907,7 @@ namespace OpenWifi {
QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false); QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false);
if(QB_.Offset<1) if(QB_.Offset<1)
QB_.Offset=0; QB_.Offset=1;
return true; return true;
} }
@@ -2083,50 +2076,6 @@ namespace OpenWifi {
Poco::JSON::Object Body_; Poco::JSON::Object Body_;
}; };
class KafkaProducer : public Poco::Runnable {
public:
inline void run();
void Start() {
if(!Running_) {
Running_=true;
Worker_.start(*this);
}
}
void Stop() {
if(Running_) {
Running_=false;
Worker_.wakeUp();
Worker_.join();
}
}
private:
std::mutex Mutex_;
Poco::Thread Worker_;
std::atomic_bool Running_=false;
};
class KafkaConsumer : public Poco::Runnable {
public:
inline void run();
void Start() {
if(!Running_) {
Running_=true;
Worker_.start(*this);
}
}
void Stop() {
if(Running_) {
Running_=false;
Worker_.wakeUp();
Worker_.join();
}
}
private:
std::mutex Mutex_;
Poco::Thread Worker_;
std::atomic_bool Running_=false;
};
class KafkaManager : public SubSystemServer { class KafkaManager : public SubSystemServer {
public: public:
struct KMessage { struct KMessage {
@@ -2135,32 +2084,33 @@ namespace OpenWifi {
PayLoad; PayLoad;
}; };
friend class KafkaConsumer;
friend class KafkaProducer;
inline void initialize(Poco::Util::Application & self) override; inline void initialize(Poco::Util::Application & self) override;
static KafkaManager *instance() { static KafkaManager *instance() {
static KafkaManager * instance_ = new KafkaManager; static KafkaManager instance;
return instance_; return &instance;
} }
inline int Start() override { inline int Start() override {
if(!KafkaEnabled_) if(!KafkaEnabled_)
return 0; return 0;
ConsumerThr_.Start(); ProducerThr_ = std::make_unique<std::thread>([this]() { this->ProducerThr(); });
ProducerThr_.Start(); ConsumerThr_ = std::make_unique<std::thread>([this]() { this->ConsumerThr(); });
return 0; return 0;
} }
inline void Stop() override { inline void Stop() override {
if(KafkaEnabled_) { if(KafkaEnabled_) {
ProducerThr_.Stop(); ProducerRunning_ = ConsumerRunning_ = false;
ConsumerThr_.Stop(); ProducerThr_->join();
ConsumerThr_->join();
return; return;
} }
} }
inline void ProducerThr();
inline void ConsumerThr();
inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true ) { inline void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true ) {
if(KafkaEnabled_) { if(KafkaEnabled_) {
std::lock_guard G(Mutex_); std::lock_guard G(Mutex_);
@@ -2213,13 +2163,18 @@ namespace OpenWifi {
// void WakeUp(); // void WakeUp();
private: private:
std::mutex ProducerMutex_;
std::mutex ConsumerMutex_;
bool KafkaEnabled_ = false; bool KafkaEnabled_ = false;
std::atomic_bool ProducerRunning_ = false;
std::atomic_bool ConsumerRunning_ = false;
std::queue<KMessage> Queue_; std::queue<KMessage> Queue_;
std::string SystemInfoWrapper_; std::string SystemInfoWrapper_;
std::unique_ptr<std::thread> ConsumerThr_;
std::unique_ptr<std::thread> ProducerThr_;
int FunctionId_=1; int FunctionId_=1;
Types::NotifyTable Notifiers_; Types::NotifyTable Notifiers_;
KafkaProducer ProducerThr_; std::unique_ptr<cppkafka::Configuration> Config_;
KafkaConsumer ConsumerThr_;
inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) { inline void PartitionAssignment(const cppkafka::TopicPartitionList& partitions) {
Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition())); Logger_.information(Poco::format("Partition assigned: %Lu...",(uint64_t )partitions.front().get_partition()));
@@ -2244,8 +2199,8 @@ namespace OpenWifi {
} }
static AuthClient *instance() { static AuthClient *instance() {
static AuthClient * instance_ = new AuthClient; static AuthClient instance;
return instance_; return &instance;
} }
inline int Start() override { inline int Start() override {
@@ -2253,20 +2208,25 @@ namespace OpenWifi {
} }
inline void Stop() override { inline void Stop() override {
Cache_.clear();
} }
inline void RemovedCachedToken(const std::string &Token) { inline void RemovedCachedToken(const std::string &Token) {
std::lock_guard G(Mutex_); std::lock_guard G(Mutex_);
Cache_.remove(Token); UserCache_.erase(Token);
} }
inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) { inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) {
return ((T.expires_in_+T.created_)<std::time(nullptr)); return ((T.expires_in_+T.created_)<std::time(nullptr));
} }
inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) { inline bool IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) {
try { std::lock_guard G(Mutex_);
auto User = UserCache_.find(SessionToken);
if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) {
UInfo = User->second;
return true;
} else {
Types::StringPairVec QueryData; Types::StringPairVec QueryData;
QueryData.push_back(std::make_pair("token",SessionToken)); QueryData.push_back(std::make_pair("token",SessionToken));
OpenAPIRequestGet Req( uSERVICE_SECURITY, OpenAPIRequestGet Req( uSERVICE_SECURITY,
@@ -2276,39 +2236,49 @@ namespace OpenWifi {
Poco::JSON::Object::Ptr Response; Poco::JSON::Object::Ptr Response;
if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) { if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) {
if(Response->has("tokenInfo") && Response->has("userInfo")) { if(Response->has("tokenInfo") && Response->has("userInfo")) {
UInfo.from_json(Response); SecurityObjects::UserInfoAndPolicy P;
if(IsTokenExpired(UInfo.webtoken)) { P.from_json(Response);
Expired = true; UserCache_[SessionToken] = P;
return false; UInfo = P;
} }
Expired = false;
Cache_.update(SessionToken, UInfo);
return true; return true;
} }
}
} catch (...) {
} }
Expired = false;
return false; return false;
} }
inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) { inline bool IsTokenAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo) {
auto User = Cache_.get(SessionToken); std::lock_guard G(Mutex_);
if(!User.isNull()) {
if(IsTokenExpired(User->webtoken)) { auto User = UserCache_.find(SessionToken);
Expired = true; if(User != UserCache_.end() && !IsTokenExpired(User->second.webtoken)) {
return false; UInfo = User->second;
return true;
} else {
Types::StringPairVec QueryData;
QueryData.push_back(std::make_pair("token",SessionToken));
OpenAPIRequestGet Req(uSERVICE_SECURITY,
"/api/v1/validateToken",
QueryData,
5000);
Poco::JSON::Object::Ptr Response;
if(Req.Do(Response)==Poco::Net::HTTPResponse::HTTP_OK) {
if(Response->has("tokenInfo") && Response->has("userInfo")) {
SecurityObjects::UserInfoAndPolicy P;
P.from_json(Response);
UserCache_[SessionToken] = P;
UInfo = P;
} }
Expired = false;
UInfo = *User;
return true; return true;
} }
return RetrieveTokenInformation(SessionToken, UInfo, Expired);
}
return false;
} }
private: private:
Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy> Cache_{1024,1200000 }; OpenWifi::SecurityObjects::UserInfoCache UserCache_;
}; };
inline AuthClient * AuthClient() { return AuthClient::instance(); } inline AuthClient * AuthClient() { return AuthClient::instance(); }
@@ -2368,14 +2338,14 @@ namespace OpenWifi {
} }
static ALBHealthCheckServer *instance() { static ALBHealthCheckServer *instance() {
static ALBHealthCheckServer * instance = new ALBHealthCheckServer; static ALBHealthCheckServer instance;
return instance; return &instance;
} }
inline int Start() override; inline int Start() override;
inline void Stop() override { inline void Stop() override {
if(Running_) if(Server_)
Server_->stop(); Server_->stop();
} }
@@ -2383,7 +2353,6 @@ namespace OpenWifi {
std::unique_ptr<Poco::Net::HTTPServer> Server_; std::unique_ptr<Poco::Net::HTTPServer> Server_;
std::unique_ptr<Poco::Net::ServerSocket> Socket_; std::unique_ptr<Poco::Net::ServerSocket> Socket_;
int Port_ = 0; int Port_ = 0;
std::atomic_bool Running_=false;
}; };
inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); } inline ALBHealthCheckServer * ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); }
@@ -2398,18 +2367,17 @@ namespace OpenWifi {
class RESTAPI_server : public SubSystemServer { class RESTAPI_server : public SubSystemServer {
public: public:
static RESTAPI_server *instance() { static RESTAPI_server *instance() {
static RESTAPI_server *instance_ = new RESTAPI_server; static RESTAPI_server instance;
return instance_; return &instance;
} }
int Start() override; int Start() override;
inline void Stop() override { inline void Stop() override {
Logger_.information("Stopping "); Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ ) for( const auto & svr : RESTServers_ )
svr->stop(); svr->stop();
Pool_.joinAll();
RESTServers_.clear(); RESTServers_.clear();
}
}
inline void reinitialize(Poco::Util::Application &self) override; inline void reinitialize(Poco::Util::Application &self) override;
inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) { inline Poco::Net::HTTPRequestHandler *CallServer(const char *Path) {
@@ -2464,7 +2432,7 @@ namespace OpenWifi {
if(!Svr.RootCA().empty()) if(!Svr.RootCA().empty())
Svr.LogCas(Logger_); Svr.LogCas(Logger_);
Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams; auto Params = new Poco::Net::HTTPServerParams;
Params->setMaxThreads(50); Params->setMaxThreads(50);
Params->setMaxQueued(200); Params->setMaxQueued(200);
Params->setKeepAlive(true); Params->setKeepAlive(true);
@@ -2481,8 +2449,8 @@ namespace OpenWifi {
public: public:
static RESTAPI_InternalServer *instance() { static RESTAPI_InternalServer *instance() {
static RESTAPI_InternalServer *instance_ = new RESTAPI_InternalServer; static RESTAPI_InternalServer instance;
return instance_; return &instance;
} }
inline int Start() override; inline int Start() override;
@@ -2490,7 +2458,7 @@ namespace OpenWifi {
Logger_.information("Stopping "); Logger_.information("Stopping ");
for( const auto & svr : RESTServers_ ) for( const auto & svr : RESTServers_ )
svr->stop(); svr->stop();
Pool_.stopAll(); RESTServers_.clear();
} }
inline void reinitialize(Poco::Util::Application &self) override; inline void reinitialize(Poco::Util::Application &self) override;
@@ -2507,6 +2475,7 @@ namespace OpenWifi {
RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi") RESTAPI_InternalServer() noexcept: SubSystemServer("RESTAPIInternalServer", "REST-ISRV", "openwifi.internal.restapi")
{ {
} }
}; };
inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); }; inline RESTAPI_InternalServer * RESTAPI_InternalServer() { return RESTAPI_InternalServer::instance(); };
@@ -2524,7 +2493,7 @@ namespace OpenWifi {
} }
private: private:
Poco::Logger & Logger_; Poco::Logger & Logger_;
RESTAPI_GenericServer & Server_; RESTAPI_GenericServer &Server_;
}; };
inline int RESTAPI_InternalServer::Start() { inline int RESTAPI_InternalServer::Start() {
@@ -2585,13 +2554,11 @@ namespace OpenWifi {
DAEMON_BUS_TIMER(BusTimer), DAEMON_BUS_TIMER(BusTimer),
SubSystems_(std::move(Subsystems)) { SubSystems_(std::move(Subsystems)) {
instance_ = this; instance_ = this;
RandomEngine_.seed(std::chrono::steady_clock::now().time_since_epoch().count());
} }
[[nodiscard]] std::string Version() { return Version_; } [[nodiscard]] std::string Version() { return Version_; }
[[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; } [[nodiscard]] const Poco::SharedPtr<Poco::Crypto::RSAKey> & Key() { return AppKey_; }
[[nodiscard]] inline const std::string & DataDir() { return DataDir_; } [[nodiscard]] inline const std::string & DataDir() { return DataDir_; }
[[nodiscard]] inline const std::string & WWWAssetsDir() { return WWWAssetsDir_; }
[[nodiscard]] bool Debug() const { return DebugMode_; } [[nodiscard]] bool Debug() const { return DebugMode_; }
[[nodiscard]] uint64_t ID() const { return ID_; } [[nodiscard]] uint64_t ID() const { return ID_; }
[[nodiscard]] std::string Hash() const { return MyHash_; }; [[nodiscard]] std::string Hash() const { return MyHash_; };
@@ -2604,13 +2571,6 @@ namespace OpenWifi {
static inline uint64_t GetPID() { return Poco::Process::id(); }; static inline uint64_t GetPID() { return Poco::Process::id(); };
[[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; }; [[nodiscard]] inline const std::string GetPublicAPIEndPoint() { return MyPublicEndPoint_ + "/api/v1"; };
[[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;}; [[nodiscard]] inline const std::string & GetUIURI() const { return UIURI_;};
[[nodiscard]] inline uint64_t Random(uint64_t ceiling) {
return (RandomEngine_() % ceiling);
}
[[nodiscard]] inline uint64_t Random(uint64_t min, uint64_t max) {
return ((RandomEngine_() % (max-min)) + min);
}
inline void Exit(int Reason); inline void Exit(int Reason);
inline void BusMessageReceived(const std::string &Key, const std::string & Message); inline void BusMessageReceived(const std::string &Key, const std::string & Message);
@@ -2632,7 +2592,7 @@ namespace OpenWifi {
inline void InitializeSubSystemServers(); inline void InitializeSubSystemServers();
inline void StartSubSystemServers(); inline void StartSubSystemServers();
inline void StopSubSystemServers(); inline void StopSubSystemServers();
[[nodiscard]] static inline std::string CreateUUID(); [[nodiscard]] inline std::string CreateUUID();
inline bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level); inline bool SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level);
inline void Reload(const std::string &Sub); inline void Reload(const std::string &Sub);
inline Types::StringVec GetSubSystems() const; inline Types::StringVec GetSubSystems() const;
@@ -2662,10 +2622,9 @@ namespace OpenWifi {
std::string ConfigFileName_; std::string ConfigFileName_;
Poco::UUIDGenerator UUIDGenerator_; Poco::UUIDGenerator UUIDGenerator_;
uint64_t ID_ = 1; uint64_t ID_ = 1;
Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_; Poco::SharedPtr<Poco::Crypto::RSAKey> AppKey_ = nullptr;
bool DebugMode_ = false; bool DebugMode_ = false;
std::string DataDir_; std::string DataDir_;
std::string WWWAssetsDir_;
SubSystemVec SubSystems_; SubSystemVec SubSystems_;
Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory(); Poco::Crypto::CipherFactory & CipherFactory_ = Poco::Crypto::CipherFactory::defaultFactory();
Poco::Crypto::Cipher * Cipher_ = nullptr; Poco::Crypto::Cipher * Cipher_ = nullptr;
@@ -2675,10 +2634,9 @@ namespace OpenWifi {
std::string MyPrivateEndPoint_; std::string MyPrivateEndPoint_;
std::string MyPublicEndPoint_; std::string MyPublicEndPoint_;
std::string UIURI_; std::string UIURI_;
std::string Version_{ OW_VERSION::VERSION + "("+ OW_VERSION::BUILD + ")" + " - " + OW_VERSION::HASH }; std::string Version_{std::string(APP_VERSION) + "("+ BUILD_NUMBER + ")"};
BusEventManager BusEventManager_; BusEventManager BusEventManager_;
std::mutex InfraMutex_; std::mutex InfraMutex_;
std::default_random_engine RandomEngine_;
std::string DAEMON_PROPERTIES_FILENAME; std::string DAEMON_PROPERTIES_FILENAME;
std::string DAEMON_ROOT_ENV_VAR; std::string DAEMON_ROOT_ENV_VAR;
@@ -2855,9 +2813,6 @@ namespace OpenWifi {
logger().log(E); logger().log(E);
} }
} }
WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets","");
if(WWWAssetsDir_.empty())
WWWAssetsDir_ = DataDir_;
LoadMyConfig(); LoadMyConfig();
@@ -2964,41 +2919,12 @@ namespace OpenWifi {
inline void MicroService::StopSubSystemServers() { inline void MicroService::StopSubSystemServers() {
BusEventManager_.Stop(); BusEventManager_.Stop();
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) { for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i)
(*i)->Stop(); (*i)->Stop();
} }
}
[[nodiscard]] inline std::string MicroService::CreateUUID() { [[nodiscard]] inline std::string MicroService::CreateUUID() {
static std::random_device rd; return UUIDGenerator_.create().toString();
static std::mt19937_64 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::stringstream ss;
int i;
ss << std::hex;
for (i = 0; i < 8; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 4; i++) {
ss << dis(gen);
}
ss << "-4";
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
ss << dis2(gen);
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 12; i++) {
ss << dis(gen);
};
return ss.str();
} }
inline bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) { inline bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
@@ -3012,6 +2938,7 @@ namespace OpenWifi {
} }
return true; return true;
} else { } else {
// std::cout << "Sub:" << SubSystem << " Level:" << Level << std::endl;
for (auto i : SubSystems_) { for (auto i : SubSystems_) {
if (Sub == Poco::toLower(i->Name())) { if (Sub == Poco::toLower(i->Name())) {
i->Logger().setLevel(P); i->Logger().setLevel(P);
@@ -3161,6 +3088,7 @@ namespace OpenWifi {
StartSubSystemServers(); StartSubSystemServers();
waitForTerminationRequest(); waitForTerminationRequest();
StopSubSystemServers(); StopSubSystemServers();
logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME)); logger.notice(Poco::format("Stopped %s...",DAEMON_APP_NAME));
} }
@@ -3240,7 +3168,6 @@ namespace OpenWifi {
inline int ALBHealthCheckServer::Start() { inline int ALBHealthCheckServer::Start() {
if(MicroService::instance().ConfigGetBool("alb.enable",false)) { if(MicroService::instance().ConfigGetBool("alb.enable",false)) {
Running_=true;
Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015); Port_ = (int)MicroService::instance().ConfigGetInt("alb.port",15015);
Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_); Socket_ = std::make_unique<Poco::Net::ServerSocket>(Port_);
auto Params = new Poco::Net::HTTPServerParams; auto Params = new Poco::Net::HTTPServerParams;
@@ -3285,42 +3212,41 @@ namespace OpenWifi {
KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false); KafkaEnabled_ = MicroService::instance().ConfigGetBool("openwifi.kafka.enable",false);
} }
inline void KafkaProducer::run() { inline void KafkaManager::ProducerThr() {
cppkafka::Configuration Config({ cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") } { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }
}); });
KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" + SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
std::to_string(MicroService::instance().ID()) + std::to_string(MicroService::instance().ID()) +
R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() + R"lit( , "host" : ")lit" + MicroService::instance().PrivateEndPoint() +
R"lit(" } , "payload" : )lit" ; R"lit(" } , "payload" : )lit" ;
cppkafka::Producer Producer(Config); cppkafka::Producer Producer(Config);
Running_ = true; ProducerRunning_ = true;
while(Running_) { while(ProducerRunning_) {
std::this_thread::sleep_for(std::chrono::milliseconds(200)); std::this_thread::sleep_for(std::chrono::milliseconds(200));
try try
{ {
std::lock_guard G(Mutex_); std::lock_guard G(ProducerMutex_);
auto Num=0; auto Num=0;
while (!KafkaManager()->Queue_.empty()) { while (!Queue_.empty()) {
const auto M = KafkaManager()->Queue_.front(); const auto M = Queue_.front();
Producer.produce( Producer.produce(
cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad)); cppkafka::MessageBuilder(M.Topic).key(M.Key).payload(M.PayLoad));
KafkaManager()->Queue_.pop(); Queue_.pop();
Num++; Num++;
} }
if(Num) if(Num)
Producer.flush(); Producer.flush();
} catch (const cppkafka::HandleException &E ) { } catch (const cppkafka::HandleException &E ) {
KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()})); Logger_.warning(Poco::format("Caught a Kafka exception (producer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) { } catch (const Poco::Exception &E) {
KafkaManager()->Logger_.log(E); Logger_.log(E);
} }
} }
Producer.flush();
} }
inline void KafkaConsumer::run() { inline void KafkaManager::ConsumerThr() {
cppkafka::Configuration Config({ cppkafka::Configuration Config({
{ "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") }, { "client.id", MicroService::instance().ConfigGetString("openwifi.kafka.client.id") },
{ "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") }, { "metadata.broker.list", MicroService::instance().ConfigGetString("openwifi.kafka.brokerlist") },
@@ -3340,13 +3266,13 @@ namespace OpenWifi {
cppkafka::Consumer Consumer(Config); cppkafka::Consumer Consumer(Config);
Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) { Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) {
if(!partitions.empty()) { if(!partitions.empty()) {
KafkaManager()->Logger_.information(Poco::format("Partition assigned: %Lu...", Logger_.information(Poco::format("Partition assigned: %Lu...",
(uint64_t)partitions.front().get_partition())); (uint64_t)partitions.front().get_partition()));
} }
}); });
Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) { Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) {
if(!partitions.empty()) { if(!partitions.empty()) {
KafkaManager()->Logger_.information(Poco::format("Partition revocation: %Lu...", Logger_.information(Poco::format("Partition revocation: %Lu...",
(uint64_t)partitions.front().get_partition())); (uint64_t)partitions.front().get_partition()));
} }
}); });
@@ -3355,13 +3281,13 @@ namespace OpenWifi {
auto BatchSize = MicroService::instance().ConfigGetInt("openwifi.kafka.consumer.batchsize",20); auto BatchSize = MicroService::instance().ConfigGetInt("openwifi.kafka.consumer.batchsize",20);
Types::StringVec Topics; Types::StringVec Topics;
for(const auto &i:KafkaManager()->Notifiers_) for(const auto &i:Notifiers_)
Topics.push_back(i.first); Topics.push_back(i.first);
Consumer.subscribe(Topics); Consumer.subscribe(Topics);
Running_ = true; ConsumerRunning_ = true;
while(Running_) { while(ConsumerRunning_) {
try { try {
std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200)); std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200));
for(auto const &Msg:MsgVec) { for(auto const &Msg:MsgVec) {
@@ -3369,14 +3295,14 @@ namespace OpenWifi {
continue; continue;
if (Msg.get_error()) { if (Msg.get_error()) {
if (!Msg.is_eof()) { if (!Msg.is_eof()) {
KafkaManager()->Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string())); Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
}if(!AutoCommit) }if(!AutoCommit)
Consumer.async_commit(Msg); Consumer.async_commit(Msg);
continue; continue;
} }
std::lock_guard G(Mutex_); std::lock_guard G(ConsumerMutex_);
auto It = KafkaManager()->Notifiers_.find(Msg.get_topic()); auto It = Notifiers_.find(Msg.get_topic());
if (It != KafkaManager()->Notifiers_.end()) { if (It != Notifiers_.end()) {
Types::TopicNotifyFunctionList &FL = It->second; Types::TopicNotifyFunctionList &FL = It->second;
std::string Key{Msg.get_key()}; std::string Key{Msg.get_key()};
std::string Payload{Msg.get_payload()}; std::string Payload{Msg.get_payload()};
@@ -3389,12 +3315,11 @@ namespace OpenWifi {
Consumer.async_commit(Msg); Consumer.async_commit(Msg);
} }
} catch (const cppkafka::HandleException &E) { } catch (const cppkafka::HandleException &E) {
KafkaManager()->Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()})); Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) { } catch (const Poco::Exception &E) {
KafkaManager()->Logger_.log(E); Logger_.log(E);
} }
} }
Consumer.unsubscribe();
} }
inline void RESTAPI_server::reinitialize(Poco::Util::Application &self) { inline void RESTAPI_server::reinitialize(Poco::Util::Application &self) {
@@ -3615,9 +3540,11 @@ namespace OpenWifi {
if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P; Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response OK" << std::endl;
} else { } else {
Poco::JSON::Parser P; Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response: " << Response.getStatus() << std::endl;
} }
return Response.getStatus(); return Response.getStatus();
} }
@@ -3663,9 +3590,11 @@ namespace OpenWifi {
if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) { if(Response.getStatus()==Poco::Net::HTTPResponse::HTTP_OK) {
Poco::JSON::Parser P; Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response OK" << std::endl;
} else { } else {
Poco::JSON::Parser P; Poco::JSON::Parser P;
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>(); ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
// std::cout << "Response: " << Response.getStatus() << std::endl;
} }
return Response.getStatus(); return Response.getStatus();
} }
@@ -3691,9 +3620,9 @@ namespace OpenWifi {
} }
#ifdef TIP_SECURITY_SERVICE #ifdef TIP_SECURITY_SERVICE
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired ); [[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo );
#endif #endif
inline bool RESTAPIHandler::IsAuthorized( bool & Expired ) { inline bool RESTAPIHandler::IsAuthorized() {
if(Internal_) { if(Internal_) {
auto Allowed = MicroService::instance().IsValidAPIKEY(*Request); auto Allowed = MicroService::instance().IsValidAPIKEY(*Request);
if(!Allowed) { if(!Allowed) {
@@ -3723,9 +3652,9 @@ namespace OpenWifi {
} }
} }
#ifdef TIP_SECURITY_SERVICE #ifdef TIP_SECURITY_SERVICE
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired)) { if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_)) {
#else #else
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired)) { if (AuthClient()->IsAuthorized(*Request, SessionToken_, UserInfo_)) {
#endif #endif
if(Server_.LogIt(Request->getMethod(),true)) { if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s", Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s",
@@ -3742,6 +3671,7 @@ namespace OpenWifi {
Utils::FormatIPv6(Request->clientAddress().toString()), Utils::FormatIPv6(Request->clientAddress().toString()),
Request->getMethod(), Request->getURI())); Request->getMethod(), Request->getURI()));
} }
UnAuthorized("Invalid token", INVALID_TOKEN);
} }
return false; return false;
} }

View File

@@ -59,7 +59,6 @@ namespace OpenWifi::RESTAPI::Errors {
static const std::string UnrecognizedRequest{"Ill-formed request. Please consult documentation."}; static const std::string UnrecognizedRequest{"Ill-formed request. Please consult documentation."};
static const std::string MissingAuthenticationInformation{"Missing authentication information."}; static const std::string MissingAuthenticationInformation{"Missing authentication information."};
static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."}; static const std::string InsufficientAccessRights{"Insufficient access rights to complete the operation."};
static const std::string ExpiredToken{"Token has expired, user must login."};
} }
#endif //OWPROV_RESTAPI_ERRORS_H #endif //OWPROV_RESTAPI_ERRORS_H

View File

@@ -2,7 +2,8 @@
// Created by stephane bourque on 2021-10-06. // Created by stephane bourque on 2021-10-06.
// //
#pragma once #ifndef OPENWIFI_STORAGE_H
#define OPENWIFI_STORAGE_H
#include "Poco/Data/Session.h" #include "Poco/Data/Session.h"
#include "Poco/Data/SessionPool.h" #include "Poco/Data/SessionPool.h"
@@ -25,6 +26,13 @@ namespace OpenWifi {
class StorageClass : public SubSystemServer { class StorageClass : public SubSystemServer {
public: public:
/* static StorageClass *instance() {
if (instance_ == nullptr) {
instance_ = new StorageClass;
}
return instance_;
}
*/
StorageClass() noexcept: StorageClass() noexcept:
SubSystemServer("StorageClass", "STORAGE-SVR", "storage") SubSystemServer("StorageClass", "STORAGE-SVR", "storage")
{ {
@@ -48,18 +56,18 @@ namespace OpenWifi {
} }
void Stop() override { void Stop() override {
Pool_->shutdown();
} }
[[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) { [[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) {
if(dbType_==sqlite) { if(dbType_==sqlite) {
return " LIMIT " + std::to_string(From) + ", " + std::to_string(HowMany) + " "; return " LIMIT " + std::to_string(From-1) + ", " + std::to_string(HowMany) + " ";
} else if(dbType_==pgsql) { } else if(dbType_==pgsql) {
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
} else if(dbType_==mysql) { } else if(dbType_==mysql) {
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
} }
return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From) + " "; return " LIMIT " + std::to_string(HowMany) + " OFFSET " + std::to_string(From-1) + " ";
} }
inline std::string ConvertParams(const std::string & S) const { inline std::string ConvertParams(const std::string & S) const {
@@ -88,10 +96,10 @@ namespace OpenWifi {
inline int Setup_PostgreSQL(); inline int Setup_PostgreSQL();
protected: protected:
Poco::SharedPtr<Poco::Data::SessionPool> Pool_; std::unique_ptr<Poco::Data::SessionPool> Pool_;
Poco::Data::SQLite::Connector SQLiteConn_; std::unique_ptr<Poco::Data::SQLite::Connector> SQLiteConn_;
Poco::Data::PostgreSQL::Connector PostgresConn_; std::unique_ptr<Poco::Data::PostgreSQL::Connector> PostgresConn_;
Poco::Data::MySQL::Connector MySQLConn_; std::unique_ptr<Poco::Data::MySQL::Connector> MySQLConn_;
DBType dbType_ = sqlite; DBType dbType_ = sqlite;
}; };
@@ -106,8 +114,9 @@ namespace OpenWifi {
auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db"); auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db");
auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64); auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64);
auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60); auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60);
SQLiteConn_.registerConnector(); SQLiteConn_ = std::make_unique<Poco::Data::SQLite::Connector>();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 4, NumSessions, IdleTime)); SQLiteConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime);
return 0; return 0;
} }
@@ -130,8 +139,9 @@ namespace OpenWifi {
";port=" + Port + ";port=" + Port +
";compress=true;auto-reconnect=true"; ";compress=true;auto-reconnect=true";
MySQLConn_.registerConnector(); MySQLConn_ = std::make_unique<Poco::Data::MySQL::Connector>();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(MySQLConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); MySQLConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
return 0; return 0;
} }
@@ -156,11 +166,14 @@ namespace OpenWifi {
" port=" + Port + " port=" + Port +
" connect_timeout=" + ConnectionTimeout; " connect_timeout=" + ConnectionTimeout;
PostgresConn_.registerConnector(); PostgresConn_ = std::make_unique<Poco::Data::PostgreSQL::Connector>();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(PostgresConn_.name(), ConnectionStr, 4, NumSessions, IdleTime)); PostgresConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
return 0; return 0;
} }
#endif #endif
} }
#endif //OPENWIFI_STORAGE_H

View File

@@ -1,13 +0,0 @@
//
// Created by stephane bourque on 2021-12-06.
//
#pragma once
#include <string>
namespace OW_VERSION {
inline static const std::string VERSION{"@CMAKE_PROJECT_VERSION@"};
inline static const std::string BUILD{"@BUILD_NUM@"};
inline static const std::string HASH{"@GIT_HASH@"};
}

View File

@@ -183,19 +183,4 @@ namespace OpenWifi {
return false; return false;
} }
void Storage::CleanOldActionLinks() {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
uint64_t CutOff = std::time(nullptr) - (30 * 24 * 60 * 60);
std::string St{"DELETE from ActionLinks where Created<=?"};
Delete << ConvertParams(St),
Poco::Data::Keywords::use(CutOff);
Delete.execute();
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
} }

View File

@@ -15,7 +15,7 @@ namespace OpenWifi {
"RevocationDate BIGINT " "RevocationDate BIGINT "
*/ */
bool Storage::AddToken(std::string &UserID, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) { bool Storage::AddToken(std::string &UserName, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut) {
try { try {
Poco::Data::Session Sess = Pool_->get(); Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess); Poco::Data::Statement Insert(Sess);
@@ -29,7 +29,7 @@ namespace OpenWifi {
Poco::Data::Keywords::use(Token), Poco::Data::Keywords::use(Token),
Poco::Data::Keywords::use(RefreshToken), Poco::Data::Keywords::use(RefreshToken),
Poco::Data::Keywords::use(TokenType), Poco::Data::Keywords::use(TokenType),
Poco::Data::Keywords::use(UserID), Poco::Data::Keywords::use(UserName),
Poco::Data::Keywords::use(Now), Poco::Data::Keywords::use(Now),
Poco::Data::Keywords::use(Expires), Poco::Data::Keywords::use(Expires),
Poco::Data::Keywords::use(TimeOut), Poco::Data::Keywords::use(TimeOut),
@@ -42,24 +42,29 @@ namespace OpenWifi {
return false; return false;
} }
bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate) { bool Storage::GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo) {
try { try {
Poco::Data::Session Sess = Pool_->get(); Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess); Poco::Data::Statement Select(Sess);
RevocationDate = 0 ;
std::string St2{"SELECT " + AllTokensFieldsForSelect + " From Tokens WHERE Token=?"}; uint32_t RevocationDate = 0 ;
std::string St2{"SELECT " + AllTokensValuesForSelect + " From Tokens WHERE Token=?"};
Select << ConvertParams(St2), Select << ConvertParams(St2),
Poco::Data::Keywords::into(UInfo.webtoken.access_token_), Poco::Data::Keywords::into(UInfo.webtoken.access_token_),
Poco::Data::Keywords::into(UInfo.webtoken.refresh_token_), Poco::Data::Keywords::into(UInfo.webtoken.refresh_token_),
Poco::Data::Keywords::into(UInfo.webtoken.token_type_), Poco::Data::Keywords::into(UInfo.webtoken.token_type_),
Poco::Data::Keywords::into(UInfo.userinfo.Id), Poco::Data::Keywords::into(UInfo.userinfo.email),
Poco::Data::Keywords::into(UInfo.webtoken.created_), Poco::Data::Keywords::into(UInfo.webtoken.created_),
Poco::Data::Keywords::into(UInfo.webtoken.expires_in_), Poco::Data::Keywords::into(UInfo.webtoken.expires_in_),
Poco::Data::Keywords::into(UInfo.webtoken.idle_timeout_), Poco::Data::Keywords::into(UInfo.webtoken.idle_timeout_),
Poco::Data::Keywords::into(RevocationDate), Poco::Data::Keywords::into(RevocationDate),
Poco::Data::Keywords::use(Token); Poco::Data::Keywords::use(Token);
Select.execute(); Select.execute();
if(RevocationDate>0)
return false;
return true; return true;
} catch (const Poco::Exception &E) { } catch (const Poco::Exception &E) {
Logger_.log(E); Logger_.log(E);
@@ -111,15 +116,15 @@ namespace OpenWifi {
return false; return false;
} }
bool Storage::CleanExpiredTokens() { bool Storage::CleanRevokedTokens(uint64_t Oldest) {
try { try {
Poco::Data::Session Sess = Pool_->get(); Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess); Poco::Data::Statement Delete(Sess);
uint64_t Now = std::time(nullptr); uint64_t Now = std::time(nullptr);
std::string St2{"DELETE From Tokens WHERE (Created+Expires) <= ?"}; std::string St2{"DELETE From Tokens WHERE Created <= ?"};
Delete << ConvertParams(St2), Delete << ConvertParams(St2),
Poco::Data::Keywords::use(Now); Poco::Data::Keywords::use(Oldest);
Delete.execute(); Delete.execute();
return true; return true;
} catch (const Poco::Exception &E) { } catch (const Poco::Exception &E) {
@@ -128,14 +133,14 @@ namespace OpenWifi {
return false; return false;
} }
bool Storage::RevokeAllTokens(std::string & UserId) { bool Storage::RevokeAllTokens(std::string & username) {
try { try {
Poco::Data::Session Sess = Pool_->get(); Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess); Poco::Data::Statement Delete(Sess);
std::string St2{"DELETE From Tokens WHERE Username=?"}; std::string St2{"DELETE From Tokens WHERE Username=?"};
Delete << ConvertParams(St2), Delete << ConvertParams(St2),
Poco::Data::Keywords::use(UserId); Poco::Data::Keywords::use(username);
Delete.execute(); Delete.execute();
return true; return true;
} catch(const Poco::Exception &E) { } catch(const Poco::Exception &E) {

View File

@@ -80,23 +80,7 @@ namespace OpenWifi {
return true; return true;
} }
std::string OldDefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"}; std::string DefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"};
std::string NewDefaultUseridStockUUID{"11111111-0000-0000-6666-999999999999"};
void Storage::ReplaceOldDefaultUUID() {
try {
Poco::Data::Session Sess = Pool_->get();
std::string St1{"update users set id=? where id=?"};
Poco::Data::Statement Update(Sess);
Update << ConvertParams(St1),
Poco::Data::Keywords::use(NewDefaultUseridStockUUID),
Poco::Data::Keywords::use(OldDefaultUseridStockUUID);
Update.execute();
} catch (...) {
}
}
// if we do not find a default user, then we need to create one based on the // 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. // property file. We must set its flag to "must change password", this user has root privilege.
@@ -105,13 +89,12 @@ namespace OpenWifi {
SecurityObjects::UserInfo U; SecurityObjects::UserInfo U;
bool DefaultUserCreated = false; bool DefaultUserCreated = false;
ReplaceOldDefaultUUID();
AppServiceRegistry().Get("defaultusercreated",DefaultUserCreated); AppServiceRegistry().Get("defaultusercreated",DefaultUserCreated);
if(!GetUserById(NewDefaultUseridStockUUID,U) && !DefaultUserCreated) { if(!GetUserById(DefaultUseridStockUUID,U) && !DefaultUserCreated) {
U.currentPassword = MicroService::instance().ConfigGetString("authentication.default.password",""); U.currentPassword = MicroService::instance().ConfigGetString("authentication.default.password","");
U.lastPasswords.push_back(U.currentPassword); U.lastPasswords.push_back(U.currentPassword);
U.email = MicroService::instance().ConfigGetString("authentication.default.username",""); U.email = MicroService::instance().ConfigGetString("authentication.default.username","");
U.Id = NewDefaultUseridStockUUID; U.Id = DefaultUseridStockUUID;
U.userRole = SecurityObjects::ROOT; U.userRole = SecurityObjects::ROOT;
U.creationDate = std::time(nullptr); U.creationDate = std::time(nullptr);
U.validated = true; U.validated = true;
@@ -149,7 +132,7 @@ namespace OpenWifi {
return false; return false;
if(!PasswordHashedAlready) { if(!PasswordHashedAlready) {
NewUser.Id = MicroService::CreateUUID(); NewUser.Id = MicroService::instance().CreateUUID();
NewUser.creationDate = std::time(nullptr); NewUser.creationDate = std::time(nullptr);
} }

View File

@@ -15,23 +15,23 @@ namespace OpenWifi {
"description varchar," "description varchar,"
"avatar varchar," "avatar varchar,"
"email varchar," "email varchar,"
"validated boolean," "validated int,"
"validationEmail varchar," "validationEmail varchar,"
"validationDate bigint," "validationDate bigint,"
"creationDate bigint," "creationDate bigint,"
"validationURI varchar," "validationURI varchar,"
"changePassword boolean," "changePassword int,"
"lastLogin bigint," "lastLogin bigint,"
"currentLoginURI varchar," "currentLoginURI varchar,"
"lastPasswordChange bigint," "lastPasswordChange bigint,"
"lastEmailCheck bigint," "lastEmailCheck bigint,"
"waitingForEmailCheck boolean," "waitingForEmailCheck int,"
"locale varchar," "locale varchar,"
"notes text," "notes text,"
"location varchar," "location varchar,"
"owner varchar," "owner varchar,"
"suspended boolean," "suspended int,"
"blackListed boolean," "blackListed int,"
"userRole varchar," "userRole varchar,"
"userTypeProprietaryInfo text," "userTypeProprietaryInfo text,"
"securityPolicy text," "securityPolicy text,"
@@ -111,23 +111,23 @@ namespace OpenWifi {
std::string, // description; std::string, // description;
std::string, // avatar; std::string, // avatar;
std::string, // email; std::string, // email;
bool, // bool validated = false; uint64_t, // bool validated = false;
std::string, // validationEmail; std::string, // validationEmail;
uint64_t, // validationDate = 0; uint64_t, // validationDate = 0;
uint64_t, // creationDate = 0; uint64_t, // creationDate = 0;
std::string, // validationURI; std::string, // validationURI;
bool, // bool changePassword = true; uint64_t, // bool changePassword = true;
uint64_t, // lastLogin = 0; uint64_t, // lastLogin = 0;
std::string, // currentLoginURI; std::string, // currentLoginURI;
uint64_t, // lastPasswordChange = 0; uint64_t, // lastPasswordChange = 0;
uint64_t, // lastEmailCheck = 0; uint64_t, // lastEmailCheck = 0;
bool, // bool waitingForEmailCheck = false; uint64_t, // bool waitingForEmailCheck = false;
std::string, // locale; std::string, // locale;
std::string, // notes; std::string, // notes;
std::string, // location; std::string, // location;
std::string, // owner; std::string, // owner;
bool, // bool suspended = false; uint64_t, // bool suspended = false;
bool, // bool blackListed = false; uint64_t, // bool blackListed = false;
std::string, // userRole; std::string, // userRole;
std::string, // userTypeProprietaryInfo; std::string, // userTypeProprietaryInfo;
std::string, // securityPolicy; std::string, // securityPolicy;

View File

@@ -35,7 +35,7 @@ fi
token="" token=""
result_file=result.json result_file=result.json
username="tip@ucentral.com" username="tip@ucentral.com"
password="Snoopy99!!!" password="openwifi"
#username="stephb@incognito.com" #username="stephb@incognito.com"
#password="Snoopy98!" #password="Snoopy98!"
browser_list=(firefox sensible-browser xdg-open w3m links links2 lynx youtube-dl) browser_list=(firefox sensible-browser xdg-open w3m links links2 lynx youtube-dl)