2019-10-25 20:16:43 +02:00
|
|
|
/*
|
|
|
|
|
* File: AppConfig.cpp
|
2019-10-28 14:12:50 +01:00
|
|
|
* Author: SET - nmset@yandex.com
|
2019-10-25 20:16:43 +02:00
|
|
|
* License : GPL v2
|
|
|
|
|
* Copyright SET - © 2019
|
|
|
|
|
*
|
|
|
|
|
* Created on 9 octobre 2019, 20:23
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "AppConfig.h"
|
|
|
|
|
#include "global.h"
|
|
|
|
|
#include <vector>
|
|
|
|
|
#include <fstream>
|
2020-11-11 21:36:08 +01:00
|
|
|
#include <mutex>
|
2019-10-25 20:16:43 +02:00
|
|
|
#include <Wt/WApplication.h>
|
|
|
|
|
#include <Wt/WEnvironment.h>
|
|
|
|
|
#include <Wt/WSslInfo.h>
|
|
|
|
|
#include <Wt/Json/Value.h>
|
|
|
|
|
#include <Wt/Json/Array.h>
|
|
|
|
|
#include <Wt/Json/Parser.h>
|
|
|
|
|
#include <Wt/Json/Serializer.h>
|
2020-11-11 21:36:08 +01:00
|
|
|
#include <algorithm>
|
2019-10-25 20:16:43 +02:00
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
2020-11-11 21:36:08 +01:00
|
|
|
mutex gs_fileWriteMutex;
|
|
|
|
|
|
2019-10-25 20:16:43 +02:00
|
|
|
// Hard coded. File must be in WT_APP_ROOT
|
|
|
|
|
#define JSON_CONFIG_FILE "k7config.json"
|
2020-11-11 21:36:08 +01:00
|
|
|
|
2019-10-25 20:16:43 +02:00
|
|
|
/*
|
|
|
|
|
{
|
|
|
|
|
"sCommonName" : {
|
|
|
|
|
"UserCommonName1" : {
|
|
|
|
|
"canImport" : true,
|
|
|
|
|
"canDelete" : true,
|
2020-10-25 17:28:47 +01:00
|
|
|
"canEditOwnerTrust" : true,
|
2020-11-03 11:06:25 +01:00
|
|
|
"canEditUidValidity" : true,
|
2020-11-07 22:17:44 +01:00
|
|
|
"canEditExpiryTime" : true,
|
2020-11-11 21:36:08 +01:00
|
|
|
"canCreateKeys" : true,
|
2020-11-19 19:16:17 +01:00
|
|
|
"canAddRevokeUids" : true,
|
2019-10-25 20:16:43 +02:00
|
|
|
"privKeyIds" : [
|
|
|
|
|
"fullKeyId1",
|
|
|
|
|
"fullKeyId2"
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
"UserCommonName2" : {
|
|
|
|
|
...
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
AppConfig::AppConfig(WText * notifyWidget)
|
|
|
|
|
{
|
|
|
|
|
m_notifyWidget = notifyWidget;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AppConfig::~AppConfig()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AppConfig::LoadConfig()
|
|
|
|
|
{
|
|
|
|
|
// Read config file
|
|
|
|
|
ifstream jsonFile;
|
|
|
|
|
const WString f(WApplication::appRoot() + WString(JSON_CONFIG_FILE));
|
|
|
|
|
jsonFile.open(f.toUTF8().c_str());
|
|
|
|
|
if (!jsonFile.is_open())
|
|
|
|
|
{
|
|
|
|
|
m_notifyWidget->setText(TR("CantLoadConfig"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
string line;
|
|
|
|
|
WString configData;
|
|
|
|
|
while (getline(jsonFile, line))
|
|
|
|
|
{
|
|
|
|
|
configData += WString(line);
|
|
|
|
|
}
|
|
|
|
|
jsonFile.close();
|
|
|
|
|
|
|
|
|
|
Json::ParseError pe;
|
|
|
|
|
Json::parse(configData.toUTF8(), m_RootObject, pe, false);
|
|
|
|
|
if (!string(pe.what()).empty())
|
|
|
|
|
{
|
|
|
|
|
// pe.what() dumps all Json data
|
|
|
|
|
m_notifyWidget->setText(TR("CantParseJson"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m_RootObject.contains("sCommonName"))
|
|
|
|
|
{
|
|
|
|
|
m_notifyWidget->setText(TR("CNObjectMissing"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
m_SubjectCNObject = m_RootObject.get("sCommonName");
|
|
|
|
|
// If the client cert's subject common name is not listed, deny access.
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
{
|
|
|
|
|
m_notifyWidget->setText(TR("AccessDenied"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AppConfig::CanImport() const
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8())) // Should not happen, see above
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canImport"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canImport");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AppConfig::CanDelete() const
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canDelete"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canDelete");
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-25 17:28:47 +01:00
|
|
|
bool AppConfig::CanEditOwnerTrust() const
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canEditOwnerTrust"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canEditOwnerTrust");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-03 11:06:25 +01:00
|
|
|
bool AppConfig::CanEditUidValidity() const
|
|
|
|
|
{
|
|
|
|
|
if (PrivateKeyIds().size() == 0)
|
|
|
|
|
return false;
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canEditUidValidity"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canEditUidValidity");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-07 22:17:44 +01:00
|
|
|
bool AppConfig::CanEditExpiryTime() const
|
|
|
|
|
{
|
|
|
|
|
if (PrivateKeyIds().size() == 0)
|
|
|
|
|
return false;
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canEditExpiryTime"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canEditExpiryTime");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-11 21:36:08 +01:00
|
|
|
bool AppConfig::CanCreateKeys() const
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canCreateKeys"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canCreateKeys");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-19 19:16:17 +01:00
|
|
|
bool AppConfig::CanAddRevokeUid() const
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("canAddRevokeUids"))
|
|
|
|
|
return false;
|
|
|
|
|
return cnObject.get("canAddRevokeUids");
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-11 21:36:08 +01:00
|
|
|
bool AppConfig::UpdateSecretKeyOwnership(const WString& fpr, bool own)
|
|
|
|
|
{
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return false;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
Json::Array aKeyId;
|
|
|
|
|
if (cnObject.contains("privKeyIds"))
|
|
|
|
|
{
|
|
|
|
|
aKeyId = cnObject.get("privKeyIds");
|
|
|
|
|
cnObject.erase("privKeyIds");
|
|
|
|
|
}
|
|
|
|
|
Json::Array::iterator it = std::find(aKeyId.begin(), aKeyId.end(), Json::Value(fpr));
|
|
|
|
|
if (it == aKeyId.end())
|
|
|
|
|
{
|
2020-11-14 14:46:28 +01:00
|
|
|
if (own)
|
2020-11-11 21:36:08 +01:00
|
|
|
aKeyId.push_back(Json::Value(fpr));
|
|
|
|
|
else
|
|
|
|
|
return true; // We don't own it.
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (not own)
|
|
|
|
|
aKeyId.erase(it);
|
|
|
|
|
else
|
|
|
|
|
return true; // We already own it.
|
|
|
|
|
}
|
2020-11-14 14:46:28 +01:00
|
|
|
|
2020-11-11 21:36:08 +01:00
|
|
|
// TODO : Do this **better**, without replacing sequentially up to root.
|
|
|
|
|
cnObject.insert(make_pair("privKeyIds", aKeyId));
|
|
|
|
|
m_SubjectCNObject.erase(commonName.toUTF8());
|
|
|
|
|
m_SubjectCNObject.insert(make_pair(commonName.toUTF8(), cnObject));
|
|
|
|
|
m_RootObject.erase("sCommonName");
|
|
|
|
|
m_RootObject.insert(make_pair("sCommonName", m_SubjectCNObject));
|
|
|
|
|
|
|
|
|
|
if (WriteTextFile(JSON_CONFIG_FILE, Json::serialize(m_RootObject)))
|
|
|
|
|
return LoadConfig();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-11-07 22:17:44 +01:00
|
|
|
|
2019-10-25 20:16:43 +02:00
|
|
|
vector<WString> AppConfig::PrivateKeyIds() const
|
|
|
|
|
{
|
|
|
|
|
// List private key identifiers.
|
|
|
|
|
vector<WString> pKeyIds;
|
|
|
|
|
const WString commonName = GetSubjectDnAttribute(WSslCertificate::DnAttributeName::CommonName);
|
|
|
|
|
if (!m_SubjectCNObject.contains(commonName.toUTF8()))
|
|
|
|
|
return pKeyIds;
|
|
|
|
|
Json::Object cnObject = m_SubjectCNObject.get(commonName.toUTF8());
|
|
|
|
|
if (!cnObject.contains("privKeyIds"))
|
|
|
|
|
return pKeyIds;
|
|
|
|
|
Json::Array aKeyId = cnObject.get("privKeyIds");
|
|
|
|
|
for (uint i = 0; i < aKeyId.size(); i++)
|
|
|
|
|
{
|
|
|
|
|
pKeyIds.push_back(aKeyId.at(i));
|
|
|
|
|
}
|
|
|
|
|
return pKeyIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const WString AppConfig::GetSubjectDnAttribute(const WSslCertificate::DnAttributeName& attrName) const
|
|
|
|
|
{
|
|
|
|
|
// Get an X509 client certificate attribute value
|
|
|
|
|
const vector<WSslCertificate::DnAttribute> * dnAttr
|
|
|
|
|
= &(WApplication::instance()->environment().sslInfo()->clientCertificate().subjectDn());
|
|
|
|
|
for (uint i = 0; i < dnAttr->size(); i++)
|
|
|
|
|
{
|
|
|
|
|
if (dnAttr->at(i).name() == attrName)
|
|
|
|
|
return dnAttr->at(i).value();
|
|
|
|
|
}
|
|
|
|
|
return WString::Empty;
|
2020-11-03 11:06:25 +01:00
|
|
|
}
|
2020-11-11 21:36:08 +01:00
|
|
|
|
|
|
|
|
bool AppConfig::WriteTextFile(const WString& filePath, const WString& content)
|
|
|
|
|
{
|
|
|
|
|
gs_fileWriteMutex.lock();
|
|
|
|
|
ofstream osFile;
|
|
|
|
|
const WString f(WApplication::appRoot() + WString(filePath));
|
|
|
|
|
osFile.open(f.toUTF8().c_str());
|
|
|
|
|
if (!osFile.is_open())
|
|
|
|
|
{
|
|
|
|
|
gs_fileWriteMutex.unlock();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
osFile << content.toUTF8();
|
|
|
|
|
osFile.flush();
|
|
|
|
|
osFile.close();
|
|
|
|
|
gs_fileWriteMutex.unlock();
|
2020-11-12 10:56:37 +01:00
|
|
|
return osFile.good();
|
2020-11-11 21:36:08 +01:00
|
|
|
}
|