mirror of https://gitee.com/bigwinds/arangodb
changed reload to outdated
This commit is contained in:
parent
5c0dd05308
commit
ee98b59e2f
|
@ -64,7 +64,6 @@ HeartbeatThread::HeartbeatThread(TRI_server_t* server,
|
||||||
_statusLock(),
|
_statusLock(),
|
||||||
_agency(),
|
_agency(),
|
||||||
_condition(),
|
_condition(),
|
||||||
_refetchUsers(true),
|
|
||||||
_myId(ServerState::instance()->getId()),
|
_myId(ServerState::instance()->getId()),
|
||||||
_interval(interval),
|
_interval(interval),
|
||||||
_maxFailsBeforeWarning(maxFailsBeforeWarning),
|
_maxFailsBeforeWarning(maxFailsBeforeWarning),
|
||||||
|
@ -115,7 +114,8 @@ void HeartbeatThread::runDBServer() {
|
||||||
// mop: the heartbeat thread itself is now ready
|
// mop: the heartbeat thread itself is now ready
|
||||||
setReady();
|
setReady();
|
||||||
// mop: however we need to wait for the rest server here to come up
|
// mop: however we need to wait for the rest server here to come up
|
||||||
// otherwise we would already create collections and the coordinator would think
|
// otherwise we would already create collections and the coordinator would
|
||||||
|
// think
|
||||||
// ohhh the dbserver is online...pump some documents into it
|
// ohhh the dbserver is online...pump some documents into it
|
||||||
// which fails when it is still in maintenance mode
|
// which fails when it is still in maintenance mode
|
||||||
while (arangodb::rest::HttpHandlerFactory::isMaintenance()) {
|
while (arangodb::rest::HttpHandlerFactory::isMaintenance()) {
|
||||||
|
@ -125,32 +125,32 @@ void HeartbeatThread::runDBServer() {
|
||||||
// convert timeout to seconds
|
// convert timeout to seconds
|
||||||
double const interval = (double)_interval / 1000.0 / 1000.0;
|
double const interval = (double)_interval / 1000.0 / 1000.0;
|
||||||
|
|
||||||
std::function<bool(VPackSlice const& result)> updatePlan = [&](
|
std::function<bool(VPackSlice const& result)> updatePlan =
|
||||||
VPackSlice const& result) {
|
[&](VPackSlice const& result) {
|
||||||
if (!result.isNumber()) {
|
if (!result.isNumber()) {
|
||||||
LOG_TOPIC(ERR, Logger::HEARTBEAT)
|
LOG_TOPIC(ERR, Logger::HEARTBEAT) << "Plan Version is not a number! "
|
||||||
<< "Plan Version is not a number! " << result.toJson();
|
<< result.toJson();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
uint64_t version = result.getNumber<uint64_t>();
|
uint64_t version = result.getNumber<uint64_t>();
|
||||||
|
|
||||||
bool doSync = false;
|
bool doSync = false;
|
||||||
{
|
{
|
||||||
MUTEX_LOCKER(mutexLocker, _statusLock);
|
MUTEX_LOCKER(mutexLocker, _statusLock);
|
||||||
if (version > _desiredVersions.plan) {
|
if (version > _desiredVersions.plan) {
|
||||||
_desiredVersions.plan = version;
|
_desiredVersions.plan = version;
|
||||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT)
|
LOG_TOPIC(DEBUG, Logger::HEARTBEAT)
|
||||||
<< "Desired Current Version is now " << _desiredVersions.plan;
|
<< "Desired Current Version is now " << _desiredVersions.plan;
|
||||||
doSync = true;
|
doSync = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doSync) {
|
if (doSync) {
|
||||||
syncDBServerStatusQuo();
|
syncDBServerStatusQuo();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
auto planAgencyCallback = std::make_shared<AgencyCallback>(
|
auto planAgencyCallback = std::make_shared<AgencyCallback>(
|
||||||
_agency, "Plan/Version", updatePlan, true);
|
_agency, "Plan/Version", updatePlan, true);
|
||||||
|
@ -166,7 +166,7 @@ void HeartbeatThread::runDBServer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// we check Current/Version every few heartbeats:
|
// we check Current/Version every few heartbeats:
|
||||||
int const currentCountStart = 1; // set to 1 by Max to speed up discovery
|
int const currentCountStart = 1; // set to 1 by Max to speed up discovery
|
||||||
int currentCount = currentCountStart;
|
int currentCount = currentCountStart;
|
||||||
|
|
||||||
while (!isStopping()) {
|
while (!isStopping()) {
|
||||||
|
@ -189,8 +189,7 @@ void HeartbeatThread::runDBServer() {
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
||||||
<< "Looking at Sync/Commands/" + _myId;
|
<< "Looking at Sync/Commands/" + _myId;
|
||||||
|
|
||||||
AgencyCommResult result =
|
AgencyCommResult result = _agency.getValues("Sync/Commands/" + _myId);
|
||||||
_agency.getValues("Sync/Commands/" + _myId);
|
|
||||||
|
|
||||||
if (result.successful()) {
|
if (result.successful()) {
|
||||||
handleStateChange(result);
|
handleStateChange(result);
|
||||||
|
@ -206,10 +205,9 @@ void HeartbeatThread::runDBServer() {
|
||||||
LOG_TOPIC(ERR, Logger::HEARTBEAT)
|
LOG_TOPIC(ERR, Logger::HEARTBEAT)
|
||||||
<< "Could not read Current/Version from agency.";
|
<< "Could not read Current/Version from agency.";
|
||||||
} else {
|
} else {
|
||||||
VPackSlice s
|
VPackSlice s = res.slice()[0].get(
|
||||||
= res.slice()[0].get(std::vector<std::string>(
|
std::vector<std::string>({_agency.prefix(), std::string("Current"),
|
||||||
{_agency.prefix(), std::string("Current"),
|
std::string("Version")}));
|
||||||
std::string("Version")}));
|
|
||||||
if (!s.isInteger()) {
|
if (!s.isInteger()) {
|
||||||
LOG_TOPIC(ERR, Logger::HEARTBEAT)
|
LOG_TOPIC(ERR, Logger::HEARTBEAT)
|
||||||
<< "Current/Version in agency is not an integer.";
|
<< "Current/Version in agency is not an integer.";
|
||||||
|
@ -320,11 +318,11 @@ void HeartbeatThread::runCoordinator() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
AgencyReadTransaction trx(std::vector<std::string>({
|
AgencyReadTransaction trx(std::vector<std::string>(
|
||||||
_agency.prefixPath() + "Plan/Version",
|
{_agency.prefixPath() + "Plan/Version",
|
||||||
_agency.prefixPath() + "Current/Version",
|
_agency.prefixPath() + "Current/Version",
|
||||||
_agency.prefixPath() + "Sync/Commands/" + _myId,
|
_agency.prefixPath() + "Sync/Commands/" + _myId,
|
||||||
_agency.prefixPath() + "Sync/UserVersion"}));
|
_agency.prefixPath() + "Sync/UserVersion"}));
|
||||||
AgencyCommResult result = _agency.sendTransactionWithFailover(trx);
|
AgencyCommResult result = _agency.sendTransactionWithFailover(trx);
|
||||||
|
|
||||||
if (!result.successful()) {
|
if (!result.successful()) {
|
||||||
|
@ -336,9 +334,8 @@ void HeartbeatThread::runCoordinator() {
|
||||||
|
|
||||||
handleStateChange(result);
|
handleStateChange(result);
|
||||||
|
|
||||||
VPackSlice versionSlice
|
VPackSlice versionSlice = result.slice()[0].get(
|
||||||
= result.slice()[0].get(std::vector<std::string>(
|
std::vector<std::string>({_agency.prefix(), "Plan", "Version"}));
|
||||||
{_agency.prefix(), "Plan", "Version"}));
|
|
||||||
|
|
||||||
if (versionSlice.isInteger()) {
|
if (versionSlice.isInteger()) {
|
||||||
// there is a plan version
|
// there is a plan version
|
||||||
|
@ -346,8 +343,7 @@ void HeartbeatThread::runCoordinator() {
|
||||||
uint64_t planVersion = 0;
|
uint64_t planVersion = 0;
|
||||||
try {
|
try {
|
||||||
planVersion = versionSlice.getUInt();
|
planVersion = versionSlice.getUInt();
|
||||||
}
|
} catch (...) {
|
||||||
catch (...) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (planVersion > lastPlanVersionNoticed) {
|
if (planVersion > lastPlanVersionNoticed) {
|
||||||
|
@ -363,58 +359,30 @@ void HeartbeatThread::runCoordinator() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VPackSlice slice =
|
VPackSlice slice = result.slice()[0].get(
|
||||||
result.slice()[0].get(std::vector<std::string>(
|
std::vector<std::string>({_agency.prefix(), "Sync", "UserVersion"}));
|
||||||
{_agency.prefix(), "Sync", "UserVersion"}));
|
|
||||||
|
|
||||||
if (slice.isInteger()) {
|
if (slice.isInteger()) {
|
||||||
// there is a UserVersion
|
// there is a UserVersion
|
||||||
uint64_t userVersion = 0;
|
uint64_t userVersion = 0;
|
||||||
try {
|
try {
|
||||||
userVersion = slice.getUInt();
|
userVersion = slice.getUInt();
|
||||||
|
} catch (...) {
|
||||||
}
|
}
|
||||||
catch (...) {
|
|
||||||
}
|
|
||||||
if (userVersion > 0 && userVersion != oldUserVersion) {
|
if (userVersion > 0 && userVersion != oldUserVersion) {
|
||||||
// reload user cache for all databases
|
oldUserVersion = userVersion;
|
||||||
std::vector<DatabaseID> dbs =
|
RestServerFeature::AUTH_INFO.outdate();
|
||||||
ClusterInfo::instance()->databases(true);
|
|
||||||
std::vector<DatabaseID>::iterator i;
|
|
||||||
bool allOK = true;
|
|
||||||
for (i = dbs.begin(); i != dbs.end(); ++i) {
|
|
||||||
TRI_vocbase_t* vocbase =
|
|
||||||
TRI_UseCoordinatorDatabaseServer(_server, i->c_str());
|
|
||||||
|
|
||||||
if (vocbase != nullptr && TRI_EqualString(vocbase->_name, TRI_VOC_SYSTEM_DATABASE)) {
|
|
||||||
LOG_TOPIC(DEBUG, Logger::HEARTBEAT)
|
|
||||||
<< "Reloading users for database " << vocbase->_name
|
|
||||||
<< ".";
|
|
||||||
|
|
||||||
if (!fetchUsers()) {
|
|
||||||
// something is wrong... probably the database server
|
|
||||||
// with the _users collection is not yet available
|
|
||||||
allOK = false;
|
|
||||||
// we will not set oldUserVersion such that we will try this
|
|
||||||
// very same exercise again in the next heartbeat
|
|
||||||
}
|
|
||||||
TRI_ReleaseVocBase(vocbase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allOK) {
|
|
||||||
oldUserVersion = userVersion;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
versionSlice = result.slice()[0].get(std::vector<std::string>(
|
versionSlice = result.slice()[0].get(
|
||||||
{_agency.prefix(), "Current", "Version"}));
|
std::vector<std::string>({_agency.prefix(), "Current", "Version"}));
|
||||||
if (versionSlice.isInteger()) {
|
if (versionSlice.isInteger()) {
|
||||||
|
|
||||||
uint64_t currentVersion = 0;
|
uint64_t currentVersion = 0;
|
||||||
try {
|
try {
|
||||||
currentVersion = versionSlice.getUInt();
|
currentVersion = versionSlice.getUInt();
|
||||||
}
|
} catch (...) {
|
||||||
catch (...) {
|
|
||||||
}
|
}
|
||||||
if (currentVersion > lastCurrentVersionNoticed) {
|
if (currentVersion > lastCurrentVersionNoticed) {
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
||||||
|
@ -440,7 +408,6 @@ void HeartbeatThread::runCoordinator() {
|
||||||
remain = 0.0;
|
remain = 0.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT) << "stopped heartbeat thread";
|
LOG_TOPIC(TRACE, Logger::HEARTBEAT) << "stopped heartbeat thread";
|
||||||
|
@ -496,7 +463,6 @@ void HeartbeatThread::removeDispatchedJob(DBServerAgencySyncResult result) {
|
||||||
|
|
||||||
static std::string const prefixPlanChangeCoordinator = "Plan/Databases";
|
static std::string const prefixPlanChangeCoordinator = "Plan/Databases";
|
||||||
bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
||||||
|
|
||||||
bool fetchingUsersFailed = false;
|
bool fetchingUsersFailed = false;
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT) << "found a plan update";
|
LOG_TOPIC(TRACE, Logger::HEARTBEAT) << "found a plan update";
|
||||||
AgencyCommResult result;
|
AgencyCommResult result;
|
||||||
|
@ -509,11 +475,9 @@ bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.successful()) {
|
if (result.successful()) {
|
||||||
|
|
||||||
std::vector<TRI_voc_tick_t> ids;
|
std::vector<TRI_voc_tick_t> ids;
|
||||||
velocypack::Slice databases =
|
velocypack::Slice databases = result.slice()[0].get(
|
||||||
result.slice()[0].get(std::vector<std::string>(
|
std::vector<std::string>({AgencyComm::prefix(), "Plan", "Databases"}));
|
||||||
{AgencyComm::prefix(), "Plan", "Databases"}));
|
|
||||||
|
|
||||||
if (!databases.isObject()) {
|
if (!databases.isObject()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -522,8 +486,7 @@ bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
||||||
// loop over all database names we got and create a local database
|
// loop over all database names we got and create a local database
|
||||||
// instance if not yet present:
|
// instance if not yet present:
|
||||||
|
|
||||||
for (auto const& options : VPackObjectIterator (databases)) {
|
for (auto const& options : VPackObjectIterator(databases)) {
|
||||||
|
|
||||||
if (!options.value.isObject()) {
|
if (!options.value.isObject()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -555,7 +518,7 @@ bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TRI_vocbase_t* vocbase =
|
TRI_vocbase_t* vocbase =
|
||||||
TRI_UseCoordinatorDatabaseServer(_server, name.c_str());
|
TRI_UseCoordinatorDatabaseServer(_server, name.c_str());
|
||||||
|
|
||||||
if (vocbase == nullptr) {
|
if (vocbase == nullptr) {
|
||||||
// database does not yet exist, create it now
|
// database does not yet exist, create it now
|
||||||
|
@ -571,32 +534,14 @@ bool HeartbeatThread::handlePlanChangeCoordinator(uint64_t currentPlanVersion) {
|
||||||
// create a local database object...
|
// create a local database object...
|
||||||
TRI_CreateCoordinatorDatabaseServer(_server, id, name.c_str(),
|
TRI_CreateCoordinatorDatabaseServer(_server, id, name.c_str(),
|
||||||
&defaults, &vocbase);
|
&defaults, &vocbase);
|
||||||
|
} else {
|
||||||
if (vocbase != nullptr && TRI_EqualString(vocbase->_name, TRI_VOC_SYSTEM_DATABASE)) {
|
|
||||||
HasRunOnce = 1;
|
|
||||||
|
|
||||||
// insert initial user(s) for database
|
|
||||||
if (!fetchUsers()) {
|
|
||||||
TRI_ReleaseVocBase(vocbase);
|
|
||||||
return false; // We give up, we will try again in the
|
|
||||||
// next heartbeat
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (TRI_EqualString(vocbase->_name, TRI_VOC_SYSTEM_DATABASE)) {
|
|
||||||
if (_refetchUsers) {
|
|
||||||
// must re-fetch users for an existing database
|
|
||||||
if (!fetchUsers()) {
|
|
||||||
fetchingUsersFailed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_ReleaseVocBase(vocbase);
|
TRI_ReleaseVocBase(vocbase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the list of databases that we know about locally
|
// get the list of databases that we know about locally
|
||||||
std::vector<TRI_voc_tick_t> localIds =
|
std::vector<TRI_voc_tick_t> localIds =
|
||||||
TRI_GetIdsCoordinatorDatabaseServer(_server);
|
TRI_GetIdsCoordinatorDatabaseServer(_server);
|
||||||
|
|
||||||
for (auto id : localIds) {
|
for (auto id : localIds) {
|
||||||
auto r = std::find(ids.begin(), ids.end(), id);
|
auto r = std::find(ids.begin(), ids.end(), id);
|
||||||
|
@ -701,9 +646,8 @@ bool HeartbeatThread::syncDBServerStatusQuo() {
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
bool HeartbeatThread::handleStateChange(AgencyCommResult& result) {
|
bool HeartbeatThread::handleStateChange(AgencyCommResult& result) {
|
||||||
VPackSlice const slice = result.slice()[0].get(
|
VPackSlice const slice = result.slice()[0].get(std::vector<std::string>(
|
||||||
std::vector<std::string>({ AgencyComm::prefix(), "Sync",
|
{AgencyComm::prefix(), "Sync", "Commands", _myId}));
|
||||||
"Commands", _myId }));
|
|
||||||
if (slice.isString()) {
|
if (slice.isString()) {
|
||||||
std::string command = slice.copyString();
|
std::string command = slice.copyString();
|
||||||
ServerState::StateEnum newState = ServerState::stringToState(command);
|
ServerState::StateEnum newState = ServerState::stringToState(command);
|
||||||
|
@ -724,7 +668,7 @@ bool HeartbeatThread::handleStateChange(AgencyCommResult& result) {
|
||||||
|
|
||||||
bool HeartbeatThread::sendState() {
|
bool HeartbeatThread::sendState() {
|
||||||
const AgencyCommResult result = _agency.sendServerState(0.0);
|
const AgencyCommResult result = _agency.sendServerState(0.0);
|
||||||
// 8.0 * static_cast<double>(_interval) / 1000.0 / 1000.0);
|
// 8.0 * static_cast<double>(_interval) / 1000.0 / 1000.0);
|
||||||
|
|
||||||
if (result.successful()) {
|
if (result.successful()) {
|
||||||
_numFails = 0;
|
_numFails = 0;
|
||||||
|
@ -735,37 +679,10 @@ bool HeartbeatThread::sendState() {
|
||||||
std::string const endpoints = AgencyComm::getEndpointsString();
|
std::string const endpoints = AgencyComm::getEndpointsString();
|
||||||
|
|
||||||
LOG_TOPIC(WARN, Logger::HEARTBEAT)
|
LOG_TOPIC(WARN, Logger::HEARTBEAT)
|
||||||
<< "heartbeat could not be sent to agency endpoints ("
|
<< "heartbeat could not be sent to agency endpoints (" << endpoints
|
||||||
<< endpoints << "): http code: " << result.httpCode()
|
<< "): http code: " << result.httpCode() << ", body: " << result.body();
|
||||||
<< ", body: " << result.body();
|
|
||||||
_numFails = 0;
|
_numFails = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief fetch users for a database (run on coordinator only)
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool HeartbeatThread::fetchUsers() {
|
|
||||||
VPackBuilder builder;
|
|
||||||
builder.openArray();
|
|
||||||
|
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
|
||||||
<< "fetching users for database";
|
|
||||||
|
|
||||||
bool result = RestServerFeature::AUTH_INFO.reload();
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
|
||||||
<< "fetching users successful";
|
|
||||||
_refetchUsers = false;
|
|
||||||
} else {
|
|
||||||
LOG_TOPIC(TRACE, Logger::HEARTBEAT)
|
|
||||||
<< "fetching users failed";
|
|
||||||
_refetchUsers = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
|
@ -136,12 +136,6 @@ class HeartbeatThread : public Thread {
|
||||||
|
|
||||||
bool sendState();
|
bool sendState();
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief fetch users for a database (run on coordinator only)
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool fetchUsers();
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief bring the db server in sync with the desired state
|
/// @brief bring the db server in sync with the desired state
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -179,12 +173,6 @@ class HeartbeatThread : public Thread {
|
||||||
|
|
||||||
arangodb::basics::ConditionVariable _condition;
|
arangodb::basics::ConditionVariable _condition;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief users will be re-fetched the next time the heartbeat thread runs
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
bool _refetchUsers;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief this server's id
|
/// @brief this server's id
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -298,7 +298,7 @@ void RestServerFeature::start() {
|
||||||
|
|
||||||
// populate the authentication cache. otherwise no one can access the new
|
// populate the authentication cache. otherwise no one can access the new
|
||||||
// database
|
// database
|
||||||
RestServerFeature::AUTH_INFO.reload();
|
RestServerFeature::AUTH_INFO.outdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RestServerFeature::stop() {
|
void RestServerFeature::stop() {
|
||||||
|
|
|
@ -945,13 +945,9 @@ static void JS_ReloadAuth(v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
TRI_V8_THROW_EXCEPTION_USAGE("RELOAD_AUTH()");
|
TRI_V8_THROW_EXCEPTION_USAGE("RELOAD_AUTH()");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool result = RestServerFeature::AUTH_INFO.reload();
|
RestServerFeature::AUTH_INFO.outdate();
|
||||||
|
|
||||||
if (result) {
|
TRI_V8_RETURN_TRUE();
|
||||||
TRI_V8_RETURN_TRUE();
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_V8_RETURN_FALSE();
|
|
||||||
TRI_V8_TRY_CATCH_END
|
TRI_V8_TRY_CATCH_END
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,26 +112,25 @@ static AuthEntry CreateAuthEntry(VPackSlice const& slice) {
|
||||||
char const* value = obj.value.getString(length);
|
char const* value = obj.value.getString(length);
|
||||||
|
|
||||||
if (TRI_CaseEqualString(value, "rw", 2)) {
|
if (TRI_CaseEqualString(value, "rw", 2)) {
|
||||||
if (key == "*") {
|
if (key == "*") {
|
||||||
allDatabases = AuthLevel::RW;
|
allDatabases = AuthLevel::RW;
|
||||||
} else {
|
} else {
|
||||||
databases.emplace(key, AuthLevel::RW);
|
databases.emplace(key, AuthLevel::RW);
|
||||||
}
|
}
|
||||||
}
|
} else if (TRI_CaseEqualString(value, "ro", 2)) {
|
||||||
else if (TRI_CaseEqualString(value, "ro", 2)) {
|
if (key == "*") {
|
||||||
if (key == "*") {
|
allDatabases = AuthLevel::RO;
|
||||||
allDatabases = AuthLevel::RO;
|
} else {
|
||||||
} else {
|
databases.emplace(key, AuthLevel::RO);
|
||||||
databases.emplace(key, AuthLevel::RO);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// build authentication entry
|
// build authentication entry
|
||||||
return AuthEntry(userSlice.copyString(), methodSlice.copyString(),
|
return AuthEntry(userSlice.copyString(), methodSlice.copyString(),
|
||||||
saltSlice.copyString(), hashSlice.copyString(),
|
saltSlice.copyString(), hashSlice.copyString(), databases,
|
||||||
databases, allDatabases, active, mustChange);
|
allDatabases, active, mustChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthLevel AuthEntry::canUseDatabase(std::string const& dbname) const {
|
AuthLevel AuthEntry::canUseDatabase(std::string const& dbname) const {
|
||||||
|
@ -217,15 +216,15 @@ bool AuthInfo::populate(VPackSlice const& slice) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthInfo::reload() {
|
void AuthInfo::reload() {
|
||||||
insertInitial();
|
insertInitial();
|
||||||
|
|
||||||
TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->vocbase();
|
TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->vocbase();
|
||||||
|
|
||||||
if (vocbase == nullptr) {
|
if (vocbase == nullptr) {
|
||||||
LOG(DEBUG) << "system database is unknown, cannot load authentication "
|
LOG(DEBUG) << "system database is unknown, cannot load authentication "
|
||||||
<< "and authorization information";
|
<< "and authorization information";
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(DEBUG) << "starting to load authentication and authorization information";
|
LOG(DEBUG) << "starting to load authentication and authorization information";
|
||||||
|
@ -236,24 +235,25 @@ bool AuthInfo::reload() {
|
||||||
int res = trx.begin();
|
int res = trx.begin();
|
||||||
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
if (res != TRI_ERROR_NO_ERROR) {
|
||||||
return false;
|
LOG(ERR) << "cannot start transaction to load authentication";
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
OperationResult users =
|
OperationResult users =
|
||||||
trx.all(TRI_COL_NAME_USERS, 0, UINT64_MAX, OperationOptions());
|
trx.all(TRI_COL_NAME_USERS, 0, UINT64_MAX, OperationOptions());
|
||||||
|
|
||||||
trx.finish(users.code);
|
trx.finish(users.code);
|
||||||
|
|
||||||
if (users.failed()) {
|
if (users.failed()) {
|
||||||
LOG(ERR) << "cannot read users from _users collection";
|
LOG(ERR) << "cannot read users from _users collection";
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto usersSlice = users.slice();
|
auto usersSlice = users.slice();
|
||||||
|
|
||||||
if (!usersSlice.isArray()) {
|
if (!usersSlice.isArray()) {
|
||||||
LOG(ERR) << "cannot read users from _users collection";
|
LOG(ERR) << "cannot read users from _users collection";
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usersSlice.length() == 0) {
|
if (usersSlice.length() == 0) {
|
||||||
|
@ -262,11 +262,15 @@ bool AuthInfo::reload() {
|
||||||
populate(usersSlice);
|
populate(usersSlice);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
_outdated = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthResult AuthInfo::checkPassword(std::string const& username,
|
AuthResult AuthInfo::checkPassword(std::string const& username,
|
||||||
std::string const& password) {
|
std::string const& password) {
|
||||||
|
if (_outdated) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
AuthResult result;
|
AuthResult result;
|
||||||
|
|
||||||
// look up username
|
// look up username
|
||||||
|
@ -299,22 +303,22 @@ AuthResult AuthInfo::checkPassword(std::string const& username,
|
||||||
try {
|
try {
|
||||||
if (passwordMethod == "sha1") {
|
if (passwordMethod == "sha1") {
|
||||||
arangodb::rest::SslInterface::sslSHA1(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslSHA1(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else if (passwordMethod == "sha512") {
|
} else if (passwordMethod == "sha512") {
|
||||||
arangodb::rest::SslInterface::sslSHA512(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslSHA512(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else if (passwordMethod == "sha384") {
|
} else if (passwordMethod == "sha384") {
|
||||||
arangodb::rest::SslInterface::sslSHA384(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslSHA384(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else if (passwordMethod == "sha256") {
|
} else if (passwordMethod == "sha256") {
|
||||||
arangodb::rest::SslInterface::sslSHA256(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslSHA256(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else if (passwordMethod == "sha224") {
|
} else if (passwordMethod == "sha224") {
|
||||||
arangodb::rest::SslInterface::sslSHA224(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslSHA224(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else if (passwordMethod == "md5") {
|
} else if (passwordMethod == "md5") {
|
||||||
arangodb::rest::SslInterface::sslMD5(salted.c_str(), len, crypted,
|
arangodb::rest::SslInterface::sslMD5(salted.c_str(), len, crypted,
|
||||||
cryptedLength);
|
cryptedLength);
|
||||||
} else {
|
} else {
|
||||||
// invalid algorithm...
|
// invalid algorithm...
|
||||||
}
|
}
|
||||||
|
@ -329,8 +333,8 @@ AuthResult AuthInfo::checkPassword(std::string const& username,
|
||||||
char* hex = TRI_EncodeHexString(crypted, cryptedLength, &hexLen);
|
char* hex = TRI_EncodeHexString(crypted, cryptedLength, &hexLen);
|
||||||
|
|
||||||
if (hex != nullptr) {
|
if (hex != nullptr) {
|
||||||
result._authorized = auth.checkPasswordHash(hex);
|
result._authorized = auth.checkPasswordHash(hex);
|
||||||
TRI_FreeString(TRI_CORE_MEM_ZONE, hex);
|
TRI_FreeString(TRI_CORE_MEM_ZONE, hex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,7 +344,12 @@ AuthResult AuthInfo::checkPassword(std::string const& username,
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthLevel AuthInfo::canUseDatabase(std::string const& username, std::string const& dbname) {
|
AuthLevel AuthInfo::canUseDatabase(std::string const& username,
|
||||||
|
std::string const& dbname) {
|
||||||
|
if (_outdated) {
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
auto const& it = _authInfo.find(username);
|
auto const& it = _authInfo.find(username);
|
||||||
|
|
||||||
if (it == _authInfo.end()) {
|
if (it == _authInfo.end()) {
|
||||||
|
@ -352,13 +361,18 @@ AuthLevel AuthInfo::canUseDatabase(std::string const& username, std::string cons
|
||||||
return entry.canUseDatabase(dbname);
|
return entry.canUseDatabase(dbname);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthResult AuthInfo::checkAuthentication(AuthType authType, std::string const& secret) {
|
AuthResult AuthInfo::checkAuthentication(AuthType authType,
|
||||||
switch (authType) {
|
std::string const& secret) {
|
||||||
case AuthType::BASIC:
|
if (_outdated) {
|
||||||
return checkAuthenticationBasic(secret);
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
case AuthType::JWT:
|
switch (authType) {
|
||||||
return checkAuthenticationJWT(secret);
|
case AuthType::BASIC:
|
||||||
|
return checkAuthenticationBasic(secret);
|
||||||
|
|
||||||
|
case AuthType::JWT:
|
||||||
|
return checkAuthenticationJWT(secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AuthResult();
|
return AuthResult();
|
||||||
|
@ -376,7 +390,7 @@ AuthResult AuthInfo::checkAuthenticationBasic(std::string const& secret) {
|
||||||
|
|
||||||
if (n == std::string::npos || n == 0 || n + 1 > up.size()) {
|
if (n == std::string::npos || n == 0 || n + 1 > up.size()) {
|
||||||
LOG(TRACE) << "invalid authentication data found, cannot extract "
|
LOG(TRACE) << "invalid authentication data found, cannot extract "
|
||||||
"username/password";
|
"username/password";
|
||||||
return AuthResult();
|
return AuthResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +443,8 @@ AuthResult AuthInfo::checkAuthenticationJWT(std::string const& secret) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<VPackBuilder> AuthInfo::parseJson(std::string const& str, std::string const& hint) {
|
std::shared_ptr<VPackBuilder> AuthInfo::parseJson(std::string const& str,
|
||||||
|
std::string const& hint) {
|
||||||
std::shared_ptr<VPackBuilder> result;
|
std::shared_ptr<VPackBuilder> result;
|
||||||
VPackParser parser;
|
VPackParser parser;
|
||||||
try {
|
try {
|
||||||
|
@ -447,7 +462,8 @@ std::shared_ptr<VPackBuilder> AuthInfo::parseJson(std::string const& str, std::s
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthInfo::validateJwtHeader(std::string const& header) {
|
bool AuthInfo::validateJwtHeader(std::string const& header) {
|
||||||
std::shared_ptr<VPackBuilder> headerBuilder = parseJson(StringUtils::decodeBase64(header), "jwt header");
|
std::shared_ptr<VPackBuilder> headerBuilder =
|
||||||
|
parseJson(StringUtils::decodeBase64(header), "jwt header");
|
||||||
if (headerBuilder.get() == nullptr) {
|
if (headerBuilder.get() == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -481,7 +497,8 @@ bool AuthInfo::validateJwtHeader(std::string const& header) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthInfo::validateJwtBody(std::string const& body, std::string* username) {
|
bool AuthInfo::validateJwtBody(std::string const& body, std::string* username) {
|
||||||
std::shared_ptr<VPackBuilder> bodyBuilder = parseJson(StringUtils::decodeBase64(body), "jwt body");
|
std::shared_ptr<VPackBuilder> bodyBuilder =
|
||||||
|
parseJson(StringUtils::decodeBase64(body), "jwt body");
|
||||||
if (bodyBuilder.get() == nullptr) {
|
if (bodyBuilder.get() == nullptr) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -515,8 +532,10 @@ bool AuthInfo::validateJwtBody(std::string const& body, std::string* username) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::chrono::system_clock::time_point expires(std::chrono::seconds(expSlice.getNumber<uint64_t>()));
|
std::chrono::system_clock::time_point expires(
|
||||||
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
|
std::chrono::seconds(expSlice.getNumber<uint64_t>()));
|
||||||
|
std::chrono::system_clock::time_point now =
|
||||||
|
std::chrono::system_clock::now();
|
||||||
|
|
||||||
if (now >= expires) {
|
if (now >= expires) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -525,9 +544,13 @@ bool AuthInfo::validateJwtBody(std::string const& body, std::string* username) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AuthInfo::validateJwtHMAC256Signature(std::string const& message, std::string const& signature) {
|
bool AuthInfo::validateJwtHMAC256Signature(std::string const& message,
|
||||||
|
std::string const& signature) {
|
||||||
std::string decodedSignature = StringUtils::decodeBase64U(signature);
|
std::string decodedSignature = StringUtils::decodeBase64U(signature);
|
||||||
|
|
||||||
std::string const& jwtSecret = RestServerFeature::getJwtSecret();
|
std::string const& jwtSecret = RestServerFeature::getJwtSecret();
|
||||||
return verifyHMAC(jwtSecret.c_str(), jwtSecret.length(), message.c_str(), message.length(), decodedSignature.c_str(), decodedSignature.length(), SslInterface::Algorithm::ALGORITHM_SHA256);
|
return verifyHMAC(jwtSecret.c_str(), jwtSecret.length(), message.c_str(),
|
||||||
|
message.length(), decodedSignature.c_str(),
|
||||||
|
decodedSignature.length(),
|
||||||
|
SslInterface::Algorithm::ALGORITHM_SHA256);
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class AuthEntry {
|
||||||
AuthEntry(std::string const& username, std::string const& passwordMethod,
|
AuthEntry(std::string const& username, std::string const& passwordMethod,
|
||||||
std::string const& passwordSalt, std::string const& passwordHash,
|
std::string const& passwordSalt, std::string const& passwordHash,
|
||||||
std::unordered_map<std::string, AuthLevel> databases, AuthLevel allDatabases,
|
std::unordered_map<std::string, AuthLevel> databases, AuthLevel allDatabases,
|
||||||
bool active, bool mustChange)
|
bool active, bool mustChange)
|
||||||
: _username(username),
|
: _username(username),
|
||||||
_passwordMethod(passwordMethod),
|
_passwordMethod(passwordMethod),
|
||||||
_passwordSalt(passwordSalt),
|
_passwordSalt(passwordSalt),
|
||||||
|
@ -96,18 +96,22 @@ class AuthInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool reload();
|
AuthInfo() : _outdated(true) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void outdate() { _outdated = true; }
|
||||||
|
|
||||||
AuthResult checkPassword(std::string const& username,
|
AuthResult checkPassword(std::string const& username,
|
||||||
std::string const& password);
|
std::string const& password);
|
||||||
|
|
||||||
AuthResult checkAuthentication(AuthType authType,
|
AuthResult checkAuthentication(AuthType authType,
|
||||||
std::string const& secret);
|
std::string const& secret);
|
||||||
|
|
||||||
AuthLevel canUseDatabase(std::string const& username,
|
AuthLevel canUseDatabase(std::string const& username,
|
||||||
std::string const& dbname);
|
std::string const& dbname);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void reload();
|
||||||
void clear();
|
void clear();
|
||||||
void insertInitial();
|
void insertInitial();
|
||||||
bool populate(velocypack::Slice const& slice);
|
bool populate(velocypack::Slice const& slice);
|
||||||
|
@ -121,6 +125,7 @@ class AuthInfo {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
basics::ReadWriteLock _authInfoLock;
|
basics::ReadWriteLock _authInfoLock;
|
||||||
|
std::atomic<bool> _outdated;
|
||||||
|
|
||||||
std::unordered_map<std::string, arangodb::AuthEntry> _authInfo;
|
std::unordered_map<std::string, arangodb::AuthEntry> _authInfo;
|
||||||
std::unordered_map<std::string, arangodb::AuthResult> _authBasicCache;
|
std::unordered_map<std::string, arangodb::AuthResult> _authBasicCache;
|
||||||
|
|
Loading…
Reference in New Issue