mirror of https://gitee.com/bigwinds/arangodb
issue 458.2.1: ensure LogicalCollection presence is checked before granting/revoking permissions (#6274)
* issue 458.2.1: ensure LogicalCollection presence is checked before granting/revoking permissions * try to address test failures * backport: support wildcard for database too * create collection before granting * adjust ruby tests to expect behaviour as defined by issue #458 * adjust expected Ruby test result * create required collection in Ruby test * revert back to previous test code since Ruby refuses to create required collection * missed revert
This commit is contained in:
parent
2177814b53
commit
a6f3444601
|
@ -39,6 +39,7 @@
|
||||||
#include "Random/UniformCharacter.h"
|
#include "Random/UniformCharacter.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
#include "RestServer/InitDatabaseFeature.h"
|
#include "RestServer/InitDatabaseFeature.h"
|
||||||
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
#include "Ssl/SslInterface.h"
|
#include "Ssl/SslInterface.h"
|
||||||
#include "Transaction/StandaloneContext.h"
|
#include "Transaction/StandaloneContext.h"
|
||||||
#include "Utils/ExecContext.h"
|
#include "Utils/ExecContext.h"
|
||||||
|
@ -51,6 +52,28 @@
|
||||||
#include <velocypack/Iterator.h>
|
#include <velocypack/Iterator.h>
|
||||||
#include <velocypack/velocypack-aliases.h>
|
#include <velocypack/velocypack-aliases.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief return a pointer to the system database or nullptr on error
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
arangodb::SystemDatabaseFeature::ptr getSystemDatabase() {
|
||||||
|
auto* feature = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||||
|
arangodb::SystemDatabaseFeature
|
||||||
|
>();
|
||||||
|
|
||||||
|
if (!feature) {
|
||||||
|
LOG_TOPIC(WARN, arangodb::Logger::AUTHENTICATION)
|
||||||
|
<< "failure to find feature '" << arangodb::SystemDatabaseFeature::name() << "' while getting the system database";
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature->use();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
using namespace arangodb::basics;
|
using namespace arangodb::basics;
|
||||||
using namespace arangodb::velocypack;
|
using namespace arangodb::velocypack;
|
||||||
|
@ -100,8 +123,9 @@ static auth::UserMap ParseUsers(VPackSlice const& slice) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::shared_ptr<VPackBuilder> QueryAllUsers(
|
static std::shared_ptr<VPackBuilder> QueryAllUsers(
|
||||||
aql::QueryRegistry* queryRegistry) {
|
aql::QueryRegistry* queryRegistry
|
||||||
TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->systemDatabase();
|
) {
|
||||||
|
auto vocbase = getSystemDatabase();
|
||||||
|
|
||||||
if (vocbase == nullptr) {
|
if (vocbase == nullptr) {
|
||||||
LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "system database is unknown";
|
LOG_TOPIC(DEBUG, arangodb::Logger::FIXME) << "system database is unknown";
|
||||||
|
@ -234,7 +258,7 @@ Result auth::UserManager::storeUserInternal(auth::User const& entry, bool replac
|
||||||
bool hasRev = data.slice().hasKey(StaticStrings::RevString);
|
bool hasRev = data.slice().hasKey(StaticStrings::RevString);
|
||||||
TRI_ASSERT((replace && hasKey && hasRev) || (!replace && !hasKey && !hasRev));
|
TRI_ASSERT((replace && hasKey && hasRev) || (!replace && !hasKey && !hasRev));
|
||||||
|
|
||||||
TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->systemDatabase();
|
auto vocbase = getSystemDatabase();
|
||||||
|
|
||||||
if (vocbase == nullptr) {
|
if (vocbase == nullptr) {
|
||||||
return Result(TRI_ERROR_INTERNAL, "unable to find system database");
|
return Result(TRI_ERROR_INTERNAL, "unable to find system database");
|
||||||
|
@ -550,7 +574,7 @@ VPackBuilder auth::UserManager::serializeUser(std::string const& user) {
|
||||||
|
|
||||||
static Result RemoveUserInternal(auth::User const& entry) {
|
static Result RemoveUserInternal(auth::User const& entry) {
|
||||||
TRI_ASSERT(!entry.key().empty());
|
TRI_ASSERT(!entry.key().empty());
|
||||||
TRI_vocbase_t* vocbase = DatabaseFeature::DATABASE->systemDatabase();
|
auto vocbase = getSystemDatabase();
|
||||||
|
|
||||||
if (vocbase == nullptr) {
|
if (vocbase == nullptr) {
|
||||||
return Result(TRI_ERROR_INTERNAL, "unable to find system database");
|
return Result(TRI_ERROR_INTERNAL, "unable to find system database");
|
||||||
|
|
|
@ -261,7 +261,7 @@ arangodb::SystemDatabaseFeature::ptr getSystemDatabase() {
|
||||||
|
|
||||||
if (!database) {
|
if (!database) {
|
||||||
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
|
LOG_TOPIC(WARN, arangodb::iresearch::TOPIC)
|
||||||
<< "failure to find feature 'SystemDatabase' while getting the system database";
|
<< "failure to find feature '" << arangodb::SystemDatabaseFeature::name() << "' while getting the system database";
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "Rest/HttpRequest.h"
|
#include "Rest/HttpRequest.h"
|
||||||
#include "Rest/Version.h"
|
#include "Rest/Version.h"
|
||||||
#include "RestServer/DatabaseFeature.h"
|
#include "RestServer/DatabaseFeature.h"
|
||||||
|
#include "Utils/CollectionNameResolver.h"
|
||||||
#include "Utils/ExecContext.h"
|
#include "Utils/ExecContext.h"
|
||||||
#include "VocBase/LogicalCollection.h"
|
#include "VocBase/LogicalCollection.h"
|
||||||
#include "VocBase/Methods/Collections.h"
|
#include "VocBase/Methods/Collections.h"
|
||||||
|
@ -35,6 +36,48 @@
|
||||||
#include <velocypack/Collection.h>
|
#include <velocypack/Collection.h>
|
||||||
#include <velocypack/velocypack-aliases.h>
|
#include <velocypack/velocypack-aliases.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @return a collection exists in database or a wildcard was specified
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
arangodb::Result existsCollection(
|
||||||
|
std::string const& database, std::string const& collection
|
||||||
|
) {
|
||||||
|
auto* databaseFeature = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||||
|
arangodb::DatabaseFeature
|
||||||
|
>("Database");
|
||||||
|
|
||||||
|
if (!databaseFeature) {
|
||||||
|
return arangodb::Result(
|
||||||
|
TRI_ERROR_INTERNAL, "failure to find feature 'Database'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string wildcard("*");
|
||||||
|
|
||||||
|
if (wildcard == database) {
|
||||||
|
return arangodb::Result(); // wildcard always matches
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* vocbase = databaseFeature->lookupDatabase(database);
|
||||||
|
|
||||||
|
if (!vocbase) {
|
||||||
|
return arangodb::Result(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wildcard == collection) {
|
||||||
|
return arangodb::Result(); // wildcard always matches
|
||||||
|
}
|
||||||
|
|
||||||
|
return !arangodb::CollectionNameResolver(*vocbase).getCollection(collection)
|
||||||
|
? arangodb::Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND)
|
||||||
|
: arangodb::Result()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
using namespace arangodb::basics;
|
using namespace arangodb::basics;
|
||||||
using namespace arangodb::rest;
|
using namespace arangodb::rest;
|
||||||
|
@ -340,18 +383,33 @@ RestStatus RestUsersHandler::putRequest(auth::UserManager* um) {
|
||||||
} else if (suffixes.size() == 3 || suffixes.size() == 4) {
|
} else if (suffixes.size() == 3 || suffixes.size() == 4) {
|
||||||
// update database / collection permissions
|
// update database / collection permissions
|
||||||
std::string const& name = suffixes[0];
|
std::string const& name = suffixes[0];
|
||||||
|
|
||||||
if (suffixes[1] == "database") {
|
if (suffixes[1] == "database") {
|
||||||
// update a user's permissions
|
// update a user's permissions
|
||||||
std::string const& db = suffixes[2];
|
std::string const& db = suffixes[2];
|
||||||
std::string coll = suffixes.size() == 4 ? suffixes[3] : "";
|
std::string coll = suffixes.size() == 4 ? suffixes[3] : "";
|
||||||
|
|
||||||
if (!isAdminUser()) {
|
if (!isAdminUser()) {
|
||||||
generateError(rest::ResponseCode::FORBIDDEN, TRI_ERROR_FORBIDDEN);
|
generateError(rest::ResponseCode::FORBIDDEN, TRI_ERROR_FORBIDDEN);
|
||||||
|
|
||||||
return RestStatus::DONE;
|
return RestStatus::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate that the collection is present
|
||||||
|
if (suffixes.size() > 3) {
|
||||||
|
auto res = existsCollection(db, coll);
|
||||||
|
|
||||||
|
if (!res.ok()) {
|
||||||
|
generateError(res);
|
||||||
|
|
||||||
|
return RestStatus::DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!body.isObject() ||
|
if (!body.isObject() ||
|
||||||
!body.get("grant").isString()) {
|
!body.get("grant").isString()) {
|
||||||
generateError(rest::ResponseCode::BAD, TRI_ERROR_BAD_PARAMETER);
|
generateError(rest::ResponseCode::BAD, TRI_ERROR_BAD_PARAMETER);
|
||||||
|
|
||||||
return RestStatus::DONE;
|
return RestStatus::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +418,9 @@ RestStatus RestUsersHandler::putRequest(auth::UserManager* um) {
|
||||||
|
|
||||||
// contains response in case of success
|
// contains response in case of success
|
||||||
VPackBuilder b;
|
VPackBuilder b;
|
||||||
|
|
||||||
b(VPackValue(VPackValueType::Object));
|
b(VPackValue(VPackValueType::Object));
|
||||||
|
|
||||||
Result r = um->updateUser(name, [&](auth::User& entry) {
|
Result r = um->updateUser(name, [&](auth::User& entry) {
|
||||||
if (coll.empty()) {
|
if (coll.empty()) {
|
||||||
entry.grantDatabase(db, lvl);
|
entry.grantDatabase(db, lvl);
|
||||||
|
@ -369,14 +429,15 @@ RestStatus RestUsersHandler::putRequest(auth::UserManager* um) {
|
||||||
entry.grantCollection(db, coll, lvl);
|
entry.grantCollection(db, coll, lvl);
|
||||||
b(db + "/" + coll, VPackValue(convertFromAuthLevel(lvl)))();
|
b(db + "/" + coll, VPackValue(convertFromAuthLevel(lvl)))();
|
||||||
}
|
}
|
||||||
return TRI_ERROR_NO_ERROR;
|
|
||||||
|
return TRI_ERROR_NO_ERROR;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (r.ok()) {
|
if (r.ok()) {
|
||||||
generateUserResult(ResponseCode::OK, b);
|
generateUserResult(ResponseCode::OK, b);
|
||||||
} else {
|
} else {
|
||||||
generateError(r);
|
generateError(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (suffixes[1] == "config") {
|
} else if (suffixes[1] == "config") {
|
||||||
// update internal config data, used in the admin dashboard
|
// update internal config data, used in the admin dashboard
|
||||||
if (!canAccessUser(name)) {
|
if (!canAccessUser(name)) {
|
||||||
|
@ -495,19 +556,34 @@ RestStatus RestUsersHandler::deleteRequest(auth::UserManager* um) {
|
||||||
// revoke a user's permissions
|
// revoke a user's permissions
|
||||||
std::string const& db = suffixes[2];
|
std::string const& db = suffixes[2];
|
||||||
std::string coll = suffixes.size() == 4 ? suffixes[3] : "";
|
std::string coll = suffixes.size() == 4 ? suffixes[3] : "";
|
||||||
|
|
||||||
if (!isAdminUser()) {
|
if (!isAdminUser()) {
|
||||||
generateError(rest::ResponseCode::FORBIDDEN, TRI_ERROR_FORBIDDEN);
|
generateError(rest::ResponseCode::FORBIDDEN, TRI_ERROR_FORBIDDEN);
|
||||||
|
|
||||||
return RestStatus::DONE;
|
return RestStatus::DONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validate that the collection is present
|
||||||
|
if (suffixes.size() > 3) {
|
||||||
|
auto res = existsCollection(db, coll);
|
||||||
|
|
||||||
|
if (!res.ok()) {
|
||||||
|
generateError(res);
|
||||||
|
|
||||||
|
return RestStatus::DONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result r = um->updateUser(user, [&](auth::User& entry) {
|
Result r = um->updateUser(user, [&](auth::User& entry) {
|
||||||
if (coll.empty()) {
|
if (coll.empty()) {
|
||||||
entry.removeDatabase(db);
|
entry.removeDatabase(db);
|
||||||
} else {
|
} else {
|
||||||
entry.removeCollection(db, coll);
|
entry.removeCollection(db, coll);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (r.ok()) {
|
if (r.ok()) {
|
||||||
VPackBuilder b;
|
VPackBuilder b;
|
||||||
b(VPackValue(VPackValueType::Object))(StaticStrings::Error, VPackValue(false))(
|
b(VPackValue(VPackValueType::Object))(StaticStrings::Error, VPackValue(false))(
|
||||||
|
|
|
@ -44,6 +44,48 @@
|
||||||
#include <velocypack/Slice.h>
|
#include <velocypack/Slice.h>
|
||||||
#include <velocypack/velocypack-aliases.h>
|
#include <velocypack/velocypack-aliases.h>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @return a collection exists in database or a wildcard was specified
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
arangodb::Result existsCollection(
|
||||||
|
std::string const& database, std::string const& collection
|
||||||
|
) {
|
||||||
|
auto* databaseFeature = arangodb::application_features::ApplicationServer::lookupFeature<
|
||||||
|
arangodb::DatabaseFeature
|
||||||
|
>("Database");
|
||||||
|
|
||||||
|
if (!databaseFeature) {
|
||||||
|
return arangodb::Result(
|
||||||
|
TRI_ERROR_INTERNAL, "failure to find feature 'Database'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::string wildcard("*");
|
||||||
|
|
||||||
|
if (wildcard == database) {
|
||||||
|
return arangodb::Result(); // wildcard always matches
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* vocbase = databaseFeature->lookupDatabase(database);
|
||||||
|
|
||||||
|
if (!vocbase) {
|
||||||
|
return arangodb::Result(TRI_ERROR_ARANGO_DATABASE_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wildcard == collection) {
|
||||||
|
return arangodb::Result(); // wildcard always matches
|
||||||
|
}
|
||||||
|
|
||||||
|
return !arangodb::CollectionNameResolver(*vocbase).getCollection(collection)
|
||||||
|
? arangodb::Result(TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND)
|
||||||
|
: arangodb::Result()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
using namespace arangodb::basics;
|
using namespace arangodb::basics;
|
||||||
using namespace arangodb::rest;
|
using namespace arangodb::rest;
|
||||||
|
@ -302,33 +344,48 @@ static void JS_GrantCollection(
|
||||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
|
||||||
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
||||||
!args[2]->IsString()) {
|
!args[2]->IsString()) {
|
||||||
TRI_V8_THROW_EXCEPTION_USAGE("grantCollection(username, db, coll[, type])");
|
TRI_V8_THROW_EXCEPTION_USAGE("grantCollection(username, db, coll[, type])");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsAdminUser()) {
|
if (!IsAdminUser()) {
|
||||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN);
|
TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
auth::UserManager* um = AuthenticationFeature::instance()->userManager();
|
auth::UserManager* um = AuthenticationFeature::instance()->userManager();
|
||||||
|
|
||||||
if (um == nullptr) {
|
if (um == nullptr) {
|
||||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED,
|
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED,
|
||||||
"user are not supported on this server");
|
"user are not supported on this server");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string username = TRI_ObjectToString(args[0]);
|
std::string username = TRI_ObjectToString(args[0]);
|
||||||
std::string db = TRI_ObjectToString(args[1]);
|
std::string db = TRI_ObjectToString(args[1]);
|
||||||
std::string coll = TRI_ObjectToString(args[2]);
|
std::string coll = TRI_ObjectToString(args[2]);
|
||||||
|
|
||||||
|
// validate that the collection is present
|
||||||
|
{
|
||||||
|
auto res = existsCollection(db, coll);
|
||||||
|
|
||||||
|
if (!res.ok()) {
|
||||||
|
TRI_V8_THROW_EXCEPTION(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auth::Level lvl = auth::Level::RW;
|
auth::Level lvl = auth::Level::RW;
|
||||||
|
|
||||||
if (args.Length() >= 4) {
|
if (args.Length() >= 4) {
|
||||||
std::string type = TRI_ObjectToString(args[3]);
|
std::string type = TRI_ObjectToString(args[3]);
|
||||||
lvl = auth::convertToAuthLevel(type);
|
lvl = auth::convertToAuthLevel(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result r = um->updateUser(username, [&](auth::User& entry) {
|
Result r = um->updateUser(username, [&](auth::User& entry) {
|
||||||
entry.grantCollection(db, coll, lvl);
|
entry.grantCollection(db, coll, lvl);
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!r.ok()) {
|
if (!r.ok()) {
|
||||||
TRI_V8_THROW_EXCEPTION(r);
|
TRI_V8_THROW_EXCEPTION(r);
|
||||||
}
|
}
|
||||||
|
@ -341,14 +398,16 @@ static void JS_RevokeCollection(
|
||||||
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
v8::FunctionCallbackInfo<v8::Value> const& args) {
|
||||||
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
TRI_V8_TRY_CATCH_BEGIN(isolate);
|
||||||
v8::HandleScope scope(isolate);
|
v8::HandleScope scope(isolate);
|
||||||
|
|
||||||
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
if (args.Length() < 3 || !args[0]->IsString() || !args[1]->IsString() ||
|
||||||
!args[2]->IsString()) {
|
!args[2]->IsString()) {
|
||||||
TRI_V8_THROW_EXCEPTION_USAGE("revokeCollection(username, db, coll)");
|
TRI_V8_THROW_EXCEPTION_USAGE("revokeCollection(username, db, coll)");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsAdminUser()) {
|
if (!IsAdminUser()) {
|
||||||
TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN);
|
TRI_V8_THROW_EXCEPTION(TRI_ERROR_FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
auth::UserManager* um = AuthenticationFeature::instance()->userManager();
|
auth::UserManager* um = AuthenticationFeature::instance()->userManager();
|
||||||
if (um == nullptr) {
|
if (um == nullptr) {
|
||||||
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED,
|
TRI_V8_THROW_EXCEPTION_MESSAGE(TRI_ERROR_NOT_IMPLEMENTED,
|
||||||
|
@ -358,12 +417,22 @@ static void JS_RevokeCollection(
|
||||||
std::string username = TRI_ObjectToString(args[0]);
|
std::string username = TRI_ObjectToString(args[0]);
|
||||||
std::string db = TRI_ObjectToString(args[1]);
|
std::string db = TRI_ObjectToString(args[1]);
|
||||||
std::string coll = TRI_ObjectToString(args[2]);
|
std::string coll = TRI_ObjectToString(args[2]);
|
||||||
|
|
||||||
|
// validate that the collection is present
|
||||||
|
{
|
||||||
|
auto res = existsCollection(db, coll);
|
||||||
|
|
||||||
|
if (!res.ok()) {
|
||||||
|
TRI_V8_THROW_EXCEPTION(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Result r = um->updateUser(
|
Result r = um->updateUser(
|
||||||
username, [&](auth::User& entry) {
|
username, [&](auth::User& entry) {
|
||||||
entry.removeCollection(db, coll);
|
entry.removeCollection(db, coll);
|
||||||
return TRI_ERROR_NO_ERROR;
|
return TRI_ERROR_NO_ERROR;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!r.ok()) {
|
if (!r.ok()) {
|
||||||
TRI_V8_THROW_EXCEPTION(r);
|
TRI_V8_THROW_EXCEPTION(r);
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,8 +55,10 @@ if (USE_IRESEARCH)
|
||||||
IResearch/StorageEngineMock.cpp
|
IResearch/StorageEngineMock.cpp
|
||||||
IResearch/IResearchViewNode-test.cpp
|
IResearch/IResearchViewNode-test.cpp
|
||||||
IResearch/VelocyPackHelper-test.cpp
|
IResearch/VelocyPackHelper-test.cpp
|
||||||
|
RestHandler/RestUsersHandler-test.cpp
|
||||||
RestHandler/RestViewHandler-test.cpp
|
RestHandler/RestViewHandler-test.cpp
|
||||||
Utils/CollectionNameResolver-test.cpp
|
Utils/CollectionNameResolver-test.cpp
|
||||||
|
V8Server/v8-users-test.cpp
|
||||||
V8Server/v8-views-test.cpp
|
V8Server/v8-views-test.cpp
|
||||||
VocBase/LogicalDataSource-test.cpp
|
VocBase/LogicalDataSource-test.cpp
|
||||||
VocBase/vocbase-test.cpp
|
VocBase/vocbase-test.cpp
|
||||||
|
|
|
@ -39,6 +39,8 @@ struct GeneralRequestMock: public arangodb::GeneralRequest {
|
||||||
arangodb::velocypack::Builder _payload; // request body
|
arangodb::velocypack::Builder _payload; // request body
|
||||||
|
|
||||||
GeneralRequestMock(TRI_vocbase_t& vocbase);
|
GeneralRequestMock(TRI_vocbase_t& vocbase);
|
||||||
|
using arangodb::GeneralRequest::addSuffix;
|
||||||
|
void addSuffix(std::string const& part) { addSuffix(std::string(part)); }
|
||||||
virtual size_t contentLength() const override;
|
virtual size_t contentLength() const override;
|
||||||
virtual arangodb::StringRef rawPayload() const override;
|
virtual arangodb::StringRef rawPayload() const override;
|
||||||
virtual arangodb::velocypack::Slice payload(arangodb::velocypack::Options const* options = &arangodb::velocypack::Options::Defaults) override;
|
virtual arangodb::velocypack::Slice payload(arangodb::velocypack::Options const* options = &arangodb::velocypack::Options::Defaults) override;
|
||||||
|
@ -55,4 +57,4 @@ struct GeneralResponseMock: public arangodb::GeneralResponse {
|
||||||
virtual arangodb::Endpoint::TransportType transportType() override;
|
virtual arangodb::Endpoint::TransportType transportType() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -861,8 +861,10 @@ arangodb::Result PhysicalCollectionMock::remove(arangodb::transaction::Methods*
|
||||||
|
|
||||||
arangodb::Result PhysicalCollectionMock::replace(arangodb::transaction::Methods* trx, arangodb::velocypack::Slice const newSlice, arangodb::ManagedDocumentResult& result, arangodb::OperationOptions& options, TRI_voc_tick_t& resultMarkerTick, bool lock, TRI_voc_rid_t& prevRev, arangodb::ManagedDocumentResult& previous) {
|
arangodb::Result PhysicalCollectionMock::replace(arangodb::transaction::Methods* trx, arangodb::velocypack::Slice const newSlice, arangodb::ManagedDocumentResult& result, arangodb::OperationOptions& options, TRI_voc_tick_t& resultMarkerTick, bool lock, TRI_voc_rid_t& prevRev, arangodb::ManagedDocumentResult& previous) {
|
||||||
before();
|
before();
|
||||||
TRI_ASSERT(false);
|
|
||||||
return TRI_ERROR_INTERNAL;
|
auto key = newSlice.get(arangodb::StaticStrings::KeyString);
|
||||||
|
|
||||||
|
return update(trx, newSlice, result, options, resultMarkerTick, lock, prevRev, previous, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
int PhysicalCollectionMock::restoreIndex(arangodb::transaction::Methods*, arangodb::velocypack::Slice const&, std::shared_ptr<arangodb::Index>&) {
|
int PhysicalCollectionMock::restoreIndex(arangodb::transaction::Methods*, arangodb::velocypack::Slice const&, std::shared_ptr<arangodb::Index>&) {
|
||||||
|
@ -1529,4 +1531,4 @@ bool TransactionStateMock::hasFailedOperations() const {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- END-OF-FILE
|
// --SECTION-- END-OF-FILE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
|
@ -0,0 +1,439 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// @author Andrey Abramov
|
||||||
|
/// @author Vasiliy Nabatchikov
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include <iostream>
|
||||||
|
#include "catch.hpp"
|
||||||
|
#include "../IResearch/RestHandlerMock.h"
|
||||||
|
#include "../IResearch/StorageEngineMock.h"
|
||||||
|
#include "Aql/QueryRegistry.h"
|
||||||
|
#include "Basics/StaticStrings.h"
|
||||||
|
|
||||||
|
#if USE_ENTERPRISE
|
||||||
|
#include "Enterprise/Ldap/LdapFeature.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
|
#include "RestHandler/RestUsersHandler.h"
|
||||||
|
#include "RestServer/DatabaseFeature.h"
|
||||||
|
#include "RestServer/QueryRegistryFeature.h"
|
||||||
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
|
#include "RestServer/ViewTypesFeature.h"
|
||||||
|
#include "RestServer/VocbaseContext.h"
|
||||||
|
#include "Sharding/ShardingFeature.h"
|
||||||
|
#include "StorageEngine/EngineSelectorFeature.h"
|
||||||
|
#include "Utils/ExecContext.h"
|
||||||
|
#include "V8Server/V8DealerFeature.h"
|
||||||
|
#include "velocypack/Parser.h"
|
||||||
|
#include "VocBase/LogicalCollection.h"
|
||||||
|
#include "VocBase/LogicalView.h"
|
||||||
|
#include "VocBase/vocbase.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct TestView: public arangodb::LogicalView {
|
||||||
|
arangodb::Result _appendVelocyPackResult;
|
||||||
|
arangodb::velocypack::Builder _properties;
|
||||||
|
|
||||||
|
TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion)
|
||||||
|
: arangodb::LogicalView(vocbase, definition, planVersion) {
|
||||||
|
}
|
||||||
|
virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override {
|
||||||
|
builder.add("properties", _properties.slice());
|
||||||
|
return _appendVelocyPackResult;
|
||||||
|
}
|
||||||
|
virtual arangodb::Result drop() override { return arangodb::Result(); }
|
||||||
|
static std::shared_ptr<LogicalView> make(
|
||||||
|
TRI_vocbase_t& vocbase,
|
||||||
|
arangodb::velocypack::Slice const& definition,
|
||||||
|
bool isNew,
|
||||||
|
uint64_t planVersion,
|
||||||
|
arangodb::LogicalView::PreCommitCallback const& preCommit
|
||||||
|
) {
|
||||||
|
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
|
||||||
|
return preCommit(view) ? view : nullptr;
|
||||||
|
}
|
||||||
|
virtual void open() override {}
|
||||||
|
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
|
||||||
|
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
|
||||||
|
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- setup / tear-down
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct RestUsersHandlerSetup {
|
||||||
|
StorageEngineMock engine;
|
||||||
|
arangodb::application_features::ApplicationServer server;
|
||||||
|
std::unique_ptr<TRI_vocbase_t> system;
|
||||||
|
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
|
||||||
|
|
||||||
|
RestUsersHandlerSetup(): engine(server), server(nullptr, nullptr) {
|
||||||
|
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
||||||
|
|
||||||
|
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
|
||||||
|
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
|
||||||
|
|
||||||
|
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for VocbaseContext
|
||||||
|
features.emplace_back(new arangodb::DatabaseFeature(server), false); // required for UserManager::updateUser(...)
|
||||||
|
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for TRI_vocbase_t
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(features.back().first); // need QueryRegistryFeature feature to be added now in order to create the system database
|
||||||
|
system = std::make_unique<TRI_vocbase_t>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE);
|
||||||
|
features.emplace_back(new arangodb::ShardingFeature(server), false); // required for LogicalCollection::LogicalCollection(...)
|
||||||
|
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
|
||||||
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for LogicalView::create(...)
|
||||||
|
|
||||||
|
#if USE_ENTERPRISE
|
||||||
|
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(
|
||||||
|
new arangodb::V8DealerFeature(server)
|
||||||
|
); // add without calling prepare(), required for DatabaseFeature::createDatabase(...)
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(f.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
f.first->prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
if (f.second) {
|
||||||
|
f.first->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* viewTypesFeature =
|
||||||
|
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::ViewTypesFeature>();
|
||||||
|
|
||||||
|
viewTypesFeature->emplace(
|
||||||
|
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
|
||||||
|
TestView::make
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
~RestUsersHandlerSetup() {
|
||||||
|
system.reset(); // destroy before reseting the 'ENGINE'
|
||||||
|
arangodb::application_features::ApplicationServer::server = nullptr;
|
||||||
|
|
||||||
|
// destroy application features
|
||||||
|
for (auto& f : features) {
|
||||||
|
if (f.second) {
|
||||||
|
f.first->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f : features) {
|
||||||
|
f.first->unprepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
arangodb::EngineSelectorFeature::ENGINE = nullptr; // nullify only after DatabaseFeature::unprepare()
|
||||||
|
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- test suite
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief setup
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TEST_CASE("RestUsersHandlerTest", "[rest]") {
|
||||||
|
RestUsersHandlerSetup s;
|
||||||
|
(void)(s);
|
||||||
|
|
||||||
|
SECTION("test_collection_auth") {
|
||||||
|
auto usersJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"_users\", \"isSystem\": true }");
|
||||||
|
static const std::string userName("testUser");
|
||||||
|
auto* databaseFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabaseFeature>("Database");
|
||||||
|
TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature
|
||||||
|
REQUIRE((TRI_ERROR_NO_ERROR == databaseFeature->createDatabase(1, "testDatabase", vocbase)));
|
||||||
|
auto grantRequestPtr = std::make_unique<GeneralRequestMock>(*vocbase);
|
||||||
|
auto& grantRequest = *grantRequestPtr;
|
||||||
|
auto grantResponcePtr = std::make_unique<GeneralResponseMock>();
|
||||||
|
auto& grantResponce = *grantResponcePtr;
|
||||||
|
auto grantWildcardRequestPtr = std::make_unique<GeneralRequestMock>(*vocbase);
|
||||||
|
auto& grantWildcardRequest = *grantWildcardRequestPtr;
|
||||||
|
auto grantWildcardResponcePtr = std::make_unique<GeneralResponseMock>();
|
||||||
|
auto& grantWildcardResponce = *grantWildcardResponcePtr;
|
||||||
|
auto revokeRequestPtr = std::make_unique<GeneralRequestMock>(*vocbase);
|
||||||
|
auto& revokeRequest = *revokeRequestPtr;
|
||||||
|
auto revokeResponcePtr = std::make_unique<GeneralResponseMock>();
|
||||||
|
auto& revokeResponce = *revokeResponcePtr;
|
||||||
|
auto revokeWildcardRequestPtr = std::make_unique<GeneralRequestMock>(*vocbase);
|
||||||
|
auto& revokeWildcardRequest = *revokeWildcardRequestPtr;
|
||||||
|
auto revokeWildcardResponcePtr = std::make_unique<GeneralResponseMock>();
|
||||||
|
auto& revokeWildcardResponce = *revokeWildcardResponcePtr;
|
||||||
|
arangodb::RestUsersHandler grantHandler(grantRequestPtr.release(), grantResponcePtr.release());
|
||||||
|
arangodb::RestUsersHandler grantWildcardHandler(grantWildcardRequestPtr.release(), grantWildcardResponcePtr.release());
|
||||||
|
arangodb::RestUsersHandler revokeHandler(revokeRequestPtr.release(), revokeResponcePtr.release());
|
||||||
|
arangodb::RestUsersHandler revokeWildcardHandler(revokeWildcardRequestPtr.release(), revokeWildcardResponcePtr.release());
|
||||||
|
|
||||||
|
grantRequest.addSuffix("testUser");
|
||||||
|
grantRequest.addSuffix("database");
|
||||||
|
grantRequest.addSuffix(vocbase->name());
|
||||||
|
grantRequest.addSuffix("testDataSource");
|
||||||
|
grantRequest.setRequestType(arangodb::rest::RequestType::PUT);
|
||||||
|
grantRequest._payload.openObject();
|
||||||
|
grantRequest._payload.add("grant", arangodb::velocypack::Value(arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW)));
|
||||||
|
grantRequest._payload.close();
|
||||||
|
|
||||||
|
grantWildcardRequest.addSuffix("testUser");
|
||||||
|
grantWildcardRequest.addSuffix("database");
|
||||||
|
grantWildcardRequest.addSuffix(vocbase->name());
|
||||||
|
grantWildcardRequest.addSuffix("*");
|
||||||
|
grantWildcardRequest.setRequestType(arangodb::rest::RequestType::PUT);
|
||||||
|
grantWildcardRequest._payload.openObject();
|
||||||
|
grantWildcardRequest._payload.add("grant", arangodb::velocypack::Value(arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW)));
|
||||||
|
grantWildcardRequest._payload.close();
|
||||||
|
|
||||||
|
revokeRequest.addSuffix("testUser");
|
||||||
|
revokeRequest.addSuffix("database");
|
||||||
|
revokeRequest.addSuffix(vocbase->name());
|
||||||
|
revokeRequest.addSuffix("testDataSource");
|
||||||
|
revokeRequest.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
|
||||||
|
|
||||||
|
revokeWildcardRequest.addSuffix("testUser");
|
||||||
|
revokeWildcardRequest.addSuffix("database");
|
||||||
|
revokeWildcardRequest.addSuffix(vocbase->name());
|
||||||
|
revokeWildcardRequest.addSuffix("*");
|
||||||
|
revokeWildcardRequest.setRequestType(arangodb::rest::RequestType::DELETE_REQ);
|
||||||
|
|
||||||
|
struct ExecContext: public arangodb::ExecContext {
|
||||||
|
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, userName, "",
|
||||||
|
arangodb::auth::Level::RW, arangodb::auth::Level::NONE) {} // ExecContext::isAdminUser() == true
|
||||||
|
} execContext;
|
||||||
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
|
auto* userManager = authFeature->userManager();
|
||||||
|
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
||||||
|
userManager->setGlobalVersion(0); // required for UserManager::loadFromDB()
|
||||||
|
userManager->setQueryRegistry(&queryRegistry);
|
||||||
|
|
||||||
|
// test auth missing (grant)
|
||||||
|
{
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = grantHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::NOT_FOUND == grantResponce.responseCode()));
|
||||||
|
auto slice = grantResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth missing (revoke)
|
||||||
|
{
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = revokeHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::NOT_FOUND == revokeResponce.responseCode()));
|
||||||
|
auto slice = revokeResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // not modified from above
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth collection (grant)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = grantHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::OK == grantResponce.responseCode()));
|
||||||
|
auto slice = grantResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(vocbase->name() + "/testDataSource") && slice.get(vocbase->name() + "/testDataSource").isString() && arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW) == slice.get(vocbase->name() + "/testDataSource").copyString()));
|
||||||
|
CHECK((arangodb::auth::Level::RW == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth collection (revoke)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = revokeHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::ACCEPTED == revokeResponce.responseCode()));
|
||||||
|
auto slice = revokeResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::ACCEPTED) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth view (grant)
|
||||||
|
{
|
||||||
|
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\", \"type\": \"testViewType\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalView = std::shared_ptr<arangodb::LogicalView>(vocbase->createView(viewJson->slice()).get(), [vocbase](arangodb::LogicalView* ptr)->void{ vocbase->dropView(ptr->id(), false); });
|
||||||
|
REQUIRE((false == !logicalView));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = grantHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::NOT_FOUND == grantResponce.responseCode()));
|
||||||
|
auto slice = grantResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth view (revoke)
|
||||||
|
{
|
||||||
|
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\", \"type\": \"testViewType\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalView = std::shared_ptr<arangodb::LogicalView>(vocbase->createView(viewJson->slice()).get(), [vocbase](arangodb::LogicalView* ptr)->void{ vocbase->dropView(ptr->id(), false); });
|
||||||
|
REQUIRE((false == !logicalView));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = revokeHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::NOT_FOUND == revokeResponce.responseCode()));
|
||||||
|
auto slice = revokeResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::NOT_FOUND) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && true == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // not modified from above
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth wildcard (grant)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = grantWildcardHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::OK == grantWildcardResponce.responseCode()));
|
||||||
|
auto slice = grantWildcardResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));std::cerr << "|" << slice.toString() << "|" << std::endl;
|
||||||
|
CHECK((slice.hasKey(vocbase->name() + "/*") && slice.get(vocbase->name() + "/*").isString() && arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW) == slice.get(vocbase->name() + "/*").copyString()));
|
||||||
|
CHECK((arangodb::auth::Level::RW == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth wildcard (revoke)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
auto status = revokeWildcardHandler.execute();
|
||||||
|
CHECK((arangodb::RestStatus::DONE == status));
|
||||||
|
CHECK((arangodb::rest::ResponseCode::ACCEPTED == revokeWildcardResponce.responseCode()));
|
||||||
|
auto slice = revokeWildcardResponce._payload.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Code) && slice.get(arangodb::StaticStrings::Code).isNumber<size_t>() && size_t(arangodb::rest::ResponseCode::ACCEPTED) == slice.get(arangodb::StaticStrings::Code).getNumber<size_t>()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::Error) && slice.get(arangodb::StaticStrings::Error).isBoolean() && false == slice.get(arangodb::StaticStrings::Error).getBoolean()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // unchanged since revocation is only for exactly matching collection names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief generate tests
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- END-OF-FILE
|
||||||
|
// -----------------------------------------------------------------------------
|
|
@ -0,0 +1,455 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// DISCLAIMER
|
||||||
|
///
|
||||||
|
/// Copyright 2018 ArangoDB GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
/// you may not use this file except in compliance with the License.
|
||||||
|
/// You may obtain a copy of the License at
|
||||||
|
///
|
||||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
///
|
||||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
/// See the License for the specific language governing permissions and
|
||||||
|
/// limitations under the License.
|
||||||
|
///
|
||||||
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
||||||
|
///
|
||||||
|
/// @author Andrey Abramov
|
||||||
|
/// @author Vasiliy Nabatchikov
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#include "src/api.h" // must inclide V8 _before_ "catch.cpp' or CATCH() macro will be broken
|
||||||
|
#include "src/objects-inl.h" // (required to avoid compile warnings) must inclide V8 _before_ "catch.cpp' or CATCH() macro will be broken
|
||||||
|
#include "src/objects/scope-info.h" // must inclide V8 _before_ "catch.cpp' or CATCH() macro will be broken
|
||||||
|
|
||||||
|
#include "catch.hpp"
|
||||||
|
#include "../IResearch/common.h"
|
||||||
|
#include "../IResearch/StorageEngineMock.h"
|
||||||
|
#include "Aql/QueryRegistry.h"
|
||||||
|
#include "Basics/StaticStrings.h"
|
||||||
|
|
||||||
|
#if USE_ENTERPRISE
|
||||||
|
#include "Enterprise/Ldap/LdapFeature.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "GeneralServer/AuthenticationFeature.h"
|
||||||
|
#include "Replication/ReplicationFeature.h"
|
||||||
|
#include "RestServer/DatabaseFeature.h"
|
||||||
|
#include "RestServer/QueryRegistryFeature.h"
|
||||||
|
#include "RestServer/SystemDatabaseFeature.h"
|
||||||
|
#include "RestServer/ViewTypesFeature.h"
|
||||||
|
#include "Sharding/ShardingFeature.h"
|
||||||
|
#include "StorageEngine/EngineSelectorFeature.h"
|
||||||
|
#include "Utils/ExecContext.h"
|
||||||
|
#include "V8/v8-vpack.h"
|
||||||
|
#include "V8Server/v8-users.h"
|
||||||
|
#include "V8Server/V8DealerFeature.h"
|
||||||
|
#include "velocypack/Builder.h"
|
||||||
|
#include "VocBase/LogicalCollection.h"
|
||||||
|
#include "VocBase/LogicalView.h"
|
||||||
|
#include "VocBase/vocbase.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class ArrayBufferAllocator: public v8::ArrayBuffer::Allocator {
|
||||||
|
public:
|
||||||
|
virtual void* Allocate(size_t length) override {
|
||||||
|
void* data = AllocateUninitialized(length);
|
||||||
|
return data == nullptr ? data : memset(data, 0, length);
|
||||||
|
}
|
||||||
|
virtual void* AllocateUninitialized(size_t length) override {
|
||||||
|
return malloc(length);
|
||||||
|
}
|
||||||
|
virtual void Free(void* data, size_t) override {
|
||||||
|
free(data);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestView: public arangodb::LogicalView {
|
||||||
|
arangodb::Result _appendVelocyPackResult;
|
||||||
|
arangodb::velocypack::Builder _properties;
|
||||||
|
|
||||||
|
TestView(TRI_vocbase_t& vocbase, arangodb::velocypack::Slice const& definition, uint64_t planVersion)
|
||||||
|
: arangodb::LogicalView(vocbase, definition, planVersion) {
|
||||||
|
}
|
||||||
|
virtual arangodb::Result appendVelocyPack(arangodb::velocypack::Builder& builder, bool /*detailed*/, bool /*forPersistence*/) const override {
|
||||||
|
builder.add("properties", _properties.slice());
|
||||||
|
return _appendVelocyPackResult;
|
||||||
|
}
|
||||||
|
virtual arangodb::Result drop() override { return arangodb::Result(); }
|
||||||
|
static std::shared_ptr<LogicalView> make(
|
||||||
|
TRI_vocbase_t& vocbase,
|
||||||
|
arangodb::velocypack::Slice const& definition,
|
||||||
|
bool isNew,
|
||||||
|
uint64_t planVersion,
|
||||||
|
arangodb::LogicalView::PreCommitCallback const& preCommit
|
||||||
|
) {
|
||||||
|
auto view = std::make_shared<TestView>(vocbase, definition, planVersion);
|
||||||
|
return preCommit(view) ? view : nullptr;
|
||||||
|
}
|
||||||
|
virtual void open() override {}
|
||||||
|
virtual arangodb::Result rename(std::string&& newName, bool doSync) override { name(std::move(newName)); return arangodb::Result(); }
|
||||||
|
virtual arangodb::Result updateProperties(arangodb::velocypack::Slice const& properties, bool partialUpdate, bool doSync) override { _properties = arangodb::velocypack::Builder(properties); return arangodb::Result(); }
|
||||||
|
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- setup / tear-down
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
struct V8UsersSetup {
|
||||||
|
StorageEngineMock engine;
|
||||||
|
arangodb::application_features::ApplicationServer server;
|
||||||
|
std::unique_ptr<TRI_vocbase_t> system;
|
||||||
|
std::vector<std::pair<arangodb::application_features::ApplicationFeature*, bool>> features;
|
||||||
|
|
||||||
|
V8UsersSetup(): engine(server), server(nullptr, nullptr) {
|
||||||
|
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
||||||
|
|
||||||
|
arangodb::tests::v8Init(); // on-time initialize V8
|
||||||
|
|
||||||
|
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
|
||||||
|
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
|
||||||
|
|
||||||
|
features.emplace_back(new arangodb::AuthenticationFeature(server), false); // required for VocbaseContext
|
||||||
|
features.emplace_back(new arangodb::DatabaseFeature(server), false); // required for UserManager::updateUser(...)
|
||||||
|
features.emplace_back(new arangodb::QueryRegistryFeature(server), false); // required for TRI_vocbase_t
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(features.back().first); // need QueryRegistryFeature feature to be added now in order to create the system database
|
||||||
|
system = std::make_unique<TRI_vocbase_t>(TRI_vocbase_type_e::TRI_VOCBASE_TYPE_NORMAL, 0, TRI_VOC_SYSTEM_DATABASE);
|
||||||
|
features.emplace_back(new arangodb::ReplicationFeature(server), false); // required for DatabaseFeature::createDatabase(...)
|
||||||
|
features.emplace_back(new arangodb::ShardingFeature(server), false); // required for LogicalCollection::LogicalCollection(...)
|
||||||
|
features.emplace_back(new arangodb::SystemDatabaseFeature(server, system.get()), false); // required for IResearchAnalyzerFeature
|
||||||
|
features.emplace_back(new arangodb::ViewTypesFeature(server), false); // required for LogicalView::create(...)
|
||||||
|
|
||||||
|
#if USE_ENTERPRISE
|
||||||
|
features.emplace_back(new arangodb::LdapFeature(server), false); // required for AuthenticationFeature with USE_ENTERPRISE
|
||||||
|
#endif
|
||||||
|
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(
|
||||||
|
new arangodb::V8DealerFeature(server)
|
||||||
|
); // add without calling prepare(), required for DatabaseFeature::createDatabase(...)
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
arangodb::application_features::ApplicationServer::server->addFeature(f.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
f.first->prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f: features) {
|
||||||
|
if (f.second) {
|
||||||
|
f.first->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* viewTypesFeature =
|
||||||
|
arangodb::application_features::ApplicationServer::lookupFeature<arangodb::ViewTypesFeature>();
|
||||||
|
|
||||||
|
viewTypesFeature->emplace(
|
||||||
|
arangodb::LogicalDataSource::Type::emplace(arangodb::velocypack::StringRef("testViewType")),
|
||||||
|
TestView::make
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
~V8UsersSetup() {
|
||||||
|
system.reset(); // destroy before reseting the 'ENGINE'
|
||||||
|
arangodb::application_features::ApplicationServer::server = nullptr;
|
||||||
|
|
||||||
|
// destroy application features
|
||||||
|
for (auto& f : features) {
|
||||||
|
if (f.second) {
|
||||||
|
f.first->stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& f : features) {
|
||||||
|
f.first->unprepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
arangodb::EngineSelectorFeature::ENGINE = nullptr; // nullify only after DatabaseFeature::unprepare()
|
||||||
|
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::DEFAULT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- test suite
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief setup
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TEST_CASE("V8UsersTest", "[v8]") {
|
||||||
|
V8UsersSetup s;
|
||||||
|
(void)(s);
|
||||||
|
|
||||||
|
SECTION("test_collection_auth") {
|
||||||
|
auto usersJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"_users\", \"isSystem\": true }");
|
||||||
|
static const std::string userName("testUser");
|
||||||
|
auto* databaseFeature = arangodb::application_features::ApplicationServer::getFeature<arangodb::DatabaseFeature>("Database");
|
||||||
|
TRI_vocbase_t* vocbase; // will be owned by DatabaseFeature
|
||||||
|
REQUIRE((TRI_ERROR_NO_ERROR == databaseFeature->createDatabase(1, "testDatabase", vocbase)));
|
||||||
|
v8::Isolate::CreateParams isolateParams;
|
||||||
|
ArrayBufferAllocator arrayBufferAllocator;
|
||||||
|
isolateParams.array_buffer_allocator = &arrayBufferAllocator;
|
||||||
|
auto isolate = std::shared_ptr<v8::Isolate>(v8::Isolate::New(isolateParams), [](v8::Isolate* p)->void { p->Dispose(); });
|
||||||
|
REQUIRE((nullptr != isolate));
|
||||||
|
v8::Isolate::Scope isolateScope(isolate.get()); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
|
||||||
|
v8::internal::Isolate::Current()->InitializeLoggingAndCounters(); // otherwise v8::Isolate::Logger() will fail (called from v8::Exception::Error)
|
||||||
|
v8::HandleScope handleScope(isolate.get()); // required for v8::Context::New(...), v8::ObjectTemplate::New(...) and TRI_AddMethodVocbase(...)
|
||||||
|
auto context = v8::Context::New(isolate.get());
|
||||||
|
v8::Context::Scope contextScope(context); // required for TRI_AddMethodVocbase(...)
|
||||||
|
auto v8g = TRI_CreateV8Globals(isolate.get()); // create and set inside 'isolate' for use with 'TRI_GET_GLOBALS()'
|
||||||
|
v8g->ArangoErrorTempl.Reset(isolate.get(), v8::ObjectTemplate::New(isolate.get())); // otherwise v8:-utils::CreateErrorObject(...) will fail
|
||||||
|
v8g->_vocbase = vocbase;
|
||||||
|
TRI_InitV8Users(context, vocbase, v8g, isolate.get());
|
||||||
|
|
||||||
|
auto arangoUsers = v8::Local<v8::ObjectTemplate>::New(isolate.get(), v8g->UsersTempl)->NewInstance();
|
||||||
|
auto fn_grantCollection = arangoUsers->Get(TRI_V8_ASCII_STRING(isolate.get(), "grantCollection"));
|
||||||
|
CHECK((fn_grantCollection->IsFunction()));
|
||||||
|
auto fn_revokeCollection = arangoUsers->Get(TRI_V8_ASCII_STRING(isolate.get(), "revokeCollection"));
|
||||||
|
CHECK((fn_revokeCollection->IsFunction()));
|
||||||
|
std::vector<v8::Local<v8::Value>> grantArgs = {
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), userName),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), vocbase->name()),
|
||||||
|
TRI_V8_ASCII_STRING(isolate.get(), "testDataSource"),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW)),
|
||||||
|
};
|
||||||
|
std::vector<v8::Local<v8::Value>> grantWildcardArgs = {
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), userName),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), vocbase->name()),
|
||||||
|
TRI_V8_ASCII_STRING(isolate.get(), "*"),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), arangodb::auth::convertFromAuthLevel(arangodb::auth::Level::RW)),
|
||||||
|
};
|
||||||
|
std::vector<v8::Local<v8::Value>> revokeArgs = {
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), userName),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), vocbase->name()),
|
||||||
|
TRI_V8_ASCII_STRING(isolate.get(), "testDataSource"),
|
||||||
|
};
|
||||||
|
std::vector<v8::Local<v8::Value>> revokeWildcardArgs = {
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), userName),
|
||||||
|
TRI_V8_STD_STRING(isolate.get(), vocbase->name()),
|
||||||
|
TRI_V8_ASCII_STRING(isolate.get(), "*"),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ExecContext: public arangodb::ExecContext {
|
||||||
|
ExecContext(): arangodb::ExecContext(arangodb::ExecContext::Type::Default, userName, "",
|
||||||
|
arangodb::auth::Level::RW, arangodb::auth::Level::NONE) {} // ExecContext::isAdminUser() == true
|
||||||
|
} execContext;
|
||||||
|
arangodb::ExecContextScope execContextScope(&execContext);
|
||||||
|
auto* authFeature = arangodb::AuthenticationFeature::instance();
|
||||||
|
auto* userManager = authFeature->userManager();
|
||||||
|
arangodb::aql::QueryRegistry queryRegistry(0); // required for UserManager::loadFromDB()
|
||||||
|
userManager->setGlobalVersion(0); // required for UserManager::loadFromDB()
|
||||||
|
userManager->setQueryRegistry(&queryRegistry);
|
||||||
|
|
||||||
|
// test auth missing (grant)
|
||||||
|
{
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_grantCollection)->CallAsFunction(context, arangoUsers, grantArgs.size(), grantArgs.data());
|
||||||
|
CHECK((result.IsEmpty()));
|
||||||
|
CHECK((tryCatch.HasCaught()));
|
||||||
|
CHECK((TRI_ERROR_NO_ERROR == TRI_V8ToVPack(isolate.get(), responce, tryCatch.Exception(), false)));
|
||||||
|
auto slice = responce.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth missing (revoke)
|
||||||
|
{
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_revokeCollection)->CallAsFunction(context, arangoUsers, revokeArgs.size(), revokeArgs.data());
|
||||||
|
CHECK((result.IsEmpty()));
|
||||||
|
CHECK((tryCatch.HasCaught()));
|
||||||
|
CHECK((TRI_ERROR_NO_ERROR == TRI_V8ToVPack(isolate.get(), responce, tryCatch.Exception(), false)));
|
||||||
|
auto slice = responce.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // not modified from above
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth collection (grant)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_grantCollection)->CallAsFunction(context, arangoUsers, grantArgs.size(), grantArgs.data());
|
||||||
|
CHECK((!result.IsEmpty()));
|
||||||
|
CHECK((result.ToLocalChecked()->IsUndefined()));
|
||||||
|
CHECK((!tryCatch.HasCaught()));
|
||||||
|
CHECK((arangodb::auth::Level::RW == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth collection (revoke)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_revokeCollection)->CallAsFunction(context, arangoUsers, revokeArgs.size(), revokeArgs.data());
|
||||||
|
CHECK((!result.IsEmpty()));
|
||||||
|
CHECK((result.ToLocalChecked()->IsUndefined()));
|
||||||
|
CHECK((!tryCatch.HasCaught()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth view (grant)
|
||||||
|
{
|
||||||
|
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\", \"type\": \"testViewType\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalView = std::shared_ptr<arangodb::LogicalView>(vocbase->createView(viewJson->slice()).get(), [vocbase](arangodb::LogicalView* ptr)->void{ vocbase->dropView(ptr->id(), false); });
|
||||||
|
REQUIRE((false == !logicalView));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_grantCollection)->CallAsFunction(context, arangoUsers, grantArgs.size(), grantArgs.data());
|
||||||
|
CHECK((result.IsEmpty()));
|
||||||
|
CHECK((tryCatch.HasCaught()));
|
||||||
|
CHECK((TRI_ERROR_NO_ERROR == TRI_V8ToVPack(isolate.get(), responce, tryCatch.Exception(), false)));
|
||||||
|
auto slice = responce.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth view (revoke)
|
||||||
|
{
|
||||||
|
auto viewJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\", \"type\": \"testViewType\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalView = std::shared_ptr<arangodb::LogicalView>(vocbase->createView(viewJson->slice()).get(), [vocbase](arangodb::LogicalView* ptr)->void{ vocbase->dropView(ptr->id(), false); });
|
||||||
|
REQUIRE((false == !logicalView));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_revokeCollection)->CallAsFunction(context, arangoUsers, revokeArgs.size(), revokeArgs.data());
|
||||||
|
CHECK((result.IsEmpty()));
|
||||||
|
CHECK((tryCatch.HasCaught()));
|
||||||
|
CHECK((TRI_ERROR_NO_ERROR == TRI_V8ToVPack(isolate.get(), responce, tryCatch.Exception(), false)));
|
||||||
|
auto slice = responce.slice();
|
||||||
|
CHECK((slice.isObject()));
|
||||||
|
CHECK((slice.hasKey(arangodb::StaticStrings::ErrorNum) && slice.get(arangodb::StaticStrings::ErrorNum).isNumber<int>() && TRI_ERROR_ARANGO_DATA_SOURCE_NOT_FOUND == slice.get(arangodb::StaticStrings::ErrorNum).getNumber<int>()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // not modified from above
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth wildcard (grant)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::NONE == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_grantCollection)->CallAsFunction(context, arangoUsers, grantWildcardArgs.size(), grantWildcardArgs.data());
|
||||||
|
CHECK((!result.IsEmpty()));
|
||||||
|
CHECK((result.ToLocalChecked()->IsUndefined()));
|
||||||
|
CHECK((!tryCatch.HasCaught()));
|
||||||
|
CHECK((arangodb::auth::Level::RW == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// test auth wildcard (revoke)
|
||||||
|
{
|
||||||
|
auto collectionJson = arangodb::velocypack::Parser::fromJson("{ \"name\": \"testDataSource\" }");
|
||||||
|
auto scopedUsers = std::shared_ptr<arangodb::LogicalCollection>(s.system->createCollection(usersJson->slice()), [&s](arangodb::LogicalCollection* ptr)->void{ s.system->dropCollection(ptr->id(), true, 0.0); });
|
||||||
|
arangodb::auth::UserMap userMap;
|
||||||
|
arangodb::auth::User* userPtr = nullptr;
|
||||||
|
userManager->setAuthInfo(userMap); // insure an empy map is set before UserManager::storeUser(...)
|
||||||
|
userManager->storeUser(false, userName, arangodb::StaticStrings::Empty, true, arangodb::velocypack::Slice());
|
||||||
|
userManager->accessUser(userName, [&userPtr](arangodb::auth::User const& user)->arangodb::Result { userPtr = const_cast<arangodb::auth::User*>(&user); return arangodb::Result(); });
|
||||||
|
REQUIRE((nullptr != userPtr));
|
||||||
|
userPtr->grantCollection(vocbase->name(), "testDataSource", arangodb::auth::Level::RO); // for missing collections User::collectionAuthLevel(...) returns database auth::Level
|
||||||
|
auto logicalCollection = std::shared_ptr<arangodb::LogicalCollection>(vocbase->createCollection(collectionJson->slice()), [vocbase](arangodb::LogicalCollection* ptr)->void{ vocbase->dropCollection(ptr->id(), false, 0); });
|
||||||
|
REQUIRE((false == !logicalCollection));
|
||||||
|
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource")));
|
||||||
|
arangodb::velocypack::Builder responce;
|
||||||
|
v8::TryCatch tryCatch(isolate.get());
|
||||||
|
auto result = v8::Function::Cast(*fn_revokeCollection)->CallAsFunction(context, arangoUsers, revokeWildcardArgs.size(), revokeWildcardArgs.data());
|
||||||
|
CHECK((!result.IsEmpty()));
|
||||||
|
CHECK((result.ToLocalChecked()->IsUndefined()));
|
||||||
|
CHECK((!tryCatch.HasCaught()));
|
||||||
|
CHECK((arangodb::auth::Level::RO == execContext.collectionAuthLevel(vocbase->name(), "testDataSource"))); // unchanged since revocation is only for exactly matching collection names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief generate tests
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// --SECTION-- END-OF-FILE
|
||||||
|
// -----------------------------------------------------------------------------
|
|
@ -26,6 +26,7 @@
|
||||||
#include "src/objects/scope-info.h" // must inclide V8 _before_ "catch.cpp' or CATCH() macro will be broken
|
#include "src/objects/scope-info.h" // must inclide V8 _before_ "catch.cpp' or CATCH() macro will be broken
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
#include "../IResearch/common.h"
|
||||||
#include "../IResearch/StorageEngineMock.h"
|
#include "../IResearch/StorageEngineMock.h"
|
||||||
#include "Aql/QueryRegistry.h"
|
#include "Aql/QueryRegistry.h"
|
||||||
#include "Basics/StaticStrings.h"
|
#include "Basics/StaticStrings.h"
|
||||||
|
@ -93,27 +94,6 @@ struct TestView: public arangodb::LogicalView {
|
||||||
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
|
virtual bool visitCollections(CollectionVisitor const& visitor) const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// @Note: once V8 is initialized all 'CATCH' errors will result in SIGILL
|
|
||||||
void v8Init() {
|
|
||||||
struct init_t {
|
|
||||||
std::shared_ptr<v8::Platform> platform;
|
|
||||||
init_t() {
|
|
||||||
platform = std::shared_ptr<v8::Platform>(
|
|
||||||
v8::platform::CreateDefaultPlatform(),
|
|
||||||
[](v8::Platform* p)->void {
|
|
||||||
v8::V8::Dispose();
|
|
||||||
v8::V8::ShutdownPlatform();
|
|
||||||
delete p;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
v8::V8::InitializePlatform(platform.get()); // avoid SIGSEGV duing 8::Isolate::New(...)
|
|
||||||
v8::V8::Initialize(); // avoid error: "Check failed: thread_data_table_"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
static const init_t init;
|
|
||||||
(void)(init);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
@ -128,7 +108,7 @@ struct V8ViewsSetup {
|
||||||
V8ViewsSetup(): engine(server), server(nullptr, nullptr) {
|
V8ViewsSetup(): engine(server), server(nullptr, nullptr) {
|
||||||
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
arangodb::EngineSelectorFeature::ENGINE = &engine;
|
||||||
|
|
||||||
v8Init(); // on-time initialize V8
|
arangodb::tests::v8Init(); // on-time initialize V8
|
||||||
|
|
||||||
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
|
// suppress INFO {authentication} Authentication is turned on (system only), authentication for unix sockets is turned on
|
||||||
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
|
arangodb::LogTopic::setLogLevel(arangodb::Logger::AUTHENTICATION.name(), arangodb::LogLevel::WARN);
|
||||||
|
@ -167,7 +147,6 @@ struct V8ViewsSetup {
|
||||||
}
|
}
|
||||||
|
|
||||||
~V8ViewsSetup() {
|
~V8ViewsSetup() {
|
||||||
|
|
||||||
arangodb::application_features::ApplicationServer::server = nullptr;
|
arangodb::application_features::ApplicationServer::server = nullptr;
|
||||||
arangodb::EngineSelectorFeature::ENGINE = nullptr;
|
arangodb::EngineSelectorFeature::ENGINE = nullptr;
|
||||||
|
|
||||||
|
@ -1441,4 +1420,4 @@ SECTION("test_auth") {
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// --SECTION-- END-OF-FILE
|
// --SECTION-- END-OF-FILE
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
|
@ -615,24 +615,37 @@ describe ArangoDB do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "granting collection" do
|
it "granting collection" do
|
||||||
|
#FIXME TODO collection does not seem to get created causing the test to fail
|
||||||
|
#ArangoDB.drop_collection("test", "test")
|
||||||
|
#ArangoDB.create_collection("test", false, 2, "test") # collection must exist
|
||||||
body = "{ \"grant\" : \"rw\"}"
|
body = "{ \"grant\" : \"rw\"}"
|
||||||
doc = ArangoDB.log_put("#{prefix}-grant", api + "/users-1/database/test/test", :body => body)
|
doc = ArangoDB.log_put("#{prefix}-grant", api + "/users-1/database/test/test", :body => body)
|
||||||
doc.code.should eq(200)
|
#doc.code.should eq(200)
|
||||||
doc.parsed_response['error'].should eq(false)
|
#doc.parsed_response['error'].should eq(false)
|
||||||
doc.parsed_response['code'].should eq(200)
|
#doc.parsed_response['code'].should eq(200)
|
||||||
|
doc.code.should eq(404)
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(404)
|
||||||
|
|
||||||
doc = ArangoDB.log_get("#{prefix}-grant-validate", api + "/users-1/database/test/test")
|
doc = ArangoDB.log_get("#{prefix}-grant-validate", api + "/users-1/database/test/test")
|
||||||
doc.code.should eq(200)
|
doc.code.should eq(200)
|
||||||
doc.parsed_response['error'].should eq(false)
|
doc.parsed_response['error'].should eq(false)
|
||||||
doc.parsed_response['code'].should eq(200)
|
doc.parsed_response['code'].should eq(200)
|
||||||
doc.parsed_response['result'].should eq('rw')
|
#doc.parsed_response['result'].should eq('rw')
|
||||||
|
doc.parsed_response['result'].should eq('ro')
|
||||||
end
|
end
|
||||||
|
|
||||||
it "revoking granted collection" do
|
it "revoking granted collection" do
|
||||||
|
#FIXME TODO collection does not seem to get created causing the test to fail
|
||||||
|
#ArangoDB.drop_collection("test", "test")
|
||||||
|
#ArangoDB.create_collection("test", false, 2, "test") # collection must exist
|
||||||
doc = ArangoDB.log_delete("#{prefix}-revoke", api + "/users-1/database/test/test")
|
doc = ArangoDB.log_delete("#{prefix}-revoke", api + "/users-1/database/test/test")
|
||||||
doc.code.should eq(202)
|
#doc.code.should eq(202)
|
||||||
doc.parsed_response['error'].should eq(false)
|
#doc.parsed_response['error'].should eq(false)
|
||||||
doc.parsed_response['code'].should eq(202)
|
#doc.parsed_response['code'].should eq(202)
|
||||||
|
doc.code.should eq(404)
|
||||||
|
doc.parsed_response['error'].should eq(true)
|
||||||
|
doc.parsed_response['code'].should eq(404)
|
||||||
|
|
||||||
doc = ArangoDB.log_get("#{prefix}-validate", api + "/users-1/database/test/test")
|
doc = ArangoDB.log_get("#{prefix}-validate", api + "/users-1/database/test/test")
|
||||||
doc.code.should eq(200)
|
doc.code.should eq(200)
|
||||||
|
|
Loading…
Reference in New Issue