Files
wlan-cloud-ucentralsec/src/AuthService.cpp
2021-07-08 19:23:29 -07:00

262 lines
8.6 KiB
C++

//
// License type: BSD 3-Clause License
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
//
// Created by Stephane Bourque on 2021-03-04.
// Arilia Wireless Inc.
//
#include <ctime>
#include "Poco/Net/OAuth20Credentials.h"
#include "Poco/JWT/Token.h"
#include "Poco/JWT/Signer.h"
#include "Daemon.h"
#include "RESTAPI_handler.h"
#include "StorageService.h"
#include "AuthService.h"
#include "Utils.h"
#include "KafkaManager.h"
#include "Kafka_topics.h"
namespace uCentral {
class AuthService *AuthService::instance_ = nullptr;
AuthService::ACCESS_TYPE AuthService::IntToAccessType(int C) {
switch (C) {
case 1: return USERNAME;
case 2: return SERVER;
case 3: return CUSTOM;
default:
return USERNAME;
}
}
int AuthService::AccessTypeToInt(ACCESS_TYPE T) {
switch (T) {
case USERNAME: return 1;
case SERVER: return 2;
case CUSTOM: return 3;
}
return 1; // some compilers complain...
}
int AuthService::Start() {
Signer_.setRSAKey(Daemon()->Key());
Signer_.addAllAlgorithms();
Logger_.notice("Starting...");
Secure_ = Daemon()->ConfigGetBool("authentication.enabled",true);
DefaultPassword_ = Daemon()->ConfigGetString("authentication.default.password","");
DefaultUserName_ = Daemon()->ConfigGetString("authentication.default.username","");
Mechanism_ = Daemon()->ConfigGetString("authentication.service.type","internal");
return 0;
}
void AuthService::Stop() {
Logger_.notice("Stopping...");
}
bool AuthService::IsAuthorized(Poco::Net::HTTPServerRequest & Request, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo )
{
if(!Secure_)
return true;
SubMutexGuard Guard(Mutex_);
std::string CallToken;
try {
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
CallToken = Auth.getBearerToken();
}
} catch(const Poco::Exception &E) {
}
if(CallToken.empty())
CallToken = Request.get("X-API-KEY ", "");
if(CallToken.empty())
return false;
auto Client = UserCache_.find(CallToken);
if( Client == UserCache_.end() )
return ValidateToken(CallToken, CallToken, UInfo);
if((Client->second.webtoken.created_ + Client->second.webtoken.expires_in_) > time(nullptr)) {
SessionToken = CallToken;
UInfo = Client->second ;
return true;
}
UserCache_.erase(CallToken);
return false;
}
void AuthService::Logout(const std::string &token) {
SubMutexGuard Guard(Mutex_);
UserCache_.erase(token);
try {
Poco::JSON::Object Obj;
Obj.set("event", "remove-token");
Obj.set("id", Daemon()->ID());
Obj.set("token", token);
std::stringstream ResultText;
Poco::JSON::Stringifier::stringify(Obj, ResultText);
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS, Daemon()->PrivateEndPoint(), ResultText.str(),
false);
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
std::string AuthService::GenerateToken(const std::string & Identity, ACCESS_TYPE Type, int NumberOfDays) {
SubMutexGuard Guard(Mutex_);
Poco::JWT::Token T;
T.setType("JWT");
switch(Type) {
case USERNAME: T.setSubject("usertoken"); break;
case SERVER: T.setSubject("servertoken"); break;
case CUSTOM: T.setSubject("customtoken"); break;
}
T.payload().set("identity", Identity);
T.setIssuedAt(Poco::Timestamp());
T.setExpiration(Poco::Timestamp() + Poco::Timespan(NumberOfDays,0,0,0,0));
std::string JWT = Signer_.sign(T,Poco::JWT::Signer::ALGO_RS256);
return JWT;
}
bool AuthService::ValidateToken(const std::string & Token, std::string & SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo ) {
SubMutexGuard Guard(Mutex_);
Poco::JWT::Token DecryptedToken;
try {
if (Signer_.tryVerify(Token, DecryptedToken)) {
auto Expires = DecryptedToken.getExpiration();
if (Expires > Poco::Timestamp()) {
auto Identity = DecryptedToken.payload().get("identity").toString();
auto IssuedAt = DecryptedToken.getIssuedAt();
auto Subject = DecryptedToken.getSubject();
UInfo.webtoken.access_token_ = Token;
UInfo.webtoken.refresh_token_= Token;
UInfo.webtoken.username_ = Identity;
UInfo.webtoken.id_token_ = Token;
UInfo.webtoken.token_type_ = "Bearer";
UInfo.webtoken.created_ = IssuedAt.epochTime();
UInfo.webtoken.expires_in_ = Expires.epochTime() - IssuedAt.epochTime();
UInfo.webtoken.idle_timeout_ = 5*60;
UserCache_[UInfo.webtoken.access_token_] = UInfo;
return true;
}
}
} catch (const Poco::Exception &E ) {
Logger_.log(E);
}
return false;
}
void AuthService::CreateToken(const std::string & UserName, SecurityObjects::UserInfoAndPolicy &UInfo)
{
SubMutexGuard Guard(Mutex_);
std::string Token = GenerateToken(UserName,USERNAME,30);
SecurityObjects::AclTemplate ACL;
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
UInfo.webtoken.acl_template_ = ACL;
UInfo.webtoken.expires_in_ = 30 * 24 * 60 * 60 ;
UInfo.webtoken.idle_timeout_ = 5 * 60;
UInfo.webtoken.token_type_ = "Bearer";
UInfo.webtoken.access_token_ = Token;
UInfo.webtoken.id_token_ = Token;
UInfo.webtoken.refresh_token_ = Token;
UInfo.webtoken.created_ = time(nullptr);
UInfo.webtoken.username_ = UserName;
UInfo.webtoken.errorCode = 0;
UInfo.webtoken.userMustChangePassword = false;
UserCache_[Token] = UInfo;
}
bool AuthService::SetPassword(const std::string &NewPassword, SecurityObjects::UserInfo & UInfo) {
auto NewPasswordHash = ComputePasswordHash(UInfo.email, NewPassword);
for (auto const &i:UInfo.lastPasswords) {
if (i == NewPasswordHash) {
return false;
}
}
if(UInfo.lastPasswords.size()==5) {
UInfo.lastPasswords.erase(UInfo.lastPasswords.begin());
}
UInfo.lastPasswords.push_back(NewPasswordHash);
UInfo.currentPassword = NewPasswordHash;
return true;
}
bool AuthService::Authorize( std::string & UserName, const std::string & Password, const std::string & NewPassword, SecurityObjects::UserInfoAndPolicy & UInfo )
{
SubMutexGuard Guard(Mutex_);
SecurityObjects::AclTemplate ACL;
Poco::toLowerInPlace(UserName);
auto PasswordHash = ComputePasswordHash(UserName, Password);
if(Storage()->GetUserByEmail(UserName,UInfo.userinfo)) {
if(PasswordHash != UInfo.userinfo.currentPassword) {
return false;
}
if(UInfo.userinfo.changePassword && NewPassword.empty()) {
UInfo.webtoken.userMustChangePassword = true ;
return true;
}
if(UInfo.userinfo.changePassword || !NewPassword.empty()) {
if(!SetPassword(NewPassword,UInfo.userinfo)) {
UInfo.webtoken.errorCode = 1;
return true;
}
UInfo.userinfo.lastPasswordChange = std::time(nullptr);
UInfo.userinfo.changePassword = false;
Storage()->UpdateUserInfo(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.
CreateToken(UserName, UInfo );
return true;
}
if(((UserName == DefaultUserName_) && (DefaultPassword_== ComputePasswordHash(UserName,Password))) || !Secure_)
{
ACL.PortalLogin_ = ACL.Read_ = ACL.ReadWrite_ = ACL.ReadWriteCreate_ = ACL.Delete_ = true;
UInfo.webtoken.acl_template_ = ACL;
UInfo.userinfo.email = DefaultUserName_;
UInfo.userinfo.currentPassword = DefaultPassword_;
UInfo.userinfo.name = DefaultUserName_;
CreateToken(UserName, UInfo );
return true;
}
return false;
}
std::string AuthService::ComputePasswordHash(const std::string &UserName, const std::string &Password) {
std::string UName = Poco::trim(Poco::toLower(UserName));
SHA2_.update(Password + UName);
return uCentral::Utils::ToHex(SHA2_.digest());
}
bool AuthService::IsValidToken(const std::string &Token, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo) {
return true;
}
} // end of namespace