1
0
mirror of https://github.com/binary-kitchen/doorlockd synced 2024-11-16 20:19:13 +01:00
doorlockd-mirror/doorlockd/logic.cpp

235 lines
5.7 KiB
C++
Raw Normal View History

2015-05-12 17:35:57 +02:00
#include <chrono>
2015-05-11 00:18:22 +02:00
#include <cstdlib>
#include <json/json.h>
2015-05-11 20:40:26 +02:00
#define LDAP_DEPRECATED 1
#include <ldap.h>
2015-05-12 17:35:57 +02:00
#include <errno.h>
2015-05-11 00:18:22 +02:00
#include "util.h"
#include "logic.h"
using namespace std;
2015-05-13 16:40:30 +02:00
Logic::Logic(const chrono::seconds tokenTimeout,
2015-05-24 19:15:47 +02:00
const string &ldapUri,
2015-05-13 16:40:30 +02:00
const string &bindDN,
const string &webPrefix,
const string &serDev) :
2015-05-11 00:18:22 +02:00
_logger(Logger::get()),
_door(serDev),
2015-05-13 16:40:30 +02:00
_tokenTimeout(tokenTimeout),
2015-05-24 19:15:47 +02:00
_ldapUri(ldapUri),
2015-05-13 16:40:30 +02:00
_bindDN(bindDN),
2015-05-21 13:35:30 +02:00
_webPrefix(webPrefix)
2015-05-11 00:18:22 +02:00
{
srand(time(NULL));
2015-05-12 17:35:57 +02:00
_createNewToken(false);
_tokenUpdater = thread([this] () {
while (_run)
{
unique_lock<mutex> l(_mutex);
2015-05-21 13:35:30 +02:00
_tokenCondition.wait_for(l, _tokenTimeout);
2015-05-12 17:35:57 +02:00
if (_run == false)
{
break;
} else {
_createNewToken(true);
}
}
});
2015-05-11 00:18:22 +02:00
}
Logic::~Logic()
{
2015-05-12 17:35:57 +02:00
_run = false;
2015-05-21 13:35:30 +02:00
_tokenCondition.notify_one();
2015-05-12 17:35:57 +02:00
_tokenUpdater.join();
2015-05-11 00:18:22 +02:00
}
2015-05-12 15:59:04 +02:00
Logic::Response Logic::parseRequest(const string &str)
2015-05-11 00:18:22 +02:00
{
2015-05-12 17:35:57 +02:00
unique_lock<mutex> l(_mutex);
2015-05-14 15:33:40 +02:00
_logger(LogLevel::info, "Incoming request...");
2015-05-11 00:18:22 +02:00
Json::Reader reader;
Json::Value root;
2015-05-12 15:59:04 +02:00
Response retval = Fail;
2015-05-12 01:28:02 +02:00
string action, user, password, ip, token;
2015-05-11 00:18:22 +02:00
bool suc = reader.parse(str, root, false);
if (!suc)
{
2015-05-14 15:33:40 +02:00
_logger(LogLevel::warning, "Request ist not valid JSON!");
2015-05-12 15:59:04 +02:00
retval = NotJson;
2015-05-12 01:28:02 +02:00
goto out;
2015-05-11 00:18:22 +02:00
}
try {
action = getJsonOrFail<string>(root, "action");
2015-05-12 01:28:02 +02:00
ip = getJsonOrFail<string>(root, "ip");
user = getJsonOrFail<string>(root, "user");
password = getJsonOrFail<string>(root, "password");
token = getJsonOrFail<string>(root, "token");
2015-05-11 00:18:22 +02:00
}
catch (...)
{
_logger(LogLevel::warning, "Error parsing JSON");
2015-05-12 15:59:04 +02:00
retval = JsonError;
2015-05-12 01:28:02 +02:00
goto out;
2015-05-11 00:18:22 +02:00
}
2015-05-14 15:33:40 +02:00
_logger(" Action: " + action, LogLevel::notice);
_logger(" User : " + user, LogLevel::notice);
_logger(" IP : " + ip, LogLevel::notice);
_logger(" Token : " + token, LogLevel::notice);
2015-05-11 00:18:22 +02:00
if (_checkToken(token) == false)
2015-05-11 00:18:22 +02:00
{
_logger(LogLevel::error, "User provided invalid token");
retval = InvalidToken;
goto out;
}
2015-05-11 00:18:22 +02:00
retval = _checkLDAP(user,password);
if (retval != Success)
{
_logger(LogLevel::error, "Ldap error");
goto out;
2015-05-11 00:18:22 +02:00
}
if (action == "lock")
{
2015-05-12 15:59:04 +02:00
retval = _lock();
2015-05-11 00:18:22 +02:00
} else if (action == "unlock") {
2015-05-12 15:59:04 +02:00
retval = _unlock();
2015-05-11 00:18:22 +02:00
} else {
_logger(LogLevel::error, "Unknown Action: %s", action.c_str());
2015-05-12 15:59:04 +02:00
retval = UnknownAction;
2015-05-11 00:18:22 +02:00
}
2015-05-12 01:28:02 +02:00
out:
return retval;
2015-05-11 00:18:22 +02:00
}
2015-05-12 15:59:04 +02:00
Logic::Response Logic::_lock()
2015-05-11 00:18:22 +02:00
{
if (_door.state() == Door::State::Locked)
2015-05-11 00:18:22 +02:00
{
2015-05-12 15:59:04 +02:00
_logger(LogLevel::warning, "Unable to lock: already closed");
return AlreadyLocked;
2015-05-11 00:18:22 +02:00
}
2015-05-12 15:59:04 +02:00
2015-05-11 00:18:22 +02:00
_door.lock();
2015-05-12 17:35:57 +02:00
_createNewToken(false);
2015-05-12 15:59:04 +02:00
return Success;
2015-05-11 00:18:22 +02:00
}
2015-05-12 15:59:04 +02:00
Logic::Response Logic::_unlock()
2015-05-11 00:18:22 +02:00
{
const auto oldState = _door.state();
_door.unlock();
_createNewToken(false);
if (oldState == Door::State::Unlocked)
{
_logger(LogLevel::warning, "Unable to unlock: already unlocked");
return AlreadyUnlocked;
}
return Success;
2015-05-11 00:18:22 +02:00
}
bool Logic::_checkToken(const string &strToken)
{
try {
uint64_t token = toUint64(strToken);
if (token == _curToken || (_prevValid == true && token == _prevToken))
{
_logger(LogLevel::info, "Token check successful");
return true;
}
}
catch (const char* const &ex)
{
_logger(LogLevel::error, "Check Token failed for token \"%s\" (expected %s): %s", strToken.c_str(), toHexString(_curToken).c_str(), ex);
}
return false;
}
2015-05-12 15:59:04 +02:00
Logic::Response Logic::_checkLDAP(const string &user, const string &password)
2015-05-11 00:18:22 +02:00
{
2015-05-11 20:40:26 +02:00
constexpr int BUFFERSIZE = 1024;
char buffer[BUFFERSIZE];
2015-05-12 15:59:04 +02:00
Response retval = Fail;
2015-05-11 20:40:26 +02:00
int rc = -1;
LDAP* ld = nullptr;
unsigned long version = LDAP_VERSION3;
_logger(LogLevel::notice, "Trying to authenticate as user \"%s\"", user.c_str());
snprintf(buffer, BUFFERSIZE, _bindDN.c_str(), user.c_str());
2015-05-24 19:15:47 +02:00
rc = ldap_initialize(&ld, _ldapUri.c_str());
2015-05-11 20:40:26 +02:00
if(rc != LDAP_SUCCESS)
{
_logger(LogLevel::error, "LDAP initialize error: %s", ldap_err2string(rc));
2015-05-12 15:59:04 +02:00
retval = LDAPInit;
2015-05-11 20:40:26 +02:00
goto out2;
}
rc = ldap_set_option(ld,
LDAP_OPT_PROTOCOL_VERSION,
(void*)&version);
if (rc != LDAP_SUCCESS)
{
_logger(LogLevel::error, "LDAP set version failed");
2015-05-12 15:59:04 +02:00
retval = LDAPInit;
2015-05-11 20:40:26 +02:00
goto out;
}
rc = ldap_simple_bind_s(ld, buffer, password.c_str());
if (rc != LDAP_SUCCESS)
{
_logger(LogLevel::error, "Credential check for user \"%s\" failed: %s", user.c_str(), ldap_err2string(rc));
2015-05-12 15:59:04 +02:00
retval = InvalidCredentials;
2015-05-11 20:40:26 +02:00
goto out;
}
_logger(LogLevel::notice, "user \"%s\" successfully authenticated", user.c_str());
2015-05-12 15:59:04 +02:00
retval = Success;
2015-05-11 20:40:26 +02:00
out:
ldap_unbind(ld);
ld = nullptr;
out2:
return retval;
2015-05-11 00:18:22 +02:00
}
2015-05-12 17:35:57 +02:00
void Logic::_createNewToken(const bool stillValid)
2015-05-11 00:18:22 +02:00
{
// Todo Mutex einführen
2015-05-11 00:18:22 +02:00
_prevToken = _curToken;
_prevValid = stillValid;
_curToken = (((uint64_t)rand())<<32) | ((uint64_t)rand());
// TODO make things more pretty
const string uri = _webPrefix + toHexString(_curToken);
const int ARRAY_SIZE=1024;
char buffer[ARRAY_SIZE];
snprintf(buffer, ARRAY_SIZE,
"qrencode -l M -d 100 -s 5 \"%s\" -t png -o /tmp/qr.png", uri.c_str());
system(buffer);
2015-05-11 00:18:22 +02:00
ostringstream message;
message << "New Token generated: " << toHexString(_curToken) << " old Token: " << toHexString(_prevToken) << " is " << (_prevValid?"still":"not") << " valid";
_logger(message, LogLevel::info);
}