mirror of
https://github.com/Telecominfraproject/wlan-cloud-ucentralsec.git
synced 2025-10-29 18:02:29 +00:00
Signed-off-by: stephb9959 <stephane.bourque@gmail.com>
This commit is contained in:
@@ -175,7 +175,7 @@ add_executable( owsec
|
||||
src/TotpCache.h
|
||||
src/RESTAPI/RESTAPI_subtotp_handler.cpp src/RESTAPI/RESTAPI_subtotp_handler.h
|
||||
src/RESTAPI/RESTAPI_signup_handler.cpp src/RESTAPI/RESTAPI_signup_handler.h
|
||||
src/MessagingTemplates.cpp src/MessagingTemplates.h)
|
||||
src/MessagingTemplates.cpp src/MessagingTemplates.h src/RESTAPI/RESTAPI_apiKey_handler.cpp src/RESTAPI/RESTAPI_apiKey_handler.h src/storage/orm_apikeys.cpp src/storage/orm_apikeys.h src/RESTAPI/RESTAPI_validate_apikey.cpp src/RESTAPI/RESTAPI_validate_apikey.h)
|
||||
|
||||
if(NOT SMALL_BUILD)
|
||||
target_link_libraries(owsec PUBLIC
|
||||
|
||||
@@ -17,6 +17,7 @@ servers:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
- ApiKeyAuth: []
|
||||
- ApiToken: []
|
||||
|
||||
components:
|
||||
securitySchemes:
|
||||
@@ -28,6 +29,10 @@ components:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
ApiToken:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: X-API-TOKEN
|
||||
|
||||
responses:
|
||||
NotFound:
|
||||
@@ -164,18 +169,61 @@ components:
|
||||
aclTemplate:
|
||||
$ref: '#/components/schemas/AclTemplate'
|
||||
|
||||
ApiKeyCreationRequest:
|
||||
ApiKeyAccessRight:
|
||||
type: object
|
||||
properties:
|
||||
service:
|
||||
type: string
|
||||
access:
|
||||
type: string
|
||||
enum:
|
||||
- read
|
||||
- modify
|
||||
- create
|
||||
- delete
|
||||
- noaccess
|
||||
|
||||
ApiKeyAccessRightList:
|
||||
type: object
|
||||
properties:
|
||||
acls:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ApiKeyAccessRight'
|
||||
|
||||
ApiKeyEntry:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
format: uuid
|
||||
userUuid:
|
||||
type: string
|
||||
format: uuid
|
||||
name:
|
||||
type: string
|
||||
description:
|
||||
type: string
|
||||
apiKey:
|
||||
type: string
|
||||
salt:
|
||||
type: string
|
||||
expiresOn:
|
||||
type: integer
|
||||
format: int64
|
||||
lastUse:
|
||||
type: integer
|
||||
format: int64
|
||||
rights:
|
||||
$ref: '#/components/schemas/AclTemplate'
|
||||
$ref: '#/components/schemas/ApiKeyAccessRightList'
|
||||
|
||||
ApiKeyEntryList:
|
||||
type: object
|
||||
properties:
|
||||
apiKeys:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ApiKeyEntry'
|
||||
|
||||
ApiKeyCreationAnswer:
|
||||
type: object
|
||||
@@ -194,7 +242,7 @@ components:
|
||||
apiKey:
|
||||
type: string
|
||||
rights:
|
||||
$ref: '#/components/schemas/AclTemplate'
|
||||
$ref: '#/components/schemas/ApiKeyAccessRights'
|
||||
|
||||
AclTemplate:
|
||||
type: object
|
||||
@@ -1634,7 +1682,103 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
|
||||
/apiKey/{uuid}:
|
||||
get:
|
||||
tags:
|
||||
- API Tokens
|
||||
summary: Retrieve all the APIKeys for a given user UUID
|
||||
operationId: getApiKeyList
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/ApiKeyEntryList'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
delete:
|
||||
tags:
|
||||
- API Tokens
|
||||
summary: Retrieve all the APIKeys for a given user UUID
|
||||
operationId: deleteApiKey
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
- in: query
|
||||
name: keyUuid
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/responses/Success'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
post:
|
||||
tags:
|
||||
- API Tokens
|
||||
summary: Retrieve all the APIKeys for a given user UUID
|
||||
operationId: createApiKey
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiKeyEntry'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/ApiKeyEntry'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
put:
|
||||
tags:
|
||||
- API Tokens
|
||||
summary: Retrieve all the APIKeys for a given user UUID
|
||||
operationId: modifyApiKey
|
||||
parameters:
|
||||
- in: path
|
||||
name: uuid
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
required: true
|
||||
- in: query
|
||||
name: name
|
||||
schema:
|
||||
type: string
|
||||
required: true
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ApiKeyEntry'
|
||||
responses:
|
||||
200:
|
||||
$ref: '#/components/schemas/ApiKeyEntry'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
#########################################################################################
|
||||
##
|
||||
@@ -1732,6 +1876,26 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/validateApiKey:
|
||||
get:
|
||||
tags:
|
||||
- Security
|
||||
summary: Allows an application to validate an API Key.
|
||||
operationId: validateApiKey
|
||||
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:
|
||||
|
||||
@@ -751,9 +751,7 @@ namespace OpenWifi {
|
||||
WebToken = WT;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// return IsValidSubToken(Token, WebToken, UserInfo, Expired);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -772,7 +770,27 @@ namespace OpenWifi {
|
||||
WebToken = WT;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthService::IsValidApiKey(const std::string &ApiKey, SecurityObjects::WebToken &WebToken,
|
||||
SecurityObjects::UserInfo &UserInfo, bool &Expired, std::uint64_t &expiresOn) {
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
std::string UserId;
|
||||
SecurityObjects::WebToken WT;
|
||||
SecurityObjects::ApiKeyEntry ApiKeyEntry;
|
||||
if(StorageService()->ApiKeyDB().GetRecord("apiKey", ApiKey, ApiKeyEntry)) {
|
||||
expiresOn = ApiKeyEntry.expiresOn;
|
||||
Expired = ApiKeyEntry.expiresOn < Utils::Now();
|
||||
if(Expired)
|
||||
return false;
|
||||
if(StorageService()->UserDB().GetUserById(ApiKeyEntry.userUuid,UserInfo)) {
|
||||
WebToken = WT;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace OpenWifi{
|
||||
[[nodiscard]] std::string GenerateTokenJWT(const std::string & UserName, ACCESS_TYPE Type);
|
||||
[[nodiscard]] std::string GenerateTokenHMAC(const std::string & UserName, ACCESS_TYPE Type);
|
||||
|
||||
[[nodiscard]] bool IsValidApiKey(const std::string &ApiKey, SecurityObjects::WebToken &WebToken, SecurityObjects::UserInfo &UserInfo, bool & Expired, std::uint64_t & expiresOn);
|
||||
[[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);
|
||||
|
||||
159
src/RESTAPI/RESTAPI_apiKey_handler.cpp
Normal file
159
src/RESTAPI/RESTAPI_apiKey_handler.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-04.
|
||||
//
|
||||
|
||||
#include "RESTAPI_apiKey_handler.h"
|
||||
#include "RESTAPI/RESTAPI_db_helpers.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void RESTAPI_apiKey_handler::DoGet() {
|
||||
std::string user_uuid = GetBinding("uuid","");
|
||||
if(user_uuid.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
SecurityObjects::ApiKeyEntryList List;
|
||||
if(DB_.GetRecords(0,500, List.apiKeys, fmt::format(" userUuid='{}' ", user_uuid), " name ")) {
|
||||
Poco::JSON::Object Answer;
|
||||
List.to_json(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
void RESTAPI_apiKey_handler::DoDelete() {
|
||||
std::string user_uuid = GetBinding("uuid","");
|
||||
if(user_uuid.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if(user_uuid!=UserInfo_.userinfo.id) {
|
||||
if(!StorageService()->UserDB().Exists("id",user_uuid)) {
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
std::string ApiKeyId= GetParameter("keyUuid","");
|
||||
if(ApiKeyId.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
SecurityObjects::ApiKeyEntry ApiKey;
|
||||
if(StorageService()->ApiKeyDB().GetRecord("id",ApiKeyId,ApiKey)) {
|
||||
if(ApiKey.userUuid==user_uuid) {
|
||||
AuthService()->RemoveTokenSystemWide(ApiKey.apiKey);
|
||||
DB_.DeleteRecord("id", ApiKeyId);
|
||||
return OK();
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
void RESTAPI_apiKey_handler::DoPost() {
|
||||
std::string user_uuid = GetBinding("uuid","");
|
||||
|
||||
if(user_uuid.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
if(user_uuid!=UserInfo_.userinfo.id) {
|
||||
// Must verify if the user exists
|
||||
if(!StorageService()->UserDB().Exists("id",user_uuid)) {
|
||||
return BadRequest(RESTAPI::Errors::UserMustExist);
|
||||
}
|
||||
}
|
||||
|
||||
SecurityObjects::ApiKeyEntry NewKey;
|
||||
if(!NewKey.from_json(ParsedBody_)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
NewKey.lastUse = 0 ;
|
||||
|
||||
if(!Utils::IsAlphaNumeric(NewKey.name) || NewKey.name.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
Poco::toLowerInPlace(NewKey.name);
|
||||
NewKey.userUuid = user_uuid;
|
||||
if(NewKey.expiresOn < Utils::Now()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
// does a key of that name already exit for this user?
|
||||
SecurityObjects::ApiKeyEntryList ExistingList;
|
||||
if(DB_.GetRecords(0,500, ExistingList.apiKeys, fmt::format(" userUuid='{}' ", user_uuid))) {
|
||||
if(std::find_if(ExistingList.apiKeys.begin(),ExistingList.apiKeys.end(), [NewKey](const SecurityObjects::ApiKeyEntry &E) -> bool {
|
||||
return E.name==NewKey.name;
|
||||
})!=ExistingList.apiKeys.end()) {
|
||||
return BadRequest(RESTAPI::Errors::ApiKeyNameAlreadyExists);
|
||||
}
|
||||
}
|
||||
|
||||
NewKey.id = MicroServiceCreateUUID();
|
||||
NewKey.userUuid = user_uuid;
|
||||
NewKey.salt = std::to_string(Utils::Now());
|
||||
NewKey.apiKey = Utils::ComputeHash(NewKey.salt, UserInfo_.userinfo.id, UserInfo_.webtoken.access_token_ );
|
||||
NewKey.created = Utils::Now();
|
||||
|
||||
if(DB_.CreateRecord(NewKey)) {
|
||||
Poco::JSON::Object Answer;
|
||||
NewKey.to_json(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
return BadRequest(RESTAPI::Errors::RecordNotCreated);
|
||||
}
|
||||
|
||||
void RESTAPI_apiKey_handler::DoPut() {
|
||||
std::string user_uuid = GetBinding("uuid","");
|
||||
if(user_uuid.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
if(user_uuid!=UserInfo_.userinfo.id && UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
SecurityObjects::ApiKeyEntry NewKey;
|
||||
if(!NewKey.from_json(ParsedBody_)) {
|
||||
return BadRequest(RESTAPI::Errors::InvalidJSONDocument);
|
||||
}
|
||||
|
||||
SecurityObjects::ApiKeyEntry ExistingKey;
|
||||
if(!DB_.GetRecord("id",NewKey.id,ExistingKey)) {
|
||||
return BadRequest(RESTAPI::Errors::ApiKeyDoesNotExist);
|
||||
}
|
||||
|
||||
if(ExistingKey.userUuid!=user_uuid) {
|
||||
return BadRequest(RESTAPI::Errors::MissingUserID);
|
||||
}
|
||||
|
||||
// You can only change the description and the expiration
|
||||
/* if(ParsedBody_->has("expiresOn")) {
|
||||
if(NewKey.expiresOn < Utils::Now()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
ExistingKey.expiresOn = NewKey.expiresOn;
|
||||
}
|
||||
*/
|
||||
AssignIfPresent(ParsedBody_,"description",ExistingKey.description);
|
||||
|
||||
if(DB_.UpdateRecord("id",ExistingKey.id,ExistingKey)) {
|
||||
Poco::JSON::Object Answer;
|
||||
ExistingKey.to_json(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::RecordNotUpdated);
|
||||
}
|
||||
|
||||
}
|
||||
34
src/RESTAPI/RESTAPI_apiKey_handler.h
Normal file
34
src/RESTAPI/RESTAPI_apiKey_handler.h
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-04.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_apiKey_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_apiKey_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_PUT,
|
||||
Poco::Net::HTTPRequest::HTTP_POST,
|
||||
Poco::Net::HTTPRequest::HTTP_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/apiKey/{uuid}"}; };
|
||||
private:
|
||||
ApiKeyDB &DB_=StorageService()->ApiKeyDB();
|
||||
|
||||
void DoGet() final;
|
||||
void DoPut() final;
|
||||
void DoPost() final;
|
||||
void DoDelete() final;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "RESTAPI/RESTAPI_totp_handler.h"
|
||||
#include "RESTAPI/RESTAPI_subtotp_handler.h"
|
||||
#include "RESTAPI/RESTAPI_signup_handler.h"
|
||||
#include "RESTAPI/RESTAPI_apiKey_handler.h"
|
||||
|
||||
#include "framework/RESTAPI_SystemCommand.h"
|
||||
#include "framework/RESTAPI_WebSocketServer.h"
|
||||
|
||||
@@ -54,7 +56,8 @@ namespace OpenWifi {
|
||||
RESTAPI_signup_handler,
|
||||
RESTAPI_validate_sub_token_handler,
|
||||
RESTAPI_validate_token_handler,
|
||||
RESTAPI_webSocketServer
|
||||
RESTAPI_webSocketServer,
|
||||
RESTAPI_apiKey_handler
|
||||
>(Path, Bindings, L, S,TransactionId);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace OpenWifi {
|
||||
StorageService()->AvatarDB().DeleteAvatar(UserInfo_.userinfo.email,Id);
|
||||
StorageService()->PreferencesDB().DeletePreferences(UserInfo_.userinfo.email,Id);
|
||||
StorageService()->UserTokenDB().RevokeAllTokens(Id);
|
||||
StorageService()->ApiKeyDB().RemoveAllApiKeys(Id);
|
||||
Logger_.information(fmt::format("User '{}' deleted by '{}'.",Id,UserInfo_.userinfo.email));
|
||||
OK();
|
||||
}
|
||||
|
||||
31
src/RESTAPI/RESTAPI_validate_apikey.cpp
Normal file
31
src/RESTAPI/RESTAPI_validate_apikey.cpp
Normal file
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-07.
|
||||
//
|
||||
|
||||
#include "RESTAPI_validate_apikey.h"
|
||||
#include "AuthService.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void RESTAPI_validate_apikey::DoGet() {
|
||||
Poco::URI URI(Request->getURI());
|
||||
auto Parameters = URI.getQueryParameters();
|
||||
for(auto const &i:Parameters) {
|
||||
if (i.first == "apikey") {
|
||||
// can we find this token?
|
||||
SecurityObjects::UserInfoAndPolicy SecObj;
|
||||
bool Expired = false;
|
||||
std::uint64_t expiresOn=0;
|
||||
if (AuthService()->IsValidApiKey(i.second, SecObj.webtoken, SecObj.userinfo, Expired, expiresOn)) {
|
||||
Poco::JSON::Object Answer;
|
||||
SecObj.to_json(Answer);
|
||||
Answer.set("expiresOn", expiresOn);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
}
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
} // OpenWifi
|
||||
27
src/RESTAPI/RESTAPI_validate_apikey.h
Normal file
27
src/RESTAPI/RESTAPI_validate_apikey.h
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-07.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_validate_apikey : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_validate_apikey(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>
|
||||
{Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {};
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/validateApiKey"}; };
|
||||
void DoGet() final;
|
||||
void DoPost() final {};
|
||||
void DoDelete() final {};
|
||||
void DoPut() final {};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -619,5 +619,80 @@ namespace OpenWifi::SecurityObjects {
|
||||
field_to_json(Obj,"login",login);
|
||||
field_to_json(Obj,"logout",logout);
|
||||
}
|
||||
|
||||
void ApiKeyAccessRight::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "service", service);
|
||||
field_to_json(Obj, "access", access);
|
||||
}
|
||||
|
||||
bool ApiKeyAccessRight::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "service", service);
|
||||
field_from_json(Obj, "access", access);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyAccessRightList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "acls", acls);
|
||||
}
|
||||
|
||||
bool ApiKeyAccessRightList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "acls", acls);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "id", id);
|
||||
field_to_json(Obj, "userUuid", userUuid);
|
||||
field_to_json(Obj, "name", name);
|
||||
field_to_json(Obj, "apiKey", apiKey);
|
||||
field_to_json(Obj, "salt", salt);
|
||||
field_to_json(Obj, "description", description);
|
||||
field_to_json(Obj, "expiresOn", expiresOn);
|
||||
field_to_json(Obj, "rights", rights);
|
||||
field_to_json(Obj, "lastUse", lastUse);
|
||||
}
|
||||
|
||||
bool ApiKeyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "id", id);
|
||||
field_from_json(Obj, "userUuid", userUuid);
|
||||
field_from_json(Obj, "name", name);
|
||||
field_from_json(Obj, "apiKey", apiKey);
|
||||
field_from_json(Obj, "salt", salt);
|
||||
field_from_json(Obj, "description", description);
|
||||
field_from_json(Obj, "expiresOn", expiresOn);
|
||||
field_from_json(Obj, "rights", rights);
|
||||
field_from_json(Obj, "lastUse", lastUse);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyEntryList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "apiKeys", apiKeys);
|
||||
}
|
||||
|
||||
bool ApiKeyEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "apiKeys", apiKeys);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -325,5 +325,44 @@ namespace OpenWifi {
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct ApiKeyAccessRight {
|
||||
std::string service;
|
||||
std::string access;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyAccessRightList {
|
||||
std::vector<ApiKeyAccessRight> acls;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyEntry {
|
||||
Types::UUID_t id;
|
||||
Types::UUID_t userUuid;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string apiKey;
|
||||
std::string salt;
|
||||
std::uint64_t created;
|
||||
std::uint64_t expiresOn=0;
|
||||
ApiKeyAccessRightList rights;
|
||||
std::uint64_t lastUse=0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyEntryList {
|
||||
std::vector<ApiKeyEntry> apiKeys;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace OpenWifi {
|
||||
SubAvatarDB_ = std::make_unique<OpenWifi::AvatarDB>("SubAvatars", "avs", dbType_,*Pool_, Logger());
|
||||
LoginDB_ = std::make_unique<OpenWifi::LoginDB>("Logins", "lin", dbType_,*Pool_, Logger());
|
||||
SubLoginDB_ = std::make_unique<OpenWifi::LoginDB>("SubLogins", "lis", dbType_,*Pool_, Logger());
|
||||
ApiKeyDB_ = std::make_unique<OpenWifi::ApiKeyDB>("ApiKeys", "api", dbType_,*Pool_, Logger());
|
||||
|
||||
UserDB_->Create();
|
||||
SubDB_->Create();
|
||||
@@ -47,6 +48,7 @@ namespace OpenWifi {
|
||||
AvatarDB_->Create();
|
||||
SubAvatarDB_->Create();
|
||||
LoginDB_->Create();
|
||||
ApiKeyDB_->Create();
|
||||
SubLoginDB_->Create();
|
||||
|
||||
OpenWifi::SpecialUserHelpers::InitializeDefaultUser();
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#ifndef UCENTRAL_USTORAGESERVICE_H
|
||||
#define UCENTRAL_USTORAGESERVICE_H
|
||||
#pragma once
|
||||
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
#include "framework/StorageClass.h"
|
||||
@@ -21,6 +20,7 @@
|
||||
#include "storage/orm_actionLinks.h"
|
||||
#include "storage/orm_avatar.h"
|
||||
#include "storage/orm_logins.h"
|
||||
#include "storage/orm_apikeys.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace OpenWifi {
|
||||
OpenWifi::AvatarDB & SubAvatarDB() { return *SubAvatarDB_; }
|
||||
OpenWifi::LoginDB & LoginDB() { return *LoginDB_; }
|
||||
OpenWifi::LoginDB & SubLoginDB() { return *SubLoginDB_; }
|
||||
OpenWifi::ApiKeyDB & ApiKeyDB() { return *ApiKeyDB_; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -66,6 +67,7 @@ namespace OpenWifi {
|
||||
std::unique_ptr<OpenWifi::AvatarDB> SubAvatarDB_;
|
||||
std::unique_ptr<OpenWifi::LoginDB> LoginDB_;
|
||||
std::unique_ptr<OpenWifi::LoginDB> SubLoginDB_;
|
||||
std::unique_ptr<OpenWifi::ApiKeyDB> ApiKeyDB_;
|
||||
|
||||
std::unique_ptr<OpenWifi::UserCache> UserCache_;
|
||||
std::unique_ptr<OpenWifi::UserCache> SubCache_;
|
||||
@@ -80,5 +82,3 @@ namespace OpenWifi {
|
||||
inline auto StorageService() { return StorageService::instance(); };
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif //UCENTRAL_USTORAGESERVICE_H
|
||||
|
||||
@@ -68,4 +68,54 @@ namespace OpenWifi {
|
||||
return RetrieveTokenInformation(SessionToken, UInfo, TID, Expired, Contacted, Sub);
|
||||
}
|
||||
|
||||
bool AuthClient::RetrieveApiKeyInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted) {
|
||||
try {
|
||||
Types::StringPairVec QueryData;
|
||||
QueryData.push_back(std::make_pair("apikey",SessionToken));
|
||||
OpenAPIRequestGet Req( uSERVICE_SECURITY,
|
||||
"/api/v1/validateApiKey" ,
|
||||
QueryData,
|
||||
10000);
|
||||
Poco::JSON::Object::Ptr Response;
|
||||
|
||||
auto StatusCode = Req.Do(Response);
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) {
|
||||
Contacted = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Contacted = true;
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) {
|
||||
if(Response->has("tokenInfo") && Response->has("userInfo") && Response->has("expiresOn")) {
|
||||
UInfo.from_json(Response);
|
||||
Expired = false;
|
||||
|
||||
ApiKeyCache_.update(SessionToken, ApiKeyCacheEntry{ .UserInfo = UInfo, .ExpiresOn = Response->get("expiresOn")});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
poco_error(Logger(),fmt::format("Failed to retrieve api key={} for TID={}", SessionToken, TID));
|
||||
}
|
||||
Expired = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthClient::IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy &UInfo,
|
||||
std::uint64_t TID, bool &Expired, bool &Contacted) {
|
||||
auto User = ApiKeyCache_.get(SessionToken);
|
||||
if (!User.isNull()) {
|
||||
if(User->ExpiresOn < Utils::Now())
|
||||
Expired = false;
|
||||
UInfo = User->UserInfo;
|
||||
return true;
|
||||
}
|
||||
return RetrieveApiKeyInformation(SessionToken, UInfo, TID, Expired, Contacted);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -12,6 +12,7 @@
|
||||
namespace OpenWifi {
|
||||
|
||||
class AuthClient : public SubSystemServer {
|
||||
|
||||
public:
|
||||
explicit AuthClient() noexcept:
|
||||
SubSystemServer("Authentication", "AUTH-CLNT", "authentication")
|
||||
@@ -23,7 +24,12 @@ namespace OpenWifi {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline int Start() override {
|
||||
struct ApiKeyCacheEntry {
|
||||
OpenWifi::SecurityObjects::UserInfoAndPolicy UserInfo;
|
||||
std::uint64_t ExpiresOn;
|
||||
};
|
||||
|
||||
inline int Start() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -36,6 +42,7 @@ namespace OpenWifi {
|
||||
|
||||
inline void RemovedCachedToken(const std::string &Token) {
|
||||
Cache_.remove(Token);
|
||||
ApiKeyCache_.remove(Token);
|
||||
}
|
||||
|
||||
inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) {
|
||||
@@ -46,12 +53,24 @@ namespace OpenWifi {
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub=false);
|
||||
|
||||
bool RetrieveApiKeyInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted);
|
||||
|
||||
bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub = false);
|
||||
|
||||
bool IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted);
|
||||
|
||||
private:
|
||||
|
||||
Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy> Cache_{512,1200000 };
|
||||
Poco::ExpireLRUCache<std::string,ApiKeyCacheEntry> ApiKeyCache_{512,1200000 };
|
||||
};
|
||||
|
||||
inline auto AuthClient() { return AuthClient::instance(); }
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#include "framework/AuthClient.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
#if defined(TIP_SECURITY_SERVICE)
|
||||
#include "AuthService.h"
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -640,7 +644,8 @@ namespace OpenWifi {
|
||||
};
|
||||
|
||||
#ifdef TIP_SECURITY_SERVICE
|
||||
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub );
|
||||
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub );
|
||||
#endif
|
||||
inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) {
|
||||
if(Internal_ && Request->has("X-INTERNAL-NAME")) {
|
||||
@@ -665,7 +670,36 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
return Allowed;
|
||||
} else {
|
||||
} else if(!Internal_ && Request->has("X-API-KEY")) {
|
||||
SessionToken_ = Request->get("X-API-KEY", "");
|
||||
std::uint64_t expiresOn;
|
||||
#ifdef TIP_SECURITY_SERVICE
|
||||
if (AuthService()->IsValidApiKey(SessionToken_, UserInfo_.webtoken, UserInfo_.userinfo, Expired, expiresOn)) {
|
||||
#else
|
||||
if (AuthClient()->IsValidApiKey( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted, Sub)) {
|
||||
#endif
|
||||
REST_Requester_ = UserInfo_.userinfo.email;
|
||||
if(Server_.LogIt(Request->getMethod(),true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): APIKEY-ACCESS TID={} User='{}@{}' Method={} Path={}",
|
||||
UserInfo_.userinfo.email,
|
||||
TransactionId_,
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
Request->clientAddress().toString(),
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if(Server_.LogBadTokens(true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}",
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
TransactionId_,
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (SessionToken_.empty()) {
|
||||
try {
|
||||
Poco::Net::OAuth20Credentials Auth(*Request);
|
||||
|
||||
@@ -222,6 +222,12 @@ namespace OpenWifi::RESTAPI::Errors {
|
||||
|
||||
static const struct msg DeviceRequiresSignature{1146,"Device requires device signature to be provided."};
|
||||
|
||||
static const struct msg ApiKeyNameAlreadyExists{1147,"API Key name must be unique."};
|
||||
static const struct msg TooManyApiKeys{1148,"Too many API Keys have already been created."};
|
||||
static const struct msg UserMustExist{1149,"User must exist."};
|
||||
static const struct msg ApiKeyNameDoesNotExist{1150,"API Key name does not exist."};
|
||||
static const struct msg ApiKeyDoesNotExist{1150,"API Key does not exist."};
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -520,4 +520,8 @@ bool ExtractBase64CompressedData(const std::string &CompressedData,
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsAlphaNumeric(const std::string &s) {
|
||||
return std::all_of(s.begin(),s.end(),[](char c) -> bool { return isalnum(c); });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace OpenWifi::Utils {
|
||||
[[nodiscard]] std::string BinaryFileToHexString(const Poco::File &F);
|
||||
[[nodiscard]] std::string SecondsToNiceText(uint64_t Seconds);
|
||||
[[nodiscard]] bool wgets(const std::string &URL, std::string &Response);
|
||||
|
||||
[[nodiscard]] bool IsAlphaNumeric(const std::string &s);
|
||||
template< typename T >
|
||||
std::string int_to_hex( T i )
|
||||
{
|
||||
|
||||
101
src/storage/orm_apikeys.cpp
Normal file
101
src/storage/orm_apikeys.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-04.
|
||||
//
|
||||
|
||||
#include "orm_apikeys.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
#include "framework/orm.h"
|
||||
#include "AuthService.h"
|
||||
#include "StorageService.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
static ORM::FieldVec ApiKeyDB_Fields{
|
||||
ORM::Field{"id", 36, true},
|
||||
ORM::Field{"userUuid", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"name", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"description", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"apiKey", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"salt", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"created", ORM::FieldType::FT_BIGINT},
|
||||
ORM::Field{"expiresOn", ORM::FieldType::FT_BIGINT},
|
||||
ORM::Field{"rights", ORM::FieldType::FT_TEXT},
|
||||
ORM::Field{"lastUse", ORM::FieldType::FT_BIGINT}
|
||||
};
|
||||
|
||||
static ORM::IndexVec MakeIndices(const std::string & shortname) {
|
||||
return ORM::IndexVec{
|
||||
{std::string(shortname + "_username_index"),
|
||||
ORM::IndexEntryVec{
|
||||
{
|
||||
std::string("userUuid"),
|
||||
ORM::Indextype::ASC }}},
|
||||
{std::string(shortname + "_apikey_index"),
|
||||
ORM::IndexEntryVec{
|
||||
{
|
||||
std::string("apiKey"),
|
||||
ORM::Indextype::ASC}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
ApiKeyDB::ApiKeyDB( const std::string &TableName, const std::string &Shortname ,OpenWifi::DBType T,
|
||||
Poco::Data::SessionPool &P, Poco::Logger &L) :
|
||||
DB(T, TableName.c_str(), ApiKeyDB_Fields, MakeIndices(Shortname), P, L, Shortname.c_str()) {
|
||||
}
|
||||
|
||||
bool ApiKeyDB::RemoveAllApiKeys(const std::string & user_uuid) {
|
||||
SecurityObjects::ApiKeyEntryList Keys;
|
||||
if(StorageService()->ApiKeyDB().GetRecords(0,500,Keys.apiKeys,fmt::format(" userUuid='{} ", user_uuid))) {
|
||||
for(const auto &key:Keys.apiKeys) {
|
||||
AuthService()->RemoveTokenSystemWide(key.apiKey);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApiKeyDB::Upgrade([[maybe_unused]] uint32_t from, uint32_t &to) {
|
||||
to = Version();
|
||||
std::vector<std::string> Script{
|
||||
};
|
||||
|
||||
for(const auto &i:Script) {
|
||||
try {
|
||||
auto Session = Pool_.get();
|
||||
Session << i , Poco::Data::Keywords::now;
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // OpenWifi
|
||||
|
||||
template<> void ORM::DB<OpenWifi::ApiKeyRecordTuple, OpenWifi::SecurityObjects::ApiKeyEntry>::Convert(const OpenWifi::ApiKeyRecordTuple &In, OpenWifi::SecurityObjects::ApiKeyEntry &Out) {
|
||||
Out.id = In.get<0>();
|
||||
Out.userUuid = In.get<1>();
|
||||
Out.name = In.get<2>();
|
||||
Out.description = In.get<3>();
|
||||
Out.apiKey = In.get<4>();
|
||||
Out.salt = In.get<5>();
|
||||
Out.created = In.get<6>();
|
||||
Out.expiresOn = In.get<7>();
|
||||
Out.rights.acls = OpenWifi::RESTAPI_utils::to_object_array<OpenWifi::SecurityObjects::ApiKeyAccessRight>(In.get<8>());
|
||||
Out.lastUse = In.get<9>();
|
||||
}
|
||||
|
||||
template<> void ORM::DB<OpenWifi::ApiKeyRecordTuple, OpenWifi::SecurityObjects::ApiKeyEntry>::Convert(const OpenWifi::SecurityObjects::ApiKeyEntry &In, OpenWifi::ApiKeyRecordTuple &Out) {
|
||||
Out.set<0>(In.id);
|
||||
Out.set<1>(In.userUuid);
|
||||
Out.set<2>(In.name);
|
||||
Out.set<3>(In.description);
|
||||
Out.set<4>(In.apiKey);
|
||||
Out.set<5>(In.salt);
|
||||
Out.set<6>(In.created);
|
||||
Out.set<7>(In.expiresOn);
|
||||
Out.set<8>(OpenWifi::RESTAPI_utils::to_string(In.rights.acls));
|
||||
Out.set<9>(In.lastUse);
|
||||
}
|
||||
39
src/storage/orm_apikeys.h
Normal file
39
src/storage/orm_apikeys.h
Normal file
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-11-04.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/orm.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
typedef Poco::Tuple<
|
||||
std::string, // id
|
||||
std::string, // userUuid
|
||||
std::string, // name
|
||||
std::string, // description
|
||||
std::string, // apiKey
|
||||
std::string, // salt
|
||||
uint64_t, // created = 0;
|
||||
uint64_t, // expiresOn = 0;
|
||||
std::string, // rights
|
||||
std::uint64_t // lastUse
|
||||
> ApiKeyRecordTuple;
|
||||
typedef std::vector <ApiKeyRecordTuple> ApiKeyRecordTupleTupleList;
|
||||
|
||||
class ApiKeyDB : public ORM::DB<ApiKeyRecordTuple, SecurityObjects::ApiKeyEntry> {
|
||||
public:
|
||||
ApiKeyDB( const std::string &name, const std::string &shortname, OpenWifi::DBType T, Poco::Data::SessionPool & P, Poco::Logger &L);
|
||||
virtual ~ApiKeyDB() {}
|
||||
inline uint32_t Version() override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool Upgrade(uint32_t from, uint32_t &to) override;
|
||||
bool RemoveAllApiKeys(const std::string & user_uuid);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user