Compare commits

...

53 Commits

Author SHA1 Message Date
Leonid Mirsky
350df38c3f Update Helm values to v2.4.0-RC1
Signed-off-by: Leonid Mirsky <leonid@opsfleet.com>
2021-11-16 23:34:10 +02:00
stephb9959
9e79b73e20 Adding proper default for mailer and sms. 2021-11-15 15:04:48 -08:00
stephb9959
eb4dfc25f2 Adding proper default for mailer and sms. 2021-11-15 14:54:23 -08:00
stephb9959
bedec254c5 Mail refactoring. 2021-11-15 14:38:56 -08:00
stephb9959
96a566a2b5 Sendmail crash fix. 2021-11-15 13:46:44 -08:00
stephb9959
ad2eb1711e ACL Fix. 2021-11-15 13:34:54 -08:00
stephb9959
7244bcb455 ACL Fix. 2021-11-15 13:22:03 -08:00
stephb9959
1db5201418 ACL Fix. 2021-11-15 13:16:34 -08:00
stephb9959
1bc635f553 ACL Fix. 2021-11-15 12:05:14 -08:00
stephb9959
257ac42d7c ACL Fix. 2021-11-15 11:51:07 -08:00
stephb9959
acb38e5313 New UUIDv4 generator 2021-11-15 11:19:31 -08:00
stephb9959
7940f0bd85 New UUIDv4 generator 2021-11-15 10:56:27 -08:00
stephb9959
62c06d0bad Filtering credentials. 2021-11-15 09:26:46 -08:00
stephb9959
494a199610 WiFi 5617 and ACL Error 2021-11-15 09:16:23 -08:00
stephb9959
5307b0b35a WiFi 5617 and ACL Error 2021-11-15 09:09:17 -08:00
stephb9959
c58728f38e WiFi 5617 and ACL Error 2021-11-15 08:29:08 -08:00
stephb9959
1f09c3b619 WiFi 5617 and ACL Error 2021-11-15 08:24:57 -08:00
stephb9959
d9c6388502 Merge remote-tracking branch 'origin/main' 2021-11-14 14:18:11 -08:00
stephb9959
5e35906aec Fixing shutdown crash 2021-11-14 14:18:02 -08:00
Dmitry Dunaev
773618ae07 [WIFI-5702] Add: README note on owgw-ui password change ability 2021-11-14 22:39:45 +03:00
stephb9959
cca4441ac7 Rate limiting log error. 2021-11-13 21:33:51 -08:00
stephb9959
730ca7b292 Proper error returning for rate limiting. 2021-11-13 17:35:15 -08:00
stephb9959
5b4dbb088f Final token cache/revocation fix. 2021-11-13 17:23:19 -08:00
stephb9959
bc11a19ee4 Fixing token revocation/ 2021-11-13 17:17:49 -08:00
stephb9959
c835e4d0b9 Fixing token revocatiopn/ 2021-11-13 17:12:39 -08:00
stephb9959
f1a2ba90f6 Fixing token revocatiopn/ 2021-11-13 17:06:52 -08:00
stephb9959
5b96ef396f Fixing token revocatiopn/ 2021-11-13 17:01:49 -08:00
stephb9959
c204d34bf4 Fixing token revocatiopn/ 2021-11-13 16:44:51 -08:00
stephb9959
4b982bf64b Fixing token revocatiopn/ 2021-11-13 16:42:20 -08:00
stephb9959
37298cc600 Fixing token revocatiopn/ 2021-11-13 16:38:18 -08:00
stephb9959
03619cc900 Fixing token revocatiopn/ 2021-11-13 16:24:13 -08:00
stephb9959
f4fc6975e1 Fixing token revocatiopn/ 2021-11-13 16:18:42 -08:00
stephb9959
1f1d596c5a Fixing token cache. 2021-11-13 16:03:58 -08:00
stephb9959
a5802bf631 Adding token expiry detection and reporting. 2021-11-13 15:24:24 -08:00
stephb9959
6471eabc82 Unitialized MFA in user record. 2021-11-13 10:24:21 -08:00
stephb9959
ab6fbaca11 Removing expired links and avatars. 2021-11-13 09:05:16 -08:00
stephb9959
1e8e5c18b2 Removing expired tokens periodically. 2021-11-13 08:43:57 -08:00
stephb9959
3cf23af068 Fix the fix...arg 2021-11-13 08:18:13 -08:00
stephb9959
1a0b549731 Fix the fix...arg 2021-11-13 07:19:52 -08:00
stephb9959
a835d2e571 Framework update. 2021-11-12 23:35:43 -08:00
stephb9959
ff7455af24 improving ACL processing. 2021-11-12 22:25:29 -08:00
stephb9959
48610bac5d Hardening SMS code. 2021-11-12 08:59:45 -08:00
stephb9959
7bd5b4d4e6 Fixing MFA UUID issue. 2021-11-12 08:49:03 -08:00
stephb9959
e1a51c2a91 Fixing MFA UUID issue. 2021-11-12 08:37:17 -08:00
stephb9959
cd0222f765 Fixing MFA UUID issue. 2021-11-12 08:21:44 -08:00
stephb9959
12fddd8bc4 Fixing MFA UUID issue. 2021-11-12 08:20:16 -08:00
stephb9959
9095d831db Merge remote-tracking branch 'origin/main' 2021-11-12 08:14:23 -08:00
stephb9959
4e8f97df9b Fixing logo problem in email 2021-11-12 08:14:15 -08:00
Dmitry Dunaev
28808eae93 Merge pull request #25 from Telecominfraproject/feature/wifi-5702--add-change-password-cli-command-on-startup
[WIFI-5702] Add: README note on changing default password
2021-11-12 19:12:47 +03:00
stephb9959
6c24a23863 Fixing logo problem in email 2021-11-12 08:10:32 -08:00
stephb9959
5931c91054 Fixing logo problem in email 2021-11-12 08:06:21 -08:00
stephb9959
9d956c13f7 Fixing logo problem in email 2021-11-12 07:53:49 -08:00
Dmitry Dunaev
ea1adde361 [WIFI-5702] Add: README note on changing default password 2021-11-12 14:40:56 +03:00
37 changed files with 3163 additions and 380 deletions

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ _deps
*.csr
/cmake-build/
/smake-build-debug/
test_scripts/curl/result.json

View File

@@ -90,7 +90,7 @@ add_executable( owsec
src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h
src/storage/storage_tokens.h
src/ActionLinkManager.cpp src/ActionLinkManager.h
)
src/ACLProcessor.h)
if(NOT SMALL_BUILD)
target_link_libraries(owsec PUBLIC

View File

@@ -91,6 +91,7 @@ RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentr
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
COPY readiness_check /readiness_check
COPY test_scripts/curl/cli /cli
EXPOSE 16001 17001 16101

View File

@@ -98,6 +98,40 @@ to get a sample. The default is
### `authentication.oldpasswords`
The number of older passwords to keep. Default is 5.
### Changing default password
On the first startup of the service new user will be created with the default credentials from properties `authentication.default.username` and `authentication.default.password`, but **you will have to change the password** before making any real requests.
You can this using [owgw-ui](https://github.com/Telecominfraproject/wlan-cloud-ucentralgw-ui/) on first login or using the following script:
```
export OWSEC=openwifi.wlan.local:16001 # endpoint to your owsec RESTAPI endpoint
#export FLAGS="-k" # uncomment and add curl flags that you would like to pass for the request (for example '-k' may be used to pass errors with self-signed certificates)
export OWSEC_DEFAULT_USERNAME=root@system.com # default username that you've set in property 'authentication.default.username'
export OWSEC_DEFAULT_PASSWORD=weLoveWifi # default password __in cleartext__ from property 'authentication.default.password'
export OWSEC_NEW_PASSWORD=NewPass123% # new password that must be set for the user (must comply with 'authentication.validation.expression')
test_scripts/curl/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD
```
CLI is also included in Docker image if you want to run it this way:
```
export OWSEC=openwifi.wlan.local:16001
#export FLAGS="-k"
export OWSEC_DEFAULT_USERNAME=root@system.com
export OWSEC_DEFAULT_PASSWORD=weLoveWifi
export OWSEC_NEW_PASSWORD=NewPass123%
docker run --rm -ti \
--network=host \
--env OWSEC \
--env FLAGS \
--env OWSEC_DEFAULT_USERNAME \
--env OWSEC_DEFAULT_PASSWORD \
--env OWSEC_NEW_PASSWORD \
tip-tip-wlan-cloud-ucentral.jfrog.io/owsec:main \
/cli testlogin $OWSEC_DEFAULT_USERNAME $OWSEC_DEFAULT_PASSWORD $OWSEC_NEW_PASSWORD
```
### Kafka integration
This security service uses Kafka to coordinate security with other services that are part of the system. You must have a Kafka service running
in order to use this. You can find several examples of Kafka services available with Docker. Here are the values you need to configure.
@@ -217,4 +251,4 @@ mailer.sender = OpenWIFI
mailer.loginmethod = login
mailer.port = 587
mailer.templates = $OWSEC_ROOT/templates
```
```

2
build
View File

@@ -1 +1 @@
47
106

View File

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

View File

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

View File

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

45
src/ACLProcessor.h Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -18,15 +18,15 @@ namespace OpenWifi {
std::string Number;
std::string Code;
std::string UserName;
uint64_t Created;
bool Validated=false;
uint64_t Created = std::time(nullptr);
bool Validated = false;
};
class SMSSender : public SubSystemServer {
public:
static SMSSender *instance() {
static SMSSender instance;
return &instance;
static auto *instance_ = new SMSSender;
return instance_;
}
int Start() final;
@@ -37,8 +37,8 @@ namespace OpenWifi {
bool IsNumberValid(const std::string &Number, const std::string &UserName);
[[nodiscard]] bool Send(const std::string &PhoneNumber, const std::string &Message);
private:
std::string Provider_;
bool Enabled_=false;
std::string Provider_;
bool Enabled_=false;
std::vector<SMSValidationCacheEntry> Cache_;
std::unique_ptr<SMS_provider> ProviderImpl_;

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@@ -26,13 +26,6 @@ namespace OpenWifi {
class StorageClass : public SubSystemServer {
public:
/* static StorageClass *instance() {
if (instance_ == nullptr) {
instance_ = new StorageClass;
}
return instance_;
}
*/
StorageClass() noexcept:
SubSystemServer("StorageClass", "STORAGE-SVR", "storage")
{
@@ -56,7 +49,7 @@ namespace OpenWifi {
}
void Stop() override {
Pool_->shutdown();
}
[[nodiscard]] inline std::string ComputeRange(uint64_t From, uint64_t HowMany) {
@@ -96,11 +89,11 @@ namespace OpenWifi {
inline int Setup_PostgreSQL();
protected:
std::unique_ptr<Poco::Data::SessionPool> Pool_;
std::unique_ptr<Poco::Data::SQLite::Connector> SQLiteConn_;
std::unique_ptr<Poco::Data::PostgreSQL::Connector> PostgresConn_;
std::unique_ptr<Poco::Data::MySQL::Connector> MySQLConn_;
DBType dbType_ = sqlite;
Poco::SharedPtr<Poco::Data::SessionPool> Pool_;
Poco::Data::SQLite::Connector SQLiteConn_;
Poco::Data::PostgreSQL::Connector PostgresConn_;
Poco::Data::MySQL::Connector MySQLConn_;
DBType dbType_ = sqlite;
};
#ifdef SMALL_BUILD
@@ -114,9 +107,8 @@ namespace OpenWifi {
auto DBName = MicroService::instance().DataDir() + "/" + MicroService::instance().ConfigGetString("storage.type.sqlite.db");
auto NumSessions = MicroService::instance().ConfigGetInt("storage.type.sqlite.maxsessions", 64);
auto IdleTime = MicroService::instance().ConfigGetInt("storage.type.sqlite.idletime", 60);
SQLiteConn_ = std::make_unique<Poco::Data::SQLite::Connector>();
SQLiteConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(SQLiteConn_->name(), DBName, 4, NumSessions, IdleTime);
SQLiteConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(SQLiteConn_.name(), DBName, 4, NumSessions, IdleTime));
return 0;
}
@@ -139,9 +131,8 @@ namespace OpenWifi {
";port=" + Port +
";compress=true;auto-reconnect=true";
MySQLConn_ = std::make_unique<Poco::Data::MySQL::Connector>();
MySQLConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(MySQLConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
MySQLConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(MySQLConn_.name(), ConnectionStr, 4, NumSessions, IdleTime));
return 0;
}
@@ -166,9 +157,8 @@ namespace OpenWifi {
" port=" + Port +
" connect_timeout=" + ConnectionTimeout;
PostgresConn_ = std::make_unique<Poco::Data::PostgreSQL::Connector>();
PostgresConn_->registerConnector();
Pool_ = std::make_unique<Poco::Data::SessionPool>(PostgresConn_->name(), ConnectionStr, 4, NumSessions, IdleTime);
PostgresConn_.registerConnector();
Pool_ = Poco::SharedPtr<Poco::Data::SessionPool>(new Poco::Data::SessionPool(PostgresConn_.name(), ConnectionStr, 4, NumSessions, IdleTime));
return 0;
}

View File

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

View File

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

View File

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

View File

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