Adding Subscriber DB Support.

This commit is contained in:
stephb9959
2021-11-30 10:07:08 -08:00
parent e794647469
commit 55d1f9571d
44 changed files with 2009 additions and 200 deletions

View File

@@ -64,18 +64,27 @@ add_executable( owsec
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp
src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp
src/RESTAPI/RESTAPI_oauth2Handler.h src/RESTAPI/RESTAPI_oauth2Handler.cpp
src/RESTAPI/RESTAPI_oauth2_handler.h src/RESTAPI/RESTAPI_oauth2_handler.cpp
src/RESTAPI/RESTAPI_users_handler.cpp src/RESTAPI/RESTAPI_users_handler.h
src/RESTAPI/RESTAPI_user_handler.cpp src/RESTAPI/RESTAPI_user_handler.h
src/RESTAPI/RESTAPI_action_links.cpp src/RESTAPI/RESTAPI_action_links.h
src/RESTAPI/RESTAPI_validateToken_handler.cpp src/RESTAPI/RESTAPI_validateToken_handler.h
src/RESTAPI/RESTAPI_systemEndpoints_handler.cpp src/RESTAPI/RESTAPI_systemEndpoints_handler.h
src/RESTAPI/RESTAPI_AssetServer.cpp src/RESTAPI/RESTAPI_AssetServer.h
src/RESTAPI/RESTAPI_avatarHandler.cpp src/RESTAPI/RESTAPI_avatarHandler.h
src/RESTAPI/RESTAPI_validate_token_handler.cpp src/RESTAPI/RESTAPI_validate_token_handler.h
src/RESTAPI/RESTAPI_system_endpoints_handler.cpp src/RESTAPI/RESTAPI_system_endpoints_handler.h
src/RESTAPI/RESTAPI_asset_server.cpp src/RESTAPI/RESTAPI_asset_server.h
src/RESTAPI/RESTAPI_avatar_handler.cpp src/RESTAPI/RESTAPI_avatar_handler.h
src/RESTAPI/RESTAPI_email_handler.cpp src/RESTAPI/RESTAPI_email_handler.h
src/RESTAPI/RESTAPI_sms_handler.cpp src/RESTAPI/RESTAPI_sms_handler.h
src/storage/storage_avatar.cpp src/storage/storage_avatar.h src/storage/storage_users.h
src/storage/storage_tables.cpp src/storage/storage_users.cpp src/storage/storage_tokens.cpp
src/storage/storage_avatar.cpp src/storage/storage_avatar.h
src/storage/storage_users.h src/storage/storage_users.cpp
src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h
src/storage/storage_tokens.h src/storage/storage_tokens.cpp
src/storage/storage_subtokens.h src/storage/storage_subtokens.cpp
src/storage/storage_preferences.cpp src/storage/storage_preferences.h
src/storage/storage_subscribers.cpp src/storage/storage_subscribers.h
src/storage/storage_tables.cpp
src/RESTAPI/RESTAPI_suboauth2_handler.h src/RESTAPI/RESTAPI_suboauth2_handler.cpp
src/RESTAPI/RESTAPI_subuser_handler.h src/RESTAPI/RESTAPI_subuser_handler.cpp
src/RESTAPI/RESTAPI_subusers_handler.h src/RESTAPI/RESTAPI_subusers_handler.cpp
src/APIServers.cpp
src/Daemon.h src/Daemon.cpp
src/AuthService.h src/AuthService.cpp
@@ -86,10 +95,9 @@ add_executable( owsec
src/SMS_provider_aws.cpp src/SMS_provider_aws.h
src/SMS_provider.cpp src/SMS_provider.h
src/SMS_provider_twilio.cpp src/SMS_provider_twilio.h
src/storage/storage_actionLinks.cpp src/storage/storage_actionLinks.h
src/storage/storage_tokens.h
src/ActionLinkManager.cpp src/ActionLinkManager.h
src/ACLProcessor.h src/RESTAPI/RESTAPI_preferences.cpp src/RESTAPI/RESTAPI_preferences.h src/storage/storage_preferences.cpp src/storage/storage_preferences.h src/framework/OpenWifiTypes.h)
src/ACLProcessor.h src/RESTAPI/RESTAPI_preferences.cpp src/RESTAPI/RESTAPI_preferences.h
src/framework/OpenWifiTypes.h)
if(NOT SMALL_BUILD)
target_link_libraries(owsec PUBLIC

2
build
View File

@@ -1 +1 @@
9
48

View File

@@ -730,6 +730,64 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/suboauth2:
post:
tags:
- Authentication
summary: Get access token - to be used as Bearer token header for all other API requests.
operationId: getAccessToken
parameters:
- in: query
name: newPassword
description: used when a user is trying to change her password. This will be the new password.
schema:
type: string
required: false
- in: query
name: forgotPassword
description: A user forgot her password. She needs to present her e-mail address in the userId and set this to true
schema:
type: boolean
required: false
- in: query
name: requirements
description: A user forgot her password. She needs to present her e-mail address in the userId and set this to true
schema:
type: boolean
required: false
- in: query
name: resendMFACode
schema:
type: boolean
required: false
- in: query
name: completeMFAChallenge
schema:
type: boolean
required: false
requestBody:
description: User id and password
required: true
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/WebTokenRequest'
- $ref: '#/components/schemas/MFAChallengeResponse'
responses:
200:
description: successful operation
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/WebTokenResult'
- $ref: '#/components/schemas/MFAChallengeRequest'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/oauth2/{token}:
delete:
tags:
@@ -755,6 +813,31 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/suboauth2/{token}:
delete:
tags:
- Authentication
summary: Revoke a token.
operationId: removeAccessToken
parameters:
- in: path
name: token
schema:
type:
string
required: true
responses:
204:
description: successful operation
content:
application/json:
schema:
$ref: '#/components/responses/Success'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/systemEndpoints:
get:
tags:
@@ -819,6 +902,52 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/subusers:
get:
tags:
- User Management
summary: Retrieve a list of existing users as well as some information about them.
operationId: getUsers
parameters:
- in: query
name: offset
schema:
type: integer
format: int64
required: false
- in: query
name: limit
schema:
type: integer
format: int64
required: false
- in: query
description: Selecting this option means the newest record will be returned. Use limit to select how many.
name: filter
schema:
type: string
required: false
- in: query
description: Return only the ids.
name: idOnly
schema:
type: boolean
required: false
- in: query
description: Return only the ids.
name: select
schema:
type: string
example: id1,id2,id3,id4,id5
required: false
responses:
200:
$ref: '#/components/schemas/UserList'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/user/{id}:
get:
tags:
@@ -923,6 +1052,110 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/subuser/{id}:
get:
tags:
- User Management
operationId: getUser
summary: Retrieve the information for a single user.
parameters:
- in: path
name: id
schema:
type: string
format: uuid
required: true
responses:
200:
$ref: '#/components/schemas/UserInfo'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
delete:
tags:
- User Management
operationId: deleteUser
summary: Delete a single user.
parameters:
- in: path
name: id
schema:
type: integer
format: int64
required: true
responses:
204:
$ref: '#/components/responses/Success'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
post:
tags:
- User Management
operationId: createUser
summary: Create a single user.
parameters:
- in: path
name: id
#must be set to 0 for user creation
schema:
type: integer
format: int64
required: true
- in: query
name: email_verification
schema:
type: boolean
required: false
requestBody:
description: User details (some fields are ignored during creation)
content:
application/json:
schema:
$ref: '#/components/schemas/UserInfo'
responses:
200:
$ref: '#/components/schemas/UserInfo'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
put:
tags:
- User Management
operationId: updateUser
summary: Modify a single user.
parameters:
- in: path
name: id
schema:
type: integer
format: int64
required: true
- in: query
name: email_verification
schema:
type: boolean
required: false
requestBody:
description: User details (some fields are ignored during update)
content:
application/json:
schema:
$ref: '#/components/schemas/UserInfo'
responses:
200:
$ref: '#/components/schemas/UserInfo'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/avatar/{id}:
get:
tags:
@@ -1196,6 +1429,26 @@ paths:
404:
$ref: '#/components/responses/NotFound'
/validateSubToken:
get:
tags:
- Security
summary: Allows any microservice to validate a token and get security policy for a specific user.
operationId: validateToken
parameters:
- in: query
name: token
schema:
type: string
required: true
responses:
200:
$ref: '#/components/schemas/TokenValidationResult'
403:
$ref: '#/components/responses/Unauthorized'
404:
$ref: '#/components/responses/NotFound'
/system:
post:
tags:

View File

@@ -4,34 +4,40 @@
#include "framework/MicroService.h"
#include "RESTAPI/RESTAPI_oauth2Handler.h"
#include "RESTAPI/RESTAPI_oauth2_handler.h"
#include "RESTAPI/RESTAPI_user_handler.h"
#include "RESTAPI/RESTAPI_users_handler.h"
#include "RESTAPI/RESTAPI_action_links.h"
#include "RESTAPI/RESTAPI_systemEndpoints_handler.h"
#include "RESTAPI/RESTAPI_AssetServer.h"
#include "RESTAPI/RESTAPI_avatarHandler.h"
#include "RESTAPI/RESTAPI_system_endpoints_handler.h"
#include "RESTAPI/RESTAPI_asset_server.h"
#include "RESTAPI/RESTAPI_avatar_handler.h"
#include "RESTAPI/RESTAPI_email_handler.h"
#include "RESTAPI/RESTAPI_sms_handler.h"
#include "RESTAPI/RESTAPI_validateToken_handler.h"
#include "RESTAPI/RESTAPI_validate_token_handler.h"
#include "RESTAPI/RESTAPI_preferences.h"
#include "RESTAPI/RESTAPI_suboauth2_handler.h"
#include "RESTAPI/RESTAPI_subuser_handler.h"
#include "RESTAPI/RESTAPI_subusers_handler.h"
namespace OpenWifi {
Poco::Net::HTTPRequestHandler * RESTAPI_external_server(const char *Path, RESTAPIHandler::BindingMap &Bindings,
Poco::Logger & L, RESTAPI_GenericServer & S) {
return RESTAPI_Router<
RESTAPI_oauth2Handler,
RESTAPI_oauth2_handler,
RESTAPI_users_handler,
RESTAPI_user_handler,
RESTAPI_system_command,
RESTAPI_AssetServer,
RESTAPI_systemEndpoints_handler,
RESTAPI_asset_server,
RESTAPI_system_endpoints_handler,
RESTAPI_action_links,
RESTAPI_avatarHandler,
RESTAPI_avatar_handler,
RESTAPI_email_handler,
RESTAPI_sms_handler,
RESTAPI_preferences
RESTAPI_preferences,
RESTAPI_suboauth2_handler,
RESTAPI_subuser_handler,
RESTAPI_subusers_handler
>(Path, Bindings, L, S);
}
@@ -42,8 +48,11 @@ namespace OpenWifi {
RESTAPI_user_handler,
RESTAPI_system_command,
RESTAPI_action_links,
RESTAPI_validateToken_handler,
RESTAPI_sms_handler
RESTAPI_validate_token_handler,
RESTAPI_sms_handler,
RESTAPI_suboauth2_handler,
RESTAPI_subuser_handler,
RESTAPI_subusers_handler
>(Path, Bindings, L, S);
}
}

View File

@@ -47,6 +47,7 @@ namespace OpenWifi {
Signer_.addAllAlgorithms();
Logger_.notice("Starting...");
PasswordValidation_ = PasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.validation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$");
SubPasswordValidation_ = SubPasswordValidationStr_ = MicroService::instance().ConfigGetString("authentication.subvalidation.expression","^(?=.*?[A-Z])(?=.*?[a-z])(?=.*?[0-9])(?=.*?[#?!@$%^&*-]).{8,}$");
TokenAging_ = (uint64_t) MicroService::instance().ConfigGetInt("authentication.token.ageing", 30 * 24 * 60 * 60);
HowManyOldPassword_ = MicroService::instance().ConfigGetInt("authentication.oldpasswords", 5);
return 0;
@@ -99,11 +100,59 @@ namespace OpenWifi {
return false;
}
bool AuthService::IsSubAuthorized(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);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
if(!CallToken.empty()) {
auto Client = SubUserCache_.get(CallToken);
if( Client.isNull() ) {
SecurityObjects::UserInfoAndPolicy UInfo2;
uint64_t RevocationDate=0;
if(StorageService()->GetSubToken(CallToken,UInfo2,RevocationDate)) {
if(RevocationDate!=0)
return false;
Expired = (UInfo2.webtoken.created_ + UInfo2.webtoken.expires_in_) < time(nullptr);
if(StorageService()->GetSubUserById(UInfo2.userinfo.Id,UInfo.userinfo)) {
UInfo.webtoken = UInfo2.webtoken;
SubUserCache_.update(CallToken, UInfo);
SessionToken = CallToken;
return true;
}
}
return false;
}
if(!Expired) {
SessionToken = CallToken;
UInfo = *Client ;
return true;
}
RevokeSubToken(CallToken);
return false;
}
} catch(const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
void AuthService::RevokeToken(std::string & Token) {
UserCache_.remove(Token);
StorageService()->RevokeToken(Token);
}
void AuthService::RevokeSubToken(std::string & Token) {
UserCache_.remove(Token);
StorageService()->RevokeSubToken(Token);
}
bool AuthService::DeleteUserFromCache(const std::string &UserName) {
std::lock_guard Guard(Mutex_);
@@ -121,6 +170,23 @@ namespace OpenWifi {
return true;
}
bool AuthService::DeleteSubUserFromCache(const std::string &UserName) {
std::lock_guard Guard(Mutex_);
std::vector<std::string> OldTokens;
SubUserCache_.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) {
SubLogout(i,false);
SubUserCache_.remove(i);
}
return true;
}
bool AuthService::RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo) {
return (UInfo.userinfo.userTypeProprietaryInfo.mfa.enabled && MFAServer().MethodEnabled(UInfo.userinfo.userTypeProprietaryInfo.mfa.method));
}
@@ -129,6 +195,10 @@ namespace OpenWifi {
return std::regex_match(Password, PasswordValidation_);
}
bool AuthService::ValidateSubPassword(const std::string &Password) {
return std::regex_match(Password, SubPasswordValidation_);
}
void AuthService::Logout(const std::string &token, bool EraseFromCache) {
std::lock_guard Guard(Mutex_);
@@ -148,6 +218,25 @@ namespace OpenWifi {
}
}
void AuthService::SubLogout(const std::string &token, bool EraseFromCache) {
std::lock_guard Guard(Mutex_);
try {
Poco::JSON::Object Obj;
Obj.set("event", "remove-token");
Obj.set("id", MicroService::instance().ID());
Obj.set("token", token);
std::stringstream ResultText;
Poco::JSON::Stringifier::stringify(Obj, ResultText);
std::string Tmp{token};
RevokeSubToken(Tmp);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, MicroService::instance().PrivateEndPoint(), ResultText.str(),
false);
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
[[nodiscard]] std::string AuthService::GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type) {
std::string Identity(UserName + ":" + Poco::format("%d",(int)std::time(nullptr)) + ":" + std::to_string(rand()));
HMAC_.update(Identity);
@@ -198,6 +287,30 @@ namespace OpenWifi {
UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
}
void AuthService::CreateSubToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo)
{
std::lock_guard Guard(Mutex_);
SecurityObjects::AclTemplate ACL;
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
UInfo.webtoken.acl_template_ = ACL;
UInfo.webtoken.expires_in_ = TokenAging_ ;
UInfo.webtoken.idle_timeout_ = 5 * 60;
UInfo.webtoken.token_type_ = "Bearer";
UInfo.webtoken.access_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,USERNAME);
UInfo.webtoken.id_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,USERNAME);
UInfo.webtoken.refresh_token_ = GenerateTokenHMAC(UInfo.userinfo.Id,CUSTOM);
UInfo.webtoken.created_ = time(nullptr);
UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false;
SubUserCache_.update(UInfo.webtoken.access_token_,UInfo);
StorageService()->SetSubLastLogin(UInfo.userinfo.Id);
StorageService()->AddSubToken(UInfo.userinfo.Id, UInfo.webtoken.access_token_,
UInfo.webtoken.refresh_token_, UInfo.webtoken.token_type_,
UInfo.webtoken.expires_in_, UInfo.webtoken.idle_timeout_);
}
bool AuthService::SetPassword(const std::string &NewPassword, SecurityObjects::UserInfo & UInfo) {
std::lock_guard G(Mutex_);
@@ -232,6 +345,40 @@ namespace OpenWifi {
return true;
}
bool AuthService::SetSubPassword(const std::string &NewPassword, SecurityObjects::UserInfo & UInfo) {
std::lock_guard G(Mutex_);
Poco::toLowerInPlace(UInfo.email);
for (const auto &i:UInfo.lastPasswords) {
auto Tokens = Poco::StringTokenizer(i,"|");
if(Tokens.count()==2) {
const auto & Salt = Tokens[0];
for(const auto &j:UInfo.lastPasswords) {
auto OldTokens = Poco::StringTokenizer(j,"|");
if(OldTokens.count()==2) {
SHA2_.update(Salt+NewPassword+UInfo.email);
if(OldTokens[1]==Utils::ToHex(SHA2_.digest()))
return false;
}
}
} else {
SHA2_.update(NewPassword+UInfo.email);
if(Tokens[0]==Utils::ToHex(SHA2_.digest()))
return false;
}
}
if(UInfo.lastPasswords.size()==HowManyOldPassword_) {
UInfo.lastPasswords.erase(UInfo.lastPasswords.begin());
}
auto NewHash = ComputeNewPasswordHash(UInfo.email,NewPassword);
UInfo.lastPasswords.push_back(NewHash);
UInfo.currentPassword = NewHash;
UInfo.changePassword = false;
return true;
}
static std::string GetMeSomeSalt() {
auto start = std::chrono::high_resolution_clock::now();
return std::to_string(start.time_since_epoch().count());
@@ -261,6 +408,23 @@ namespace OpenWifi {
return false;
}
bool AuthService::ValidateSubPasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword) {
std::lock_guard G(Mutex_);
std::string UName = Poco::trim(Poco::toLower(UserName));
auto Tokens = Poco::StringTokenizer(StoredPassword,"|");
if(Tokens.count()==1) {
SHA2_.update(Password+UName);
if(Tokens[0]==Utils::ToHex(SHA2_.digest()))
return true;
} else if (Tokens.count()==2) {
SHA2_.update(Tokens[0]+Password+UName);
if(Tokens[1]==Utils::ToHex(SHA2_.digest()))
return true;
}
return false;
}
UNAUTHORIZED_REASON AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired )
{
std::lock_guard Guard(Mutex_);
@@ -306,6 +470,51 @@ namespace OpenWifi {
return INVALID_CREDENTIALS;
}
UNAUTHORIZED_REASON AuthService::AuthorizeSub( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired )
{
std::lock_guard Guard(Mutex_);
Poco::toLowerInPlace(UserName);
if(StorageService()->GetSubUserByEmail(UserName,UInfo.userinfo)) {
if(UInfo.userinfo.waitingForEmailCheck) {
return USERNAME_PENDING_VERIFICATION;
}
if(!ValidateSubPasswordHash(UserName,Password,UInfo.userinfo.currentPassword)) {
return INVALID_CREDENTIALS;
}
if(UInfo.userinfo.changePassword && NewPassword.empty()) {
UInfo.webtoken.userMustChangePassword = true ;
return PASSWORD_CHANGE_REQUIRED;
}
if(!NewPassword.empty() && !ValidateSubPassword(NewPassword)) {
return PASSWORD_INVALID;
}
if(UInfo.userinfo.changePassword || !NewPassword.empty()) {
if(!SetSubPassword(NewPassword,UInfo.userinfo)) {
UInfo.webtoken.errorCode = 1;
return PASSWORD_ALREADY_USED;
}
UInfo.userinfo.lastPasswordChange = std::time(nullptr);
UInfo.userinfo.changePassword = false;
StorageService()->UpdateSubUserInfo(AUTHENTICATION_SYSTEM, UInfo.userinfo.Id,UInfo.userinfo);
}
// so we have a good password, password up date has taken place if need be, now generate the token.
UInfo.userinfo.lastLogin=std::time(nullptr);
StorageService()->SetSubLastLogin(UInfo.userinfo.Id);
CreateSubToken(UserName, UInfo );
return SUCCESS;
}
return INVALID_CREDENTIALS;
}
bool AuthService::SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) {
SecurityObjects::UserInfo UInfo;
@@ -341,6 +550,41 @@ namespace OpenWifi {
return false;
}
bool AuthService::SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason) {
SecurityObjects::UserInfo UInfo;
if(StorageService()->GetSubUserByEmail(Email,UInfo)) {
switch (Reason) {
case FORGOT_PASSWORD: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetLogoAssetURI();
Attrs[SUBJECT] = "Password reset link";
Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=password_reset&id=" + LinkId ;
SMTPMailerService()->SendMessage(UInfo.email, "password_reset.txt", Attrs);
}
break;
case EMAIL_VERIFICATION: {
MessageAttributes Attrs;
Attrs[RECIPIENT_EMAIL] = UInfo.email;
Attrs[LOGO] = GetLogoAssetURI();
Attrs[SUBJECT] = "EMail Address Verification";
Attrs[ACTION_LINK] = MicroService::instance().GetPublicAPIEndPoint() + "/actionLink?action=email_verification&id=" + LinkId ;
SMTPMailerService()->SendMessage(UInfo.email, "email_verification.txt", Attrs);
UInfo.waitingForEmailCheck = true;
}
break;
default:
break;
}
return true;
}
return false;
}
bool AuthService::VerifyEmail(SecurityObjects::UserInfo &UInfo) {
SecurityObjects::ActionLink A;
@@ -354,6 +598,19 @@ namespace OpenWifi {
return true;
}
bool AuthService::VerifySubEmail(SecurityObjects::UserInfo &UInfo) {
SecurityObjects::ActionLink A;
A.action = OpenWifi::SecurityObjects::LinkActions::VERIFY_EMAIL;
A.userId = UInfo.email;
A.id = MicroService::CreateUUID();
A.created = std::time(nullptr);
A.expires = A.created + 24*60*60;
StorageService()->CreateAction(A);
UInfo.waitingForEmailCheck = true;
return true;
}
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired) {
std::lock_guard G(Mutex_);
@@ -388,5 +645,38 @@ namespace OpenWifi {
return false;
}
bool AuthService::IsValidSubToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired) {
std::lock_guard G(Mutex_);
Expired = false;
auto Client = SubUserCache_.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()->IsSubTokenRevoked(TToken)) {
return false;
}
// get the token from disk...
SecurityObjects::UserInfoAndPolicy UInfo;
uint64_t RevocationDate=0;
if(StorageService()->GetSubToken(TToken, UInfo, RevocationDate)) {
if(RevocationDate!=0)
return false;
Expired = (UInfo.webtoken.created_ + UInfo.webtoken.expires_in_) < std::time(nullptr);
if(StorageService()->GetSubUserById(UInfo.userinfo.Id,UInfo.userinfo)) {
WebToken = UInfo.webtoken;
SubUserCache_.update(UInfo.webtoken.access_token_, UInfo);
return true;
}
}
return false;
}
} // end of namespace

View File

@@ -59,23 +59,42 @@ namespace OpenWifi{
[[nodiscard]] const std:: string & PasswordValidationExpression() const { return PasswordValidationStr_;};
void Logout(const std::string &token, bool EraseFromCache=true);
[[nodiscard]] bool IsSubAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired);
[[nodiscard]] UNAUTHORIZED_REASON AuthorizeSub( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired );
void CreateSubToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo);
[[nodiscard]] bool SetSubPassword(const std::string &Password, SecurityObjects::UserInfo & UInfo);
[[nodiscard]] const std:: string & SubPasswordValidationExpression() const { return PasswordValidationStr_;};
void SubLogout(const std::string &token, bool EraseFromCache=true);
bool ValidatePassword(const std::string &pwd);
bool ValidateSubPassword(const std::string &pwd);
[[nodiscard]] bool IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired);
[[nodiscard]] bool IsValidSubToken(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);
[[nodiscard]] std::string ComputeNewPasswordHash(const std::string &UserName, const std::string &Password);
[[nodiscard]] bool ValidatePasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword);
[[nodiscard]] bool ValidateSubPasswordHash(const std::string & UserName, const std::string & Password, const std::string &StoredPassword);
[[nodiscard]] bool UpdatePassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword);
[[nodiscard]] std::string ResetPassword(const std::string &Admin, const std::string &UserName);
[[nodiscard]] bool UpdateSubPassword(const std::string &Admin, const std::string &UserName, const std::string & OldPassword, const std::string &NewPassword);
[[nodiscard]] std::string ResetSubPassword(const std::string &Admin, const std::string &UserName);
[[nodiscard]] static bool VerifyEmail(SecurityObjects::UserInfo &UInfo);
[[nodiscard]] static bool VerifySubEmail(SecurityObjects::UserInfo &UInfo);
[[nodiscard]] static bool SendEmailToUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason);
[[nodiscard]] static bool SendEmailToSubUser(const std::string &LinkId, std::string &Email, EMAIL_REASON Reason);
[[nodiscard]] bool DeleteUserFromCache(const std::string &UserName);
[[nodiscard]] bool DeleteSubUserFromCache(const std::string &UserName);
[[nodiscard]] bool RequiresMFA(const SecurityObjects::UserInfoAndPolicy &UInfo);
void RevokeToken(std::string & Token);
void RevokeSubToken(std::string & Token);
[[nodiscard]] static inline const std::string GetLogoAssetURI() {
return MicroService::instance().PublicEndPoint() + "/wwwassets/the_logo.png";
@@ -88,11 +107,16 @@ namespace OpenWifi{
private:
Poco::JWT::Signer Signer_;
Poco::SHA2Engine SHA2_;
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;
Poco::ExpireLRUCache<std::string,SecurityObjects::UserInfoAndPolicy> UserCache_{256,1200000};
Poco::ExpireLRUCache<std::string,SecurityObjects::UserInfoAndPolicy> SubUserCache_{4096,1200000};
std::string PasswordValidationStr_;
std::string SubPasswordValidationStr_;
std::regex PasswordValidation_;
std::regex SubPasswordValidation_;
uint64_t TokenAging_ = 30 * 24 * 60 * 60;
uint64_t HowManyOldPassword_=5;
class SHA256Engine : public Poco::Crypto::DigestEngine
@@ -121,8 +145,11 @@ namespace OpenWifi{
inline AuthService * AuthService() { return AuthService::instance(); }
[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired) {
return AuthService()->IsAuthorized(Request, SessionToken, UInfo, Expired );
[[nodiscard]] inline bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo , bool & Expired, bool Sub ) {
if(Sub)
return AuthService()->IsSubAuthorized(Request, SessionToken, UInfo, Expired );
else
return AuthService()->IsAuthorized(Request, SessionToken, UInfo, Expired );
}
} // end of namespace

View File

@@ -2,9 +2,7 @@
// Created by stephane bourque on 2021-06-22.
//
#ifndef UCENTRALSEC_RESTAPI_ACTION_LINKS_H
#define UCENTRALSEC_RESTAPI_ACTION_LINKS_H
#pragma once
#include "framework/MicroService.h"
@@ -33,5 +31,3 @@ namespace OpenWifi {
void DoPut() final {};
};
}
#endif //UCENTRALSEC_RESTAPI_ACTION_LINKS_H

View File

@@ -2,13 +2,13 @@
// Created by stephane bourque on 2021-07-10.
//
#include "RESTAPI_AssetServer.h"
#include "RESTAPI_asset_server.h"
#include "Poco/File.h"
#include "framework/RESTAPI_protocol.h"
#include "Daemon.h"
namespace OpenWifi {
void RESTAPI_AssetServer::DoGet() {
void RESTAPI_asset_server::DoGet() {
Poco::File AssetFile;
if(Request->getURI().find("/favicon.ico") != std::string::npos) {

View File

@@ -2,15 +2,14 @@
// Created by stephane bourque on 2021-07-10.
//
#ifndef UCENTRALSEC_RESTAPI_ASSETSERVER_H
#define UCENTRALSEC_RESTAPI_ASSETSERVER_H
#pragma once
#include "../framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_AssetServer : public RESTAPIHandler {
class RESTAPI_asset_server : public RESTAPIHandler {
public:
RESTAPI_AssetServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
RESTAPI_asset_server(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_POST,
@@ -32,5 +31,3 @@ namespace OpenWifi {
};
}
#endif //UCENTRALSEC_RESTAPI_ASSETSERVER_H

View File

@@ -5,7 +5,7 @@
#include <fstream>
#include <iostream>
#include "RESTAPI_avatarHandler.h"
#include "RESTAPI_avatar_handler.h"
#include "StorageService.h"
#include "Poco/Net/HTMLForm.h"
#include "framework/RESTAPI_protocol.h"
@@ -27,7 +27,7 @@ namespace OpenWifi {
Length_ = InputStream.chars();
};
void RESTAPI_avatarHandler::DoPost() {
void RESTAPI_avatar_handler::DoPost() {
std::string Id = GetBinding(RESTAPI::Protocol::ID, "");
SecurityObjects::UserInfo UInfo;
@@ -57,7 +57,7 @@ namespace OpenWifi {
ReturnObject(Answer);
}
void RESTAPI_avatarHandler::DoGet() {
void RESTAPI_avatar_handler::DoGet() {
std::string Id = GetBinding(RESTAPI::Protocol::ID, "");
if (Id.empty()) {
return NotFound();
@@ -70,7 +70,7 @@ namespace OpenWifi {
SendFile(TempAvatar, Type, Name);
}
void RESTAPI_avatarHandler::DoDelete() {
void RESTAPI_avatar_handler::DoDelete() {
std::string Id = GetBinding(RESTAPI::Protocol::ID, "");
if (Id.empty()) {
return NotFound();

View File

@@ -1,10 +1,7 @@
//
// Created by stephane bourque on 2021-07-15.
//
#ifndef UCENTRALSEC_RESTAPI_AVATARHANDLER_H
#define UCENTRALSEC_RESTAPI_AVATARHANDLER_H
#pragma once
#include "framework/MicroService.h"
@@ -31,9 +28,9 @@ namespace OpenWifi {
Poco::TemporaryFile &TempFile_;
};
class RESTAPI_avatarHandler : public RESTAPIHandler {
class RESTAPI_avatar_handler : public RESTAPIHandler {
public:
RESTAPI_avatarHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
RESTAPI_avatar_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{
Poco::Net::HTTPRequest::HTTP_GET,
@@ -51,4 +48,3 @@ namespace OpenWifi {
};
}
#endif //UCENTRALSEC_RESTAPI_AVATARHANDLER_H

View File

@@ -2,9 +2,7 @@
// Created by stephane bourque on 2021-09-02.
//
#ifndef OWSEC_RESTAPI_EMAIL_HANDLER_H
#define OWSEC_RESTAPI_EMAIL_HANDLER_H
#pragma once
#include "framework/MicroService.h"
@@ -24,5 +22,3 @@ namespace OpenWifi {
void DoPut() final {};
};
}
#endif //OWSEC_RESTAPI_EMAIL_HANDLER_H

View File

@@ -10,7 +10,7 @@
#include "Daemon.h"
#include "AuthService.h"
#include "RESTAPI_oauth2Handler.h"
#include "RESTAPI_oauth2_handler.h"
#include "MFAServer.h"
#include "framework/RESTAPI_protocol.h"
#include "framework/MicroService.h"
@@ -24,7 +24,7 @@ namespace OpenWifi {
U.oauthType.clear();
}
void RESTAPI_oauth2Handler::DoGet() {
void RESTAPI_oauth2_handler::DoGet() {
bool Expired = false;
if (!IsAuthorized(Expired)) {
if(Expired)
@@ -43,7 +43,7 @@ namespace OpenWifi {
BadRequest(RESTAPI::Errors::UnrecognizedRequest);
}
void RESTAPI_oauth2Handler::DoDelete() {
void RESTAPI_oauth2_handler::DoDelete() {
bool Expired = false;
if (!IsAuthorized(Expired)) {
if(Expired)
@@ -61,7 +61,7 @@ namespace OpenWifi {
NotFound();
}
void RESTAPI_oauth2Handler::DoPost() {
void RESTAPI_oauth2_handler::DoPost() {
auto Obj = ParseStream();
auto userId = GetS(RESTAPI::Protocol::USERID, Obj);
auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj);

View File

@@ -6,15 +6,13 @@
// Arilia Wireless Inc.
//
#ifndef UCENTRAL_RESTAPI_OAUTH2HANDLER_H
#define UCENTRAL_RESTAPI_OAUTH2HANDLER_H
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_oauth2Handler : public RESTAPIHandler {
class RESTAPI_oauth2_handler : public RESTAPIHandler {
public:
RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
RESTAPI_oauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_DELETE,
@@ -29,4 +27,5 @@ namespace OpenWifi {
void DoPut() final {};
};
}
#endif //UCENTRAL_RESTAPI_OAUTH2HANDLER_H

View File

@@ -2,8 +2,7 @@
// Created by stephane bourque on 2021-11-16.
//
#ifndef OWSEC_RESTAPI_PREFERENCES_H
#define OWSEC_RESTAPI_PREFERENCES_H
#pragma once
#include "framework/MicroService.h"
@@ -25,5 +24,3 @@ namespace OpenWifi {
void DoDelete() final {};
};
}
#endif //OWSEC_RESTAPI_PREFERENCES_H

View File

@@ -2,9 +2,7 @@
// Created by stephane bourque on 2021-10-09.
//
#ifndef OWSEC_RESTAPI_SMS_HANDLER_H
#define OWSEC_RESTAPI_SMS_HANDLER_H
#pragma once
#include "framework/MicroService.h"
@@ -24,5 +22,3 @@ namespace OpenWifi {
void DoPut() final {};
};
}
#endif //OWSEC_RESTAPI_SMS_HANDLER_H

View File

@@ -0,0 +1,157 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "RESTAPI_suboauth2_handler.h"
#include "Daemon.h"
#include "AuthService.h"
#include "MFAServer.h"
#include "framework/RESTAPI_protocol.h"
#include "framework/MicroService.h"
#include "StorageService.h"
namespace OpenWifi {
static void FilterCredentials(SecurityObjects::UserInfo & U) {
U.currentPassword.clear();
U.lastPasswords.clear();
U.oauthType.clear();
}
void RESTAPI_suboauth2_handler::DoGet() {
bool Expired = false;
if (!IsAuthorized(Expired, true)) {
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;
SecurityObjects::UserInfo ReturnedUser = UserInfo_.userinfo;
FilterCredentials(ReturnedUser);
ReturnedUser.to_json(Me);
return ReturnObject(Me);
}
BadRequest(RESTAPI::Errors::UnrecognizedRequest);
}
void RESTAPI_suboauth2_handler::DoDelete() {
bool Expired = false;
if (!IsAuthorized(Expired, true)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken,EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::MissingAuthenticationInformation);
}
auto Token = GetBinding(RESTAPI::Protocol::TOKEN, "...");
if (Token == SessionToken_) {
AuthService()->Logout(Token);
return ReturnStatus(Poco::Net::HTTPResponse::HTTP_NO_CONTENT, true);
}
Logger_.information(Poco::format("BAD-LOGOUT(%s): Request for %s", Request->clientAddress().toString(), UserInfo_.userinfo.email));
NotFound();
}
void RESTAPI_suboauth2_handler::DoPost() {
auto Obj = ParseStream();
auto userId = GetS(RESTAPI::Protocol::USERID, Obj);
auto password = GetS(RESTAPI::Protocol::PASSWORD, Obj);
auto newPassword = GetS(RESTAPI::Protocol::NEWPASSWORD, Obj);
Poco::toLowerInPlace(userId);
if(GetBoolParameter(RESTAPI::Protocol::REQUIREMENTS, false)) {
Logger_.information(Poco::format("POLICY-REQUEST(%s): Request.", Request->clientAddress().toString()));
Poco::JSON::Object Answer;
Answer.set(RESTAPI::Protocol::PASSWORDPATTERN, AuthService()->SubPasswordValidationExpression());
Answer.set(RESTAPI::Protocol::ACCESSPOLICY, Daemon()->GetAccessPolicy());
Answer.set(RESTAPI::Protocol::PASSWORDPOLICY, Daemon()->GetPasswordPolicy());
return ReturnObject(Answer);
}
if(GetBoolParameter(RESTAPI::Protocol::FORGOTPASSWORD,false)) {
SecurityObjects::UserInfo UInfo1;
auto UserExists = StorageService()->GetSubUserByEmail(userId,UInfo1);
if(UserExists) {
Logger_.information(Poco::format("FORGOTTEN-PASSWORD(%s): Request for %s", Request->clientAddress().toString(), userId));
SecurityObjects::ActionLink NewLink;
NewLink.action = OpenWifi::SecurityObjects::LinkActions::FORGOT_PASSWORD;
NewLink.id = MicroService::CreateUUID();
NewLink.userId = UInfo1.Id;
NewLink.created = std::time(nullptr);
NewLink.expires = NewLink.created + (24*60*60);
StorageService()->CreateAction(NewLink);
Poco::JSON::Object ReturnObj;
SecurityObjects::UserInfoAndPolicy UInfo;
UInfo.webtoken.userMustChangePassword = true;
UInfo.webtoken.to_json(ReturnObj);
return ReturnObject(ReturnObj);
} else {
Poco::JSON::Object ReturnObj;
SecurityObjects::UserInfoAndPolicy UInfo;
UInfo.webtoken.userMustChangePassword = true;
UInfo.webtoken.to_json(ReturnObj);
return ReturnObject(ReturnObj);
}
}
if(GetBoolParameter(RESTAPI::Protocol::RESENDMFACODE,false)) {
Logger_.information(Poco::format("RESEND-MFA-CODE(%s): Request for %s", Request->clientAddress().toString(), userId));
if(Obj->has("uuid")) {
auto uuid = Obj->get("uuid").toString();
if(MFAServer().ResendCode(uuid))
return OK();
}
return UnAuthorized(RESTAPI::Errors::InvalidCredentials);
}
if(GetBoolParameter(RESTAPI::Protocol::COMPLETEMFACHALLENGE,false)) {
Logger_.information(Poco::format("COMPLETE-MFA-CHALLENGE(%s): Request for %s", Request->clientAddress().toString(), userId));
if(Obj->has("uuid")) {
SecurityObjects::UserInfoAndPolicy UInfo;
if(MFAServer().CompleteMFAChallenge(Obj,UInfo)) {
Poco::JSON::Object ReturnObj;
UInfo.webtoken.to_json(ReturnObj);
return ReturnObject(ReturnObj);
}
}
return UnAuthorized(RESTAPI::Errors::InvalidCredentials);
}
SecurityObjects::UserInfoAndPolicy UInfo;
bool Expired=false;
auto Code=AuthService()->AuthorizeSub(userId, password, newPassword, UInfo, Expired);
if (Code==SUCCESS) {
Poco::JSON::Object ReturnObj;
if(AuthService()->RequiresMFA(UInfo)) {
if(MFAServer().StartMFAChallenge(UInfo, ReturnObj)) {
return ReturnObject(ReturnObj);
}
Logger_.warning("MFA Seems to be broken. Please fix. Disabling MFA checking for now.");
}
UInfo.webtoken.to_json(ReturnObj);
return ReturnObject(ReturnObj);
} else {
switch(Code) {
case INVALID_CREDENTIALS:
return UnAuthorized(RESTAPI::Errors::InvalidCredentials, Code);
case PASSWORD_INVALID:
return UnAuthorized(RESTAPI::Errors::InvalidPassword, Code);
case PASSWORD_ALREADY_USED:
return UnAuthorized(RESTAPI::Errors::PasswordRejected, Code);
case USERNAME_PENDING_VERIFICATION:
return UnAuthorized(RESTAPI::Errors::UserPendingVerification, Code);
case PASSWORD_CHANGE_REQUIRED:
return UnAuthorized(RESTAPI::Errors::PasswordMustBeChanged, Code);
default:
return UnAuthorized(RESTAPI::Errors::InvalidCredentials); break;
}
return;
}
}
}

View File

@@ -0,0 +1,26 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_suboauth2_handler : public RESTAPIHandler {
public:
RESTAPI_suboauth2_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal, false, false , RateLimit{.Interval=1000,.MaxCalls=10},
false) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/suboauth2/{token}","/api/v1/suboauth2"}; };
void DoGet() final;
void DoPost() final;
void DoDelete() final;
void DoPut() final {};
};
}

View File

@@ -0,0 +1,239 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "RESTAPI_subuser_handler.h"
#include "StorageService.h"
#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_subuser_handler::DoGet() {
std::string Id = GetBinding("id", "");
if(Id.empty()) {
return BadRequest(RESTAPI::Errors::MissingUserID);
}
Poco::toLowerInPlace(Id);
std::string Arg;
SecurityObjects::UserInfo UInfo;
if(HasParameter("byEmail",Arg) && Arg=="true") {
if(!StorageService()->GetSubUserByEmail(Id,UInfo)) {
return NotFound();
}
} else if(!StorageService()->GetSubUserById(Id,UInfo)) {
return NotFound();
}
Poco::JSON::Object UserInfoObject;
FilterCredentials(UInfo);
UInfo.to_json(UserInfoObject);
ReturnObject(UserInfoObject);
}
void RESTAPI_subuser_handler::DoDelete() {
std::string Id = GetBinding("id", "");
if(Id.empty()) {
return BadRequest(RESTAPI::Errors::MissingUserID);
}
SecurityObjects::UserInfo UInfo;
if(!StorageService()->GetUserById(Id,UInfo)) {
return NotFound();
}
if(!ACLProcessor::Can(UserInfo_.userinfo, UInfo,ACLProcessor::DELETE)) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
if(!StorageService()->DeleteSubUser(UserInfo_.userinfo.email,Id)) {
return NotFound();
}
if(AuthService()->DeleteSubUserFromCache(UInfo.email)) {
// nothing to do
}
Logger_.information(Poco::format("Remove all tokens for '%s'", UserInfo_.userinfo.email));
StorageService()->RevokeAllSubTokens(UInfo.email);
Logger_.information(Poco::format("User '%s' deleted by '%s'.",Id,UserInfo_.userinfo.email));
OK();
}
void RESTAPI_subuser_handler::DoPost() {
std::string Id = GetBinding("id", "");
if(Id!="0") {
return BadRequest(RESTAPI::Errors::IdMustBe0);
}
SecurityObjects::UserInfo NewUser;
RESTAPI_utils::from_request(NewUser,*Request);
if(NewUser.userRole == SecurityObjects::UNKNOWN) {
return BadRequest(RESTAPI::Errors::InvalidUserRole);
}
if(!ACLProcessor::Can(UserInfo_.userinfo,NewUser,ACLProcessor::CREATE)) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
Poco::toLowerInPlace(NewUser.email);
if(!Utils::ValidEMailAddress(NewUser.email)) {
return BadRequest(RESTAPI::Errors::InvalidEmailAddress);
}
if(!NewUser.currentPassword.empty()) {
if(!AuthService()->ValidateSubPassword(NewUser.currentPassword)) {
return BadRequest(RESTAPI::Errors::InvalidPassword);
}
}
if(NewUser.name.empty())
NewUser.name = NewUser.email;
if(!StorageService()->CreateSubUser(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::VerifySubEmail(NewUser))
Logger_.information(Poco::format("Verification e-mail requested for %s",NewUser.email));
StorageService()->UpdateSubUserInfo(UserInfo_.userinfo.email,NewUser.Id,NewUser);
}
if(!StorageService()->GetSubUserByEmail(NewUser.email, NewUser)) {
Logger_.information(Poco::format("User '%s' but not retrieved.",NewUser.email));
return NotFound();
}
Poco::JSON::Object UserInfoObject;
FilterCredentials(NewUser);
NewUser.to_json(UserInfoObject);
ReturnObject(UserInfoObject);
Logger_.information(Poco::format("User '%s' has been added by '%s')",NewUser.email, UserInfo_.userinfo.email));
}
void RESTAPI_subuser_handler::DoPut() {
std::string Id = GetBinding("id", "");
if(Id.empty()) {
return BadRequest(RESTAPI::Errors::MissingUserID);
}
SecurityObjects::UserInfo Existing;
if(!StorageService()->GetSubUserById(Id,Existing)) {
return NotFound();
}
if(!ACLProcessor::Can(UserInfo_.userinfo,Existing,ACLProcessor::MODIFY)) {
return UnAuthorized("Insufficient access rights.", ACCESS_DENIED);
}
SecurityObjects::UserInfo NewUser;
auto RawObject = ParseStream();
if(!NewUser.from_json(RawObject)) {
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
}
// some basic validations
if(RawObject->has("userRole") && SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString())==SecurityObjects::UNKNOWN) {
return BadRequest(RESTAPI::Errors::InvalidUserRole);
}
// The only valid things to change are: changePassword, name,
AssignIfPresent(RawObject,"name", Existing.name);
AssignIfPresent(RawObject,"description", Existing.description);
AssignIfPresent(RawObject,"owner", Existing.owner);
AssignIfPresent(RawObject,"location", Existing.location);
AssignIfPresent(RawObject,"locale", Existing.locale);
AssignIfPresent(RawObject,"changePassword", Existing.changePassword);
AssignIfPresent(RawObject,"suspended", Existing.suspended);
AssignIfPresent(RawObject,"blackListed", Existing.blackListed);
if(RawObject->has("userRole")) {
auto NewRole = SecurityObjects::UserTypeFromString(RawObject->get("userRole").toString());
if(NewRole!=Existing.userRole) {
if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT && NewRole==SecurityObjects::ROOT) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
if(Id==UserInfo_.userinfo.Id) {
return UnAuthorized(RESTAPI::Errors::InsufficientAccessRights, ACCESS_DENIED);
}
Existing.userRole = NewRole;
}
}
if(RawObject->has("notes")) {
SecurityObjects::NoteInfoVec NIV;
NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(RawObject->get("notes").toString());
for(auto const &i:NIV) {
SecurityObjects::NoteInfo ii{.created=(uint64_t)std::time(nullptr), .createdBy=UserInfo_.userinfo.email, .note=i.note};
Existing.notes.push_back(ii);
}
}
if(RawObject->has("currentPassword")) {
if(!AuthService()->ValidateSubPassword(RawObject->get("currentPassword").toString())) {
return BadRequest(RESTAPI::Errors::InvalidPassword);
}
if(!AuthService()->SetPassword(RawObject->get("currentPassword").toString(),Existing)) {
return BadRequest(RESTAPI::Errors::PasswordRejected);
}
}
if(GetParameter("email_verification","false")=="true") {
if(AuthService::VerifySubEmail(Existing))
Logger_.information(Poco::format("Verification e-mail requested for %s",Existing.email));
}
if(RawObject->has("userTypeProprietaryInfo")) {
bool ChangingMFA = NewUser.userTypeProprietaryInfo.mfa.enabled && !Existing.userTypeProprietaryInfo.mfa.enabled;
Existing.userTypeProprietaryInfo.mfa.enabled = NewUser.userTypeProprietaryInfo.mfa.enabled;
auto PropInfo = RawObject->get("userTypeProprietaryInfo");
auto PInfo = PropInfo.extract<Poco::JSON::Object::Ptr>();
if(PInfo->isArray("mobiles")) {
Existing.userTypeProprietaryInfo.mobiles = NewUser.userTypeProprietaryInfo.mobiles;
}
if(ChangingMFA && !NewUser.userTypeProprietaryInfo.mobiles.empty() && !SMSSender()->IsNumberValid(NewUser.userTypeProprietaryInfo.mobiles[0].number,UserInfo_.userinfo.email)){
return BadRequest(RESTAPI::Errors::NeedMobileNumber);
}
if(NewUser.userTypeProprietaryInfo.mfa.method=="sms" && Existing.userTypeProprietaryInfo.mobiles.empty()) {
return BadRequest(RESTAPI::Errors::NeedMobileNumber);
}
if(!NewUser.userTypeProprietaryInfo.mfa.method.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()->UpdateSubUserInfo(UserInfo_.userinfo.email,Id,Existing)) {
SecurityObjects::UserInfo NewUserInfo;
StorageService()->GetSubUserByEmail(UserInfo_.userinfo.email,NewUserInfo);
Poco::JSON::Object ModifiedObject;
FilterCredentials(NewUserInfo);
NewUserInfo.to_json(ModifiedObject);
return ReturnObject(ModifiedObject);
}
BadRequest(RESTAPI::Errors::RecordNotUpdated);
}
}

View File

@@ -0,0 +1,30 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_subuser_handler : public RESTAPIHandler {
public:
RESTAPI_subuser_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_PUT,
Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/subuser/{id}"}; };
void DoGet() final;
void DoPost() final;
void DoDelete() final;
void DoPut() final;
private:
};
}

View File

@@ -0,0 +1,58 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "RESTAPI_subusers_handler.h"
#include "StorageService.h"
#include "framework/RESTAPI_protocol.h"
#include "framework/MicroService.h"
namespace OpenWifi {
void RESTAPI_subusers_handler::DoGet() {
std::vector<SecurityObjects::UserInfo> Users;
bool IdOnly = (GetParameter("idOnly","false")=="true");
if(QB_.Select.empty()) {
Poco::JSON::Array ArrayObj;
Poco::JSON::Object Answer;
if (StorageService()->GetSubUsers(QB_.Offset, QB_.Limit, Users)) {
for (auto &i : Users) {
Poco::JSON::Object Obj;
if (IdOnly) {
ArrayObj.add(i.Id);
} else {
i.currentPassword.clear();
i.lastPasswords.clear();
i.oauthType.clear();
i.to_json(Obj);
ArrayObj.add(Obj);
}
}
Answer.set(RESTAPI::Protocol::USERS, ArrayObj);
}
return ReturnObject(Answer);
} else {
Types::StringVec IDs = Utils::Split(QB_.Select);
Poco::JSON::Array ArrayObj;
for(auto &i:IDs) {
SecurityObjects::UserInfo UInfo;
if(StorageService()->GetSubUserById(i,UInfo)) {
Poco::JSON::Object Obj;
if (IdOnly) {
ArrayObj.add(UInfo.Id);
} else {
UInfo.currentPassword.clear();
UInfo.lastPasswords.clear();
UInfo.oauthType.clear();
UInfo.to_json(Obj);
ArrayObj.add(Obj);
}
}
}
Poco::JSON::Object RetObj;
RetObj.set(RESTAPI::Protocol::USERS, ArrayObj);
return ReturnObject(RetObj);
}
}
}

View File

@@ -0,0 +1,25 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_subusers_handler : public RESTAPIHandler {
public:
RESTAPI_subusers_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {}
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/subusers"}; };
void DoGet() final;
void DoPost() final {};
void DoDelete() final {};
void DoPut() final {};
};
};

View File

@@ -2,12 +2,12 @@
// Created by stephane bourque on 2021-07-01.
//
#include "RESTAPI_systemEndpoints_handler.h"
#include "RESTAPI_system_endpoints_handler.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
namespace OpenWifi {
void RESTAPI_systemEndpoints_handler::DoGet() {
void RESTAPI_system_endpoints_handler::DoGet() {
auto Services = MicroService::instance().GetServices();
SecurityObjects::SystemEndpointList L;
for(const auto &i:Services) {

View File

@@ -2,15 +2,14 @@
// Created by stephane bourque on 2021-07-01.
//
#ifndef UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H
#define UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H
#pragma once
#include "../framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_systemEndpoints_handler : public RESTAPIHandler {
class RESTAPI_system_endpoints_handler : public RESTAPIHandler {
public:
RESTAPI_systemEndpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
RESTAPI_system_endpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
@@ -23,5 +22,3 @@ namespace OpenWifi {
void DoPut() final {};
};
}
#endif //UCENTRALSEC_RESTAPI_SYSTEMENDPOINTS_HANDLER_H

View File

@@ -2,8 +2,7 @@
// Created by stephane bourque on 2021-06-21.
//
#ifndef UCENTRALSEC_RESTAPI_USER_HANDLER_H
#define UCENTRALSEC_RESTAPI_USER_HANDLER_H
#pragma once
#include "framework/MicroService.h"
@@ -29,6 +28,3 @@ namespace OpenWifi {
};
}
#endif //UCENTRALSEC_RESTAPI_USER_HANDLER_H

View File

@@ -2,8 +2,7 @@
// Created by stephane bourque on 2021-06-21.
//
#ifndef UCENTRALSEC_RESTAPI_USERS_HANDLER_H
#define UCENTRALSEC_RESTAPI_USERS_HANDLER_H
#pragma once
#include "framework/MicroService.h"
@@ -25,5 +24,3 @@ namespace OpenWifi {
};
};
#endif //UCENTRALSEC_RESTAPI_USERS_HANDLER_H

View File

@@ -0,0 +1,26 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "RESTAPI_validate_sub_token_handler.h"
#include "AuthService.h"
namespace OpenWifi {
void RESTAPI_validate_sub_token_handler::DoGet() {
Poco::URI URI(Request->getURI());
auto Parameters = URI.getQueryParameters();
for(auto const &i:Parameters) {
if (i.first == "token") {
// can we find this token?
SecurityObjects::UserInfoAndPolicy SecObj;
bool Expired = false;
if (AuthService()->IsValidSubToken(i.second, SecObj.webtoken, SecObj.userinfo, Expired)) {
Poco::JSON::Object Obj;
SecObj.to_json(Obj);
return ReturnObject(Obj);
}
}
}
return NotFound();
}
}

View File

@@ -0,0 +1,25 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_validate_sub_token_handler : public RESTAPIHandler {
public:
RESTAPI_validate_sub_token_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Server,
Internal) {};
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/validateSubToken"}; };
void DoGet() final;
void DoPost() final {};
void DoDelete() final {};
void DoPut() final {};
};
}

View File

@@ -2,11 +2,11 @@
// Created by stephane bourque on 2021-07-01.
//
#include "RESTAPI_validateToken_handler.h"
#include "RESTAPI_validate_token_handler.h"
#include "AuthService.h"
namespace OpenWifi {
void RESTAPI_validateToken_handler::DoGet() {
void RESTAPI_validate_token_handler::DoGet() {
Poco::URI URI(Request->getURI());
auto Parameters = URI.getQueryParameters();
for(auto const &i:Parameters) {

View File

@@ -2,15 +2,14 @@
// Created by stephane bourque on 2021-07-01.
//
#ifndef UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H
#define UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H
#pragma once
#include "framework/MicroService.h"
namespace OpenWifi {
class RESTAPI_validateToken_handler : public RESTAPIHandler {
class RESTAPI_validate_token_handler : public RESTAPIHandler {
public:
RESTAPI_validateToken_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
RESTAPI_validate_token_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer &Server, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_GET,
@@ -25,4 +24,3 @@ namespace OpenWifi {
};
}
#endif //UCENTRALSEC_RESTAPI_VALIDATETOKEN_HANDLER_H

View File

@@ -12,6 +12,7 @@
#include "Daemon.h"
#ifdef TIP_GATEWAY_SERVICE
#include "DeviceRegistry.h"
#include "CapabilitiesCache.h"
#endif
#include "RESTAPI_GWobjects.h"
@@ -26,7 +27,7 @@ namespace OpenWifi::GWObjects {
void Device::to_json(Poco::JSON::Object &Obj) const {
field_to_json(Obj,"serialNumber", SerialNumber);
#ifdef TIP_GATEWAY_SERVICE
field_to_json(Obj,"deviceType", Daemon::instance()->IdentifyDevice(Compatible));
field_to_json(Obj,"deviceType", CapabilitiesCache::instance()->Get(Compatible));
#endif
field_to_json(Obj,"macAddress", MACAddress);
field_to_json(Obj,"manufacturer", Manufacturer);

View File

@@ -6,8 +6,7 @@
// Arilia Wireless Inc.
//
#ifndef UCENTRAL_RESTAPI_OBJECTS_H
#define UCENTRAL_RESTAPI_OBJECTS_H
#pragma once
#include "Poco/JSON/Object.h"
#include "RESTAPI_SecurityObjects.h"
@@ -111,7 +110,7 @@ namespace OpenWifi::GWObjects {
struct DefaultConfiguration {
std::string Name;
std::string Configuration;
std::string Models;
Types::StringVec Models;
std::string Description;
uint64_t Created;
uint64_t LastModified;
@@ -191,5 +190,3 @@ namespace OpenWifi::GWObjects {
void to_json(Poco::JSON::Object &Obj) const;
};
}
#endif //UCENTRAL_RESTAPI_OBJECTS_H

View File

@@ -6,9 +6,7 @@
// Arilia Wireless Inc.
//
#ifndef OWPROV_RESTAPI_PROVOBJECTS_H
#define OWPROV_RESTAPI_PROVOBJECTS_H
#pragma once
#include <string>
#include "RESTAPI_SecurityObjects.h"
@@ -380,6 +378,3 @@ namespace OpenWifi::ProvObjects {
bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I);
bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I);
};
#endif //OWPROV_RESTAPI_PROVOBJECTS_H

View File

@@ -95,6 +95,7 @@ namespace OpenWifi {
* All user management functions
*/
bool InitializeDefaultUser();
bool CreateUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser, bool PasswordHashedAlready = false);
bool GetUserByEmail(std::string & email, SecurityObjects::UserInfo & User);
bool GetUserById(USER_ID_TYPE & Id, SecurityObjects::UserInfo & User);
@@ -108,6 +109,19 @@ namespace OpenWifi {
bool GetUsers( uint64_t Offset, uint64_t Limit, SecurityObjects::UserInfoVec & Users);
bool SetLastLogin(USER_ID_TYPE & Id);
bool CreateSubUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser, bool PasswordHashedAlready = false);
bool GetSubUserByEmail(std::string & email, SecurityObjects::UserInfo & User);
bool GetSubUserById(USER_ID_TYPE & Id, SecurityObjects::UserInfo & User);
bool DeleteSubUser(const std::string & Admin, USER_ID_TYPE & Id);
bool SetSubOwner(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Owner);
bool SetSubLocation(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Location);
AUTH_ERROR ChangeSubPassword(const std::string & Admin, USER_ID_TYPE & Id, const std::string &OldPassword, const std::string &NewPassword);
bool AddSubNotes(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Notes);
bool SetSubPolicyChange(const std::string & Admin, USER_ID_TYPE & Id, const std::string &NewPolicy);
bool UpdateSubUserInfo(const std::string & Admin, USER_ID_TYPE & Id, SecurityObjects::UserInfo &UInfo);
bool GetSubUsers( uint64_t Offset, uint64_t Limit, SecurityObjects::UserInfoVec & Users);
bool SetSubLastLogin(USER_ID_TYPE & Id);
bool SetAvatar(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);
@@ -119,6 +133,13 @@ namespace OpenWifi {
bool RevokeAllTokens( std::string & UserName );
bool GetToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate);
bool AddSubToken(std::string &UserId, std::string &Token, std::string &RefreshToken, std::string & TokenType, uint64_t Expires, uint64_t TimeOut);
bool RevokeSubToken( std::string & Token );
bool IsSubTokenRevoked( std::string & Token );
bool CleanExpiredSubTokens();
bool RevokeAllSubTokens( std::string & UserName );
bool GetSubToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate);
/*
* All ActionLinks functions
*/
@@ -142,6 +163,8 @@ namespace OpenWifi {
int Create_TokensTable();
int Create_ActionLinkTable();
int Create_Preferences();
int Create_SubTokensTable();
int Create_SubscriberTable();
Poco::Timer Timer_;
Archiver Archiver_;

View File

@@ -1522,7 +1522,8 @@ namespace OpenWifi {
bool Internal=false,
bool AlwaysAuthorize=true,
bool RateLimited=false,
const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100})
const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100},
bool SubscriberOnly=false)
: Bindings_(std::move(map)),
Logger_(l),
Methods_(std::move(Methods)),
@@ -1530,7 +1531,8 @@ namespace OpenWifi {
Internal_(Internal),
AlwaysAuthorize_(AlwaysAuthorize),
RateLimited_(RateLimited),
MyRates_(Profile){
MyRates_(Profile),
SubOnlyService_(SubscriberOnly){
}
inline bool RoleIsAuthorized(const std::string & Path, const std::string & Method, std::string & Reason) {
@@ -1551,7 +1553,7 @@ namespace OpenWifi {
return;
bool Expired=false;
if (AlwaysAuthorize_ && !IsAuthorized(Expired)) {
if (AlwaysAuthorize_ && !IsAuthorized(Expired, SubOnlyService_)) {
if(Expired)
return UnAuthorized(RESTAPI::Errors::ExpiredToken, EXPIRED_TOKEN);
return UnAuthorized(RESTAPI::Errors::InvalidCredentials, ACCESS_DENIED);
@@ -1897,7 +1899,7 @@ namespace OpenWifi {
return true;
}
inline bool IsAuthorized(bool & Expired);
inline bool IsAuthorized(bool & Expired, bool SubOnly = false );
inline void ReturnObject(Poco::JSON::Object &Object) {
PrepareResponse();
@@ -1980,6 +1982,7 @@ namespace OpenWifi {
bool Internal_=false;
bool RateLimited_=false;
bool QueryBlockInitialized_=false;
bool SubOnlyService_=false;
Poco::Net::HTTPServerRequest *Request= nullptr;
Poco::Net::HTTPServerResponse *Response= nullptr;
bool AlwaysAuthorize_=true;
@@ -2281,12 +2284,12 @@ namespace OpenWifi {
return ((T.expires_in_+T.created_)<std::time(nullptr));
}
inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) {
inline bool RetrieveTokenInformation(const std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired, bool Sub=false) {
try {
Types::StringPairVec QueryData;
QueryData.push_back(std::make_pair("token",SessionToken));
OpenAPIRequestGet Req( uSERVICE_SECURITY,
"/api/v1/validateToken",
Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken",
QueryData,
5000);
Poco::JSON::Object::Ptr Response;
@@ -2309,7 +2312,7 @@ namespace OpenWifi {
return false;
}
inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired) {
inline bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, bool & Expired, bool Sub = false) {
auto User = Cache_.get(SessionToken);
if(!User.isNull()) {
if(IsTokenExpired(User->webtoken)) {
@@ -2320,7 +2323,7 @@ namespace OpenWifi {
UInfo = *User;
return true;
}
return RetrieveTokenInformation(SessionToken, UInfo, Expired);
return RetrieveTokenInformation(SessionToken, UInfo, Expired, Sub);
}
private:
@@ -3707,9 +3710,9 @@ namespace OpenWifi {
}
#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, bool & Expired , bool Sub );
#endif
inline bool RESTAPIHandler::IsAuthorized( bool & Expired ) {
inline bool RESTAPIHandler::IsAuthorized( bool & Expired , bool Sub ) {
if(Internal_) {
auto Allowed = MicroService::instance().IsValidAPIKEY(*Request);
if(!Allowed) {
@@ -3739,9 +3742,9 @@ namespace OpenWifi {
}
}
#ifdef TIP_SECURITY_SERVICE
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired)) {
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, Expired, Sub)) {
#else
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired)) {
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, Expired, Sub)) {
#endif
if(Server_.LogIt(Request->getMethod(),true)) {
Logger_.debug(Poco::format("X-REQ-ALLOWED(%s): User='%s@%s' Method='%s' Path='%s",

View File

@@ -0,0 +1,5 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "storage_conversions.h"

View File

@@ -0,0 +1,77 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include "framework/MicroService.h"
#include "RESTObjects/RESTAPI_SecurityObjects.h"
namespace OpenWifi {
inline bool Convert(const UserInfoRecord &T, SecurityObjects::UserInfo &U) {
U.Id = T.get<0>();
U.name = T.get<1>();
U.description = T.get<2>();
U.avatar = T.get<3>();
U.email = T.get<4>();
U.validated = T.get<5>();
U.validationEmail = T.get<6>();
U.validationDate = T.get<7>();
U.creationDate = T.get<8>();
U.validationURI = T.get<9>();
U.changePassword = T.get<10>();
U.lastLogin = T.get<11>();
U.currentLoginURI = T.get<12>();
U.lastPasswordChange = T.get<13>();
U.lastEmailCheck = T.get<14>();
U.waitingForEmailCheck = T.get<15>();
U.locale = T.get<16>();
U.notes = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(T.get<17>());
U.location = T.get<18>();
U.owner = T.get<19>();
U.suspended = T.get<20>();
U.blackListed = T.get<21>();
U.userRole = SecurityObjects::UserTypeFromString(T.get<22>());
U.userTypeProprietaryInfo = RESTAPI_utils::to_object<SecurityObjects::UserLoginLoginExtensions>(T.get<23>());
U.securityPolicy = T.get<24>();
U.securityPolicyChange = T.get<25>();
U.currentPassword = T.get<26>();
U.lastPasswords = RESTAPI_utils::to_object_array(T.get<27>());
U.oauthType = T.get<28>();
U.oauthUserInfo = T.get<29>();
return true;
}
inline bool Convert(const SecurityObjects::UserInfo &U, UserInfoRecord &T) {
T.set<0>(U.Id);
T.set<1>(U.name);
T.set<2>(U.description);
T.set<3>(U.avatar);
T.set<4>(U.email);
T.set<5>(U.validated);
T.set<6>(U.validationEmail);
T.set<7>(U.validationDate);
T.set<8>(U.creationDate);
T.set<9>(U.validationURI);
T.set<10>(U.changePassword);
T.set<11>(U.lastLogin);
T.set<12>(U.currentLoginURI);
T.set<13>(U.lastPasswordChange);
T.set<14>(U.lastEmailCheck);
T.set<15>(U.waitingForEmailCheck);
T.set<16>(U.locale);
T.set<17>(RESTAPI_utils::to_string(U.notes));
T.set<18>(U.location);
T.set<19>(U.owner);
T.set<20>(U.suspended);
T.set<21>(U.blackListed);
T.set<22>(SecurityObjects::UserTypeToString(U.userRole));
T.set<23>(RESTAPI_utils::to_string(U.userTypeProprietaryInfo));
T.set<24>(U.securityPolicy);
T.set<25>(U.securityPolicyChange);
T.set<26>(U.currentPassword);
T.set<27>(RESTAPI_utils::to_string(U.lastPasswords));
T.set<28>(U.oauthType);
T.set<29>(U.oauthUserInfo);
return true;
}
}

View File

@@ -0,0 +1,283 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include <vector>
#include "Poco/Tuple.h"
#include "storage_subscribers.h"
#include "StorageService.h"
#include "framework/MicroService.h"
#include "storage/storage_conversions.h"
namespace OpenWifi {
bool Storage::CreateSubUser(const std::string & Admin, SecurityObjects::UserInfo & NewUser, bool PasswordHashedAlready ) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::toLowerInPlace(NewUser.email);
// if the user exists, must return an error
std::string St1{"select " + AllSubUsersFieldsForSelect + " from Subscribers where email=?"};
UserInfoRecordList Records;
try {
Poco::Data::Statement Statement(Sess);
Statement << ConvertParams(St1),
Poco::Data::Keywords::into(Records),
Poco::Data::Keywords::use(NewUser.email);
Statement.execute();
} catch (const Poco::Exception &E) {
}
if(!Records.empty())
return false;
if(!PasswordHashedAlready) {
NewUser.Id = MicroService::CreateUUID();
NewUser.creationDate = std::time(nullptr);
}
// if there is a password, we assume that we do not want email verification,
// if there is no password, we will do email verification
if(NewUser.currentPassword.empty()) {
} else {
if(!PasswordHashedAlready) {
NewUser.currentPassword = AuthService()->ComputeNewPasswordHash(NewUser.email,NewUser.currentPassword);
NewUser.lastPasswords.clear();
NewUser.lastPasswords.push_back(NewUser.currentPassword);
NewUser.lastPasswordChange = std::time(nullptr);
NewUser.validated = true;
}
}
auto Notes = RESTAPI_utils::to_string(NewUser.notes);
auto UserType = SecurityObjects::UserTypeToString(NewUser.userRole);
auto OldPasswords = RESTAPI_utils::to_string(NewUser.lastPasswords);
auto userTypeProprietaryInfo = RESTAPI_utils::to_string(NewUser.userTypeProprietaryInfo);
St1 = "INSERT INTO Subscribers (" + AllSubUsersFieldsForSelect + ") VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
Poco::Data::Statement Statement(Sess);
UserInfoRecord R;
Convert(NewUser, R);
Statement << ConvertParams(St1),
Poco::Data::Keywords::use(R);
Statement.execute();
return true;
} catch (const Poco::Exception &E) {
std::cout << "What: " << E.what() << " name: " << E.name() << std::endl;
Logger_.log(E);
}
return false;
}
bool Storage::GetSubUserByEmail(std::string & email, SecurityObjects::UserInfo & User) {
std::string St1;
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
Poco::toLowerInPlace(email);
// if the user exists, must return an error
St1 = "select " + AllSubUsersFieldsForSelect + " from Subscribers where email=?";
UserInfoRecordList Records;
Select << ConvertParams(St1) ,
Poco::Data::Keywords::into(Records),
Poco::Data::Keywords::use(email);
Select.execute();
if(Records.empty())
return false;
Convert(Records[0],User);
return true;
} catch (const Poco::Exception &E) {
std::cout << "Statement: " << St1 << std::endl;
std::cout << "What:" << E.what() << " name: " << E.name() << std::endl;
Logger_.log(E);
}
return false;
}
bool Storage::GetSubUserById(std::string &Id, SecurityObjects::UserInfo &User) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
// if the user exists, must return an error
std::string St1{"select " + AllSubUsersFieldsForSelect + " from Subscribers where id=?"};
UserInfoRecordList Records;
Select << ConvertParams(St1) ,
Poco::Data::Keywords::into(Records),
Poco::Data::Keywords::use(Id);
Select.execute();
if(Records.empty())
return false;
Convert(Records[0],User);
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::GetSubUsers( uint64_t Offset, uint64_t HowMany, SecurityObjects::UserInfoVec & Users) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
UserInfoRecordList Records;
std::string St1{"select " + AllSubUsersFieldsForSelect + " from Subscribers order by id ASC "};
Select << ConvertParams(St1) + ComputeRange(Offset, HowMany),
Poco::Data::Keywords::into(Records);
Select.execute();
for(const auto &R:Records) {
SecurityObjects::UserInfo U;
Convert(R,U);
Users.push_back(U);
}
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::UpdateSubUserInfo(const std::string & Admin, USER_ID_TYPE & Id, SecurityObjects::UserInfo &UInfo) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Update(Sess);
std::string St1{"update Subscribers set " + AllSubUsersFieldsForUpdate + " where id=?"};
auto Notes = RESTAPI_utils::to_string(UInfo.notes);
auto UserType = SecurityObjects::UserTypeToString(UInfo.userRole);
auto OldPasswords = RESTAPI_utils::to_string(UInfo.lastPasswords);
auto userTypeProprietaryInfo = RESTAPI_utils::to_string(UInfo.userTypeProprietaryInfo);
UserInfoRecord R;
Convert(UInfo, R);
Update << ConvertParams(St1),
Poco::Data::Keywords::use(R),
Poco::Data::Keywords::use(UInfo.Id);
Update.execute();
return true;
} catch (const Poco::Exception &E) {
std::cout << " Exception: " << E.what() << " name: " << E.name() << std::endl;
Logger_.log(E);
}
return false;
}
bool Storage::DeleteSubUser(const std::string & Admin, USER_ID_TYPE & Id) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
std::string St1{"delete from Subscribers where id=?"};
Delete << ConvertParams(St1),
Poco::Data::Keywords::use(Id);
Delete.execute();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::SetSubOwner(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Owner) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::SetSubLocation(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Location) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::SetSubLastLogin(std::string &Id) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Update(Sess);
std::string St1{"update Subscribers set lastLogin=? where id=?"};
uint64_t Now=std::time(nullptr);
Update << ConvertParams(St1),
Poco::Data::Keywords::use(Now),
Poco::Data::Keywords::use(Id);
Update.execute();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
Storage::AUTH_ERROR Storage::ChangeSubPassword(const std::string & Admin, USER_ID_TYPE & Id, const std::string &OldPassword, const std::string &NewPassword) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
return SUCCESS;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return INTERNAL_ERROR;
}
bool Storage::AddSubNotes(const std::string & Admin, USER_ID_TYPE & Id, const std::string &Notes) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::SetSubPolicyChange(const std::string & Admin, USER_ID_TYPE & Id, const std::string &NewPolicy) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Insert(Sess);
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
}

View File

@@ -0,0 +1,142 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include <string>
#include <vector>
#include "Poco/Tuple.h"
namespace OpenWifi {
static const std::string AllSubUsersFieldsForCreation{
" Id varchar(36) UNIQUE PRIMARY KEY,"
"name varchar,"
"description varchar,"
"avatar varchar,"
"email varchar,"
"validated int,"
"validationEmail varchar,"
"validationDate bigint,"
"creationDate bigint,"
"validationURI varchar,"
"changePassword int,"
"lastLogin bigint,"
"currentLoginURI varchar,"
"lastPasswordChange bigint,"
"lastEmailCheck bigint,"
"waitingForEmailCheck int,"
"locale varchar,"
"notes text,"
"location varchar,"
"owner varchar,"
"suspended int,"
"blackListed int,"
"userRole varchar,"
"userTypeProprietaryInfo text,"
"securityPolicy text,"
"securityPolicyChange bigint,"
"currentPassword varchar,"
"lastPasswords varchar,"
"oauthType varchar,"
"oauthUserInfo text"};
static const std::string AllSubUsersFieldsForSelect{
"Id,"
"name,"
"description,"
"avatar,"
"email,"
"validated,"
"validationEmail,"
"validationDate,"
"creationDate,"
"validationURI,"
"changePassword,"
"lastLogin,"
"currentLoginURI,"
"lastPasswordChange,"
"lastEmailCheck,"
"waitingForEmailCheck,"
"locale,"
"notes,"
"location,"
"owner,"
"suspended,"
"blackListed,"
"userRole,"
"userTypeProprietaryInfo,"
"securityPolicy,"
"securityPolicyChange,"
"currentPassword,"
"lastPasswords,"
"oauthType,"
"oauthUserInfo"};
static const std::string AllSubUsersFieldsForUpdate{
" Id=?, "
"name=?, "
"description=?, "
"avatar=?, "
"email=?, "
"validated=?, "
"validationEmail=?, "
"validationDate=?, "
"creationDate=?, "
"validationURI=?, "
"changePassword=?, "
"lastLogin=?, "
"currentLoginURI=?, "
"lastPasswordChange=?, "
"lastEmailCheck=?, "
"waitingForEmailCheck=?, "
"locale=?, "
"notes=?, "
"location=?, "
"owner=?, "
"suspended=?, "
"blackListed=?, "
"userRole=?, "
"userTypeProprietaryInfo=?, "
"securityPolicy=?, "
"securityPolicyChange=?, "
"currentPassword=?, "
"lastPasswords=?, "
"oauthType=?, "
"oauthUserInfo=? "};
typedef Poco::Tuple <
std::string, // Id = 0;
std::string, // name;
std::string, // description;
std::string, // avatar;
std::string, // email;
uint64_t, // bool validated = false;
std::string, // validationEmail;
uint64_t, // validationDate = 0;
uint64_t, // creationDate = 0;
std::string, // validationURI;
uint64_t, // bool changePassword = true;
uint64_t, // lastLogin = 0;
std::string, // currentLoginURI;
uint64_t, // lastPasswordChange = 0;
uint64_t, // lastEmailCheck = 0;
uint64_t, // bool waitingForEmailCheck = false;
std::string, // locale;
std::string, // notes;
std::string, // location;
std::string, // owner;
uint64_t, // bool suspended = false;
uint64_t, // bool blackListed = false;
std::string, // userRole;
std::string, // userTypeProprietaryInfo;
std::string, // securityPolicy;
uint64_t, // securityPolicyChange;
std::string, // currentPassword;
std::string, // lastPasswords;
std::string, // oauthType;
std::string // oauthUserInfo;
> UserInfoRecord;
typedef std::vector <UserInfoRecord> UserInfoRecordList;
}

View File

@@ -0,0 +1,150 @@
//
// Created by stephane bourque on 2021-11-30.
//
#include "storage/storage_subtokens.h"
#include "StorageService.h"
namespace OpenWifi {
/*
"Token TEXT PRIMARY KEY, "
"RefreshToken TEXT, "
"TokenType TEXT, "
"UserName TEXT, "
"Created BIGINT, "
"Expires BIGINT, "
"IdleTimeOut BIGINT, "
"RevocationDate BIGINT "
*/
bool Storage::AddSubToken(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);
uint64_t Now = std::time(nullptr);
uint64_t Z = 0;
std::string St2{
"INSERT INTO SubTokens (" + AllSubTokensFieldsForSelect + ") VALUES(" + AllSubTokensValuesForSelect + ")"};
Insert << ConvertParams(St2),
Poco::Data::Keywords::use(Token),
Poco::Data::Keywords::use(RefreshToken),
Poco::Data::Keywords::use(TokenType),
Poco::Data::Keywords::use(UserID),
Poco::Data::Keywords::use(Now),
Poco::Data::Keywords::use(Expires),
Poco::Data::Keywords::use(TimeOut),
Poco::Data::Keywords::use(Z);
Insert.execute();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::GetSubToken(std::string &Token, SecurityObjects::UserInfoAndPolicy &UInfo, uint64_t &RevocationDate) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
RevocationDate = 0 ;
std::string St2{"SELECT " + AllSubTokensFieldsForSelect + " From SubTokens 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.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();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::IsSubTokenRevoked(std::string &Token) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Select(Sess);
uint32_t RevocationDate = 0 ;
std::string St2{"SELECT RevocationDate From SubTokens WHERE Token=?"};
Select << ConvertParams(St2),
Poco::Data::Keywords::into(RevocationDate),
Poco::Data::Keywords::use(Token);
Select.execute();
if(Select.columnsExtracted()==0)
return false;
return RevocationDate>0 ;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::RevokeSubToken(std::string &Token) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Update(Sess);
uint64_t Now = std::time(nullptr);
// update users set lastLogin=? where id=?
std::string St2{"UPDATE SubTokens Set RevocationDate=? WHERE Token=?"};
Update << ConvertParams(St2),
Poco::Data::Keywords::use(Now),
Poco::Data::Keywords::use(Token);
Update.execute();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::CleanExpiredSubTokens() {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
uint64_t Now = std::time(nullptr);
std::string St2{"DELETE From SubTokens WHERE (Created+Expires) <= ?"};
Delete << ConvertParams(St2),
Poco::Data::Keywords::use(Now);
Delete.execute();
return true;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
bool Storage::RevokeAllSubTokens(std::string & UserId) {
try {
Poco::Data::Session Sess = Pool_->get();
Poco::Data::Statement Delete(Sess);
std::string St2{"DELETE SubFrom Tokens WHERE Username=?"};
Delete << ConvertParams(St2),
Poco::Data::Keywords::use(UserId);
Delete.execute();
return true;
} catch(const Poco::Exception &E) {
Logger_.log(E);
}
return false;
}
}

View File

@@ -0,0 +1,27 @@
//
// Created by stephane bourque on 2021-11-30.
//
#pragma once
#include <string>
#include <vector>
#include "Poco/Tuple.h"
namespace OpenWifi {
static std::string AllSubTokensFieldsForCreation{ "Token TEXT PRIMARY KEY, "
"RefreshToken TEXT, "
"TokenType TEXT, "
"UserName TEXT, "
"Created BIGINT, "
"Expires BIGINT, "
"IdleTimeOut BIGINT, "
"RevocationDate BIGINT "
};
static std::string AllSubTokensFieldsForSelect {"Token, RefreshToken, TokenType, Username, Created, Expires, IdleTimeOut, RevocationDate"};
static std::string AllSubTokensValuesForSelect{"?,?,?,?,?,?,?,?"};
}

View File

@@ -17,6 +17,8 @@ namespace OpenWifi {
Create_TokensTable();
Create_ActionLinkTable();
Create_Preferences();
Create_SubTokensTable();
Create_SubscriberTable();
return 0;
}
@@ -45,6 +47,31 @@ namespace OpenWifi {
return 1;
}
int Storage::Create_SubscriberTable() {
Poco::Data::Session Sess = Pool_->get();
try {
if (dbType_ == mysql) {
Sess << "CREATE TABLE IF NOT EXISTS Subscribers (" +
AllUsersFieldsForCreation +
" ,INDEX emailindex (email ASC)"
" ,INDEX nameindex (name ASC))",
Poco::Data::Keywords::now;
} else {
Sess << "CREATE TABLE IF NOT EXISTS Subscribers (" +
AllUsersFieldsForCreation +
")",
Poco::Data::Keywords::now;
Sess << "CREATE INDEX IF NOT EXISTS emailindex ON Subscribers (email ASC)", Poco::Data::Keywords::now;
Sess << "CREATE INDEX IF NOT EXISTS nameindex ON Subscribers (name ASC)", Poco::Data::Keywords::now;
}
return 0;
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
return 1;
}
int Storage::Create_ActionLinkTable() {
try {
Poco::Data::Session Sess = Pool_->get();
@@ -93,6 +120,19 @@ namespace OpenWifi {
return 1;
}
int Storage::Create_SubTokensTable() {
try {
Poco::Data::Session Sess = Pool_->get();
Sess << "CREATE TABLE IF NOT EXISTS SubTokens (" +
AllTokensFieldsForCreation +
") ", Poco::Data::Keywords::now;
return 0;
} catch(const Poco::Exception &E) {
Logger_.log(E);
}
return 1;
}
int Storage::Create_Preferences() {
try {
Poco::Data::Session Sess = Pool_->get();

View File

@@ -9,77 +9,10 @@
#include "StorageService.h"
#include "framework/MicroService.h"
#include "storage/storage_conversions.h"
namespace OpenWifi {
bool Convert(const UserInfoRecord &T, SecurityObjects::UserInfo &U) {
U.Id = T.get<0>();
U.name = T.get<1>();
U.description = T.get<2>();
U.avatar = T.get<3>();
U.email = T.get<4>();
U.validated = T.get<5>();
U.validationEmail = T.get<6>();
U.validationDate = T.get<7>();
U.creationDate = T.get<8>();
U.validationURI = T.get<9>();
U.changePassword = T.get<10>();
U.lastLogin = T.get<11>();
U.currentLoginURI = T.get<12>();
U.lastPasswordChange = T.get<13>();
U.lastEmailCheck = T.get<14>();
U.waitingForEmailCheck = T.get<15>();
U.locale = T.get<16>();
U.notes = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(T.get<17>());
U.location = T.get<18>();
U.owner = T.get<19>();
U.suspended = T.get<20>();
U.blackListed = T.get<21>();
U.userRole = SecurityObjects::UserTypeFromString(T.get<22>());
U.userTypeProprietaryInfo = RESTAPI_utils::to_object<SecurityObjects::UserLoginLoginExtensions>(T.get<23>());
U.securityPolicy = T.get<24>();
U.securityPolicyChange = T.get<25>();
U.currentPassword = T.get<26>();
U.lastPasswords = RESTAPI_utils::to_object_array(T.get<27>());
U.oauthType = T.get<28>();
U.oauthUserInfo = T.get<29>();
return true;
}
bool Convert(const SecurityObjects::UserInfo &U, UserInfoRecord &T) {
T.set<0>(U.Id);
T.set<1>(U.name);
T.set<2>(U.description);
T.set<3>(U.avatar);
T.set<4>(U.email);
T.set<5>(U.validated);
T.set<6>(U.validationEmail);
T.set<7>(U.validationDate);
T.set<8>(U.creationDate);
T.set<9>(U.validationURI);
T.set<10>(U.changePassword);
T.set<11>(U.lastLogin);
T.set<12>(U.currentLoginURI);
T.set<13>(U.lastPasswordChange);
T.set<14>(U.lastEmailCheck);
T.set<15>(U.waitingForEmailCheck);
T.set<16>(U.locale);
T.set<17>(RESTAPI_utils::to_string(U.notes));
T.set<18>(U.location);
T.set<19>(U.owner);
T.set<20>(U.suspended);
T.set<21>(U.blackListed);
T.set<22>(SecurityObjects::UserTypeToString(U.userRole));
T.set<23>(RESTAPI_utils::to_string(U.userTypeProprietaryInfo));
T.set<24>(U.securityPolicy);
T.set<25>(U.securityPolicyChange);
T.set<26>(U.currentPassword);
T.set<27>(RESTAPI_utils::to_string(U.lastPasswords));
T.set<28>(U.oauthType);
T.set<29>(U.oauthUserInfo);
return true;
}
std::string OldDefaultUseridStockUUID{"DEFAULT-USER-UUID-SHOULD-BE-DELETED!!!"};
std::string NewDefaultUseridStockUUID{"11111111-0000-0000-6666-999999999999"};

View File

@@ -2,11 +2,7 @@
// Created by stephane bourque on 2021-07-15.
//
#ifndef UCENTRALSEC_STORAGE_USERS_H
#define UCENTRALSEC_STORAGE_USERS_H
#include <string>
#include <vector>
#pragma once
namespace OpenWifi {
static const std::string AllUsersFieldsForCreation{
@@ -140,4 +136,3 @@ namespace OpenWifi {
typedef std::vector <UserInfoRecord> UserInfoRecordList;
}
#endif //UCENTRALSEC_STORAGE_USERS_H