Fixing internal security token problem

This commit is contained in:
stephb9959
2021-07-18 22:13:10 -07:00
parent c585dc9884
commit 734ce263ee
19 changed files with 181 additions and 113 deletions

2
build
View File

@@ -1 +1 @@
6
7

0
data/user1 Normal file
View File

View File

@@ -113,20 +113,22 @@ namespace uCentral {
cppkafka::Consumer Consumer(Config);
Consumer.set_assignment_callback([this](cppkafka::TopicPartitionList& partitions) {
if(partitions.size()>0) {
if(!partitions.empty()) {
Logger_.information(Poco::format("Partition assigned: %Lu...",
(uint64_t)partitions.front().get_partition()));
}
});
Consumer.set_revocation_callback([this](const cppkafka::TopicPartitionList& partitions) {
if(partitions.size()>0) {
if(!partitions.empty()) {
Logger_.information(Poco::format("Partition revocation: %Lu...",
(uint64_t)partitions.front().get_partition()));
}
});
bool AutoCommit = Daemon()->ConfigGetBool("ucentral.kafka.auto.commit",false);
auto BatchSize = Daemon()->ConfigGetInt("ucentral.kafka.consumer.batchsize",20);
Types::StringVec Topics;
Types::StringVec Topics;
for(const auto &i:Notifiers_)
Topics.push_back(i.first);
@@ -135,28 +137,31 @@ namespace uCentral {
ConsumerRunning_ = true;
while(ConsumerRunning_) {
try {
cppkafka::Message Msg = Consumer.poll(std::chrono::milliseconds(200));
if (!Msg)
continue;
if (Msg.get_error()) {
if (!Msg.is_eof()) {
Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
}
Consumer.commit(Msg);
continue;
}
SubMutexGuard G(ConsumerMutex_);
auto It = Notifiers_.find(Msg.get_topic());
if (It != Notifiers_.end()) {
Types::TopicNotifyFunctionList &FL = It->second;
std::string Key{Msg.get_key()};
std::string Payload{Msg.get_payload()};
for (auto &F : FL) {
std::thread T(F.first, Key, Payload);
T.detach();
}
}
Consumer.commit(Msg);
std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(200));
for(auto const &Msg:MsgVec) {
if (!Msg)
continue;
if (Msg.get_error()) {
if (!Msg.is_eof()) {
Logger_.error(Poco::format("Error: %s", Msg.get_error().to_string()));
}if(!AutoCommit)
Consumer.async_commit(Msg);
continue;
}
SubMutexGuard G(ConsumerMutex_);
auto It = Notifiers_.find(Msg.get_topic());
if (It != Notifiers_.end()) {
Types::TopicNotifyFunctionList &FL = It->second;
std::string Key{Msg.get_key()};
std::string Payload{Msg.get_payload()};
for (auto &F : FL) {
std::thread T(F.first, Key, Payload);
T.detach();
}
}
if (!AutoCommit)
Consumer.async_commit(Msg);
}
} catch (const cppkafka::HandleException &E) {
Logger_.warning(Poco::format("Caught a Kafka exception (consumer): %s",std::string{E.what()}));
} catch (const Poco::Exception &E) {

View File

@@ -10,14 +10,15 @@
namespace uCentral {
class RESTAPI_AssetServer : public RESTAPIHandler {
public:
RESTAPI_AssetServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_AssetServer(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, 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}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/wwwassets/{id}" ,
"/favicon.ico"}; };

View File

@@ -64,7 +64,7 @@ namespace uCentral {
Poco::URI uri(Request.getURI());
const auto & Path = uri.getPath();
RESTAPIHandler::BindingMap Bindings;
return RESTAPI_Router<
return RESTAPI_Router_I<
RESTAPI_users_handler,
RESTAPI_user_handler,
RESTAPI_system_command,

View File

@@ -18,12 +18,13 @@
namespace uCentral {
class RESTAPI_action_links : public RESTAPIHandler {
public:
RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_action_links(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/actionLink"}; };

View File

@@ -33,13 +33,14 @@ namespace uCentral {
class RESTAPI_avatarHandler : public RESTAPIHandler {
public:
RESTAPI_avatarHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_avatarHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{
Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) override;

View File

@@ -25,6 +25,7 @@
#include "RESTAPI_handler.h"
#include "RESTAPI_protocol.h"
#include "Utils.h"
#include "Daemon.h"
namespace uCentral {
@@ -306,10 +307,6 @@ namespace uCentral {
bool RESTAPIHandler::ContinueProcessing(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) {
if (Request.getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
/* std::cout << "REQUEST:" << std::endl;
for(const auto &[f,s]:Request)
std::cout << "First: " << f << " second:" << s << std::endl;
*/
ProcessOptions(Request, Response);
return false;
} else if (std::find(Methods_.begin(), Methods_.end(), Request.getMethod()) == Methods_.end()) {
@@ -322,45 +319,34 @@ namespace uCentral {
bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) {
if(SessionToken_.empty()) {
try {
Poco::Net::OAuth20Credentials Auth(Request);
if(Internal_) {
return Daemon()->IsValidAPIKEY(Request);
} else {
if (SessionToken_.empty()) {
try {
Poco::Net::OAuth20Credentials Auth(Request);
if (Auth.getScheme() == "Bearer") {
SessionToken_ = Auth.getBearerToken();
}
} catch(const Poco::Exception &E) {
Logger_.log(E);
}
}
#ifdef TIP_SECURITY_SERVICE
if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
if (Auth.getScheme() == "Bearer") {
SessionToken_ = Auth.getBearerToken();
}
} catch (const Poco::Exception &E) {
Logger_.log(E);
}
}
#ifdef TIP_SECURITY_SERVICE
if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
#else
if (AuthClient()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
if (AuthClient()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
#endif
return true;
} else {
UnAuthorized(Request, Response);
}
return false;
}
bool RESTAPIHandler::IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response, std::string &UserName) {
#ifdef TIP_SECURITY_SERVICE
if (AuthService()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
#else
if (AuthClient()->IsAuthorized(Request, SessionToken_, UserInfo_)) {
#endif
UserName = UserInfo_.webtoken.username_;
return true;
} else {
UnAuthorized(Request, Response);
}
return false;
return true;
} else {
UnAuthorized(Request, Response);
}
return false;
}
}
/*
bool RESTAPIHandler::ValidateAPIKey(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response) {
auto Key = Request.get("X-API-KEY", "");
@@ -370,7 +356,7 @@ namespace uCentral {
return true;
}
*/
void RESTAPIHandler::ReturnObject(Poco::Net::HTTPServerRequest &Request, Poco::JSON::Object &Object,
Poco::Net::HTTPServerResponse &Response) {
PrepareResponse(Request, Response);

View File

@@ -91,8 +91,8 @@ namespace uCentral {
typedef std::map<std::string, std::string> BindingMap;
RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods)
: Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)) {}
RESTAPIHandler(BindingMap map, Poco::Logger &l, std::vector<std::string> Methods, bool Internal=false)
: Bindings_(std::move(map)), Logger_(l), Methods_(std::move(Methods)), Internal_(Internal) {}
static bool ParseBindings(const std::string & Request, const std::list<const char *> & EndPoints, BindingMap &Keys);
void PrintBindings();
@@ -111,10 +111,8 @@ namespace uCentral {
bool IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response);
bool IsAuthorized(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response, std::string &UserName);
bool ValidateAPIKey(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response);
/* bool ValidateAPIKey(Poco::Net::HTTPServerRequest &Request,
Poco::Net::HTTPServerResponse &Response); */
uint64_t GetParameter(const std::string &Name, uint64_t Default);
std::string GetParameter(const std::string &Name, const std::string &Default);
@@ -157,6 +155,7 @@ namespace uCentral {
SecurityObjects::UserInfoAndPolicy UserInfo_;
std::vector<std::string> Methods_;
QueryBlock QB_;
bool Internal_=false;
};
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
@@ -183,10 +182,10 @@ namespace uCentral {
}
template<typename T, typename... Args>
RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger) {
RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger ) {
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
return new T(Bindings, Logger);
return new T(Bindings, Logger, false);
}
if constexpr (sizeof...(Args) == 0) {
@@ -196,6 +195,21 @@ namespace uCentral {
}
}
template<typename T, typename... Args>
RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings, Poco::Logger & Logger) {
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
return new T(Bindings, Logger, true);
}
if constexpr (sizeof...(Args) == 0) {
return new RESTAPI_UnknownRequestHandler(Bindings,Logger);
} else {
return RESTAPI_Router_I<Args...>(RequestedPath, Bindings, Logger);
}
}
}
#endif //UCENTRAL_RESTAPI_HANDLER_H

View File

@@ -14,11 +14,12 @@
namespace uCentral {
class RESTAPI_oauth2Handler : public RESTAPIHandler {
public:
RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_oauth2Handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_DELETE,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/oauth2/{token}","/api/v1/oauth2"}; };

View File

@@ -9,10 +9,11 @@
namespace uCentral {
class RESTAPI_systemEndpoints_handler : public RESTAPIHandler {
public:
RESTAPI_systemEndpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_systemEndpoints_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/systemEndpoints"}; };

View File

@@ -14,10 +14,11 @@
namespace uCentral {
class RESTAPI_system_command : public RESTAPIHandler {
public:
RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &request,
Poco::Net::HTTPServerResponse &response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/system"}; };

View File

@@ -10,14 +10,15 @@
namespace uCentral {
class RESTAPI_user_handler : public RESTAPIHandler {
public:
RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_user_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, 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}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/user/{id}"}; };
void DoGet(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response);

View File

@@ -10,11 +10,12 @@
namespace uCentral {
class RESTAPI_users_handler : public RESTAPIHandler {
public:
RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_users_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {}
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {}
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/users"}; };
void DoGet(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response);

View File

@@ -14,7 +14,7 @@ namespace uCentral {
if (!ContinueProcessing(Request, Response))
return;
if (!Daemon()->IsValidAPIKEY(Request))
if (!IsAuthorized(Request, Response))
return;
try {

View File

@@ -10,11 +10,12 @@
namespace uCentral {
class RESTAPI_validateToken_handler : public RESTAPIHandler {
public:
RESTAPI_validateToken_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L)
RESTAPI_validateToken_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, bool Internal)
: RESTAPIHandler(bindings, L,
std::vector<std::string>
{Poco::Net::HTTPRequest::HTTP_GET,
Poco::Net::HTTPRequest::HTTP_OPTIONS}) {};
Poco::Net::HTTPRequest::HTTP_OPTIONS},
Internal) {};
void handleRequest(Poco::Net::HTTPServerRequest &Request, Poco::Net::HTTPServerResponse &Response) override;
static const std::list<const char *> PathName() { return std::list<const char *>{"/api/v1/validateToken"}; };

View File

@@ -471,6 +471,17 @@ namespace uCentral::Utils {
return Result;
}
std::string SecondsToNiceText(uint64_t Seconds) {
std::string Result;
int Days = Seconds / (24*60*60);
Seconds -= Days * (24*60*60);
int Hours= Seconds / (60*60);
Seconds -= Hours * (60*60);
int Minutes = Seconds / 60;
Seconds -= Minutes * 60;
Result = std::to_string(Days) +" days, " + std::to_string(Hours) + ":" + std::to_string(Minutes) + ":" + std::to_string(Seconds);
return Result;
}
}

View File

@@ -68,5 +68,7 @@ namespace uCentral::Utils {
[[nodiscard]] MediaTypeEncoding FindMediaType(const Poco::File &F);
[[nodiscard]] std::string BinaryFileToHexString( const Poco::File &F);
[[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds);
}
#endif // UCENTRALGW_UTILS_H

View File

@@ -7,7 +7,7 @@
form {border: 3px solid #f1f1f1;}
input[type=text], input[type=password] {
width: 100%;
width: 90%;
padding: 12px 20px;
margin: 8px 0;
display: inline-block;
@@ -22,7 +22,8 @@
margin: 8px 0;
border: none;
cursor: pointer;
width: 100%;
width: 40%;
font-size: medium;
}
button:hover {
@@ -31,7 +32,14 @@
.imgcontainer {
text-align: center;
margin: 24px 0 12px 0;
margin: 5px 0 5px 0;
grid-column-start: 2;
grid-column-end: 2;
}
.passwordlabel {
grid-column-start: 2;
grid-column-end: 2;
}
img.avatar {
@@ -39,8 +47,39 @@
border-radius: 50%;
}
.container {
padding: 16px;
.grid-container {
display: grid;
grid-template-columns: 15% 70% 15%;
background-color: #f3db21;
grid-column-gap: 5px;
padding: 10px;
}
.grid-container > div {
background-color: rgba(255, 255, 255, 0.8);
text-align: center;
padding: 20px 0;
font-size: 30px;
}
.passwordtext {
float: left;
margin-left: 5%;
}
.rulestext {
display: inline-block;
text-align: left;
text-justify: none;
margin: 5px 0 5px 0;
grid-column-start: 2;
grid-column-end: 2;
}
ul {
display: inline-block;
text-align: left;
font-size: small;
}
span.password1 {
@@ -63,33 +102,35 @@
<body>
<form action="/api/v1/actionLink?action=password_reset" method="post" onsubmit="return validatePassword()">
<input type="hidden" id="custId" name="id" value="${UUID}">
<div class="grid-container">
<div class="imgcontainer">
<img src="/wwwassets/open-wifi.svg" alt="Avatar" class="avatar">
</div>
<div>
<h1>Password Reset</h1>
</div>
<div class="container">
<label for="password1"><b>New Password</b></label>
<div class="imgcontainer">
<img src="/wwwassets/open-wifi.svg" alt="Logo" class="logo">
</div>
<div class="passwordlabel">
<label class="passwordtext" for="password1" ><b>New Password</b></label>
<input id="password1" type="password" placeholder="New Password" name="password1" pattern="${PASSWORD_VALIDATION}" required>
<label for="password2"><b>Retype Password</b></label>
<input id="password2" type="password" placeholder="New Password" name="password2" pattern="${PASSWORD_VALIDATION}" required>
<input type="hidden" id="custId" name="id" value="${UUID}">
</div>
<div class="passwordlabel">
<label class="passwordtext" for="password2"><b>Retype Password</b></label>
<input id="password2" type="password" placeholder="Retype Password" name="password2" pattern="${PASSWORD_VALIDATION}" required>
</div>
<div class="passwordlabel">
<button type="submit">Reset Password</button>
</div>
<div>
<p>Password rules:</p>
<div class="rulestext">
<ul>
<li>Must be at least 8 characters long.</li>
<li>Must be at least 8 characters long</li>
<li>Must contain 1 uppercase letter</li>
<li>Must contain 1 lowercase letter</li>
<li>Must contain 1 digit</li>
<li>Must contain 1 special character</li>
</ul>
</div>
</div>
</form>
<script>