Add rest of user auth queries

Reviewers: mferencevic, teon.banek

Reviewed By: mferencevic, teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1522
This commit is contained in:
Marin Tomic 2018-08-06 15:16:39 +02:00
parent ce306a4c21
commit 2a5fce8464
22 changed files with 1106 additions and 510 deletions

View File

@ -132,14 +132,13 @@ struct Clause {
merge @10 :Merge; merge @10 :Merge;
unwind @11 :Unwind; unwind @11 :Unwind;
createIndex @12 :CreateIndex; createIndex @12 :CreateIndex;
modifyUser @13 :ModifyUser; authQuery @13 :AuthQuery;
dropUser @14 :DropUser; createStream @14 :CreateStream;
createStream @15 :CreateStream; dropStream @15 :DropStream;
dropStream @16 :DropStream; showStreams @16 :ShowStreams;
showStreams @17 :ShowStreams; startStopStream @17 :StartStopStream;
startStopStream @18 :StartStopStream; startStopAllStreams @18 :StartStopAllStreams;
startStopAllStreams @19 :StartStopAllStreams; testStream @19 :TestStream;
testStream @20 :TestStream;
} }
} }
@ -398,14 +397,39 @@ struct CreateIndex {
property @1 :Storage.Common; property @1 :Storage.Common;
} }
struct ModifyUser { struct AuthQuery {
username @0 :Text; enum Action {
password @1 :Tree; createRole @0;
isCreate @2 :Bool; dropRole @1;
showRoles @2;
createUser @3;
setPassword @4;
dropUser @5;
showUsers @6;
grantRole @7;
revokeRole @8;
grantPrivilege @9;
denyPrivilege @10;
revokePrivilege @11;
showGrants @12;
showRoleForUser @13;
showUsersForRole @14;
} }
enum Privilege {
struct DropUser { create @0;
usernames @0 :List(Text); delete @1;
match @2;
merge @3;
set @4;
auth @5;
stream @6;
}
action @0 :Action;
user @1 :Text;
role @2 :Text;
userOrRole @3 :Text;
password @4 :Tree;
privileges @5 :List(Privilege);
} }
struct CreateStream { struct CreateStream {

View File

@ -1268,13 +1268,9 @@ Clause *Clause::Construct(const capnp::Clause::Reader &reader,
auto with_reader = reader.getWith(); auto with_reader = reader.getWith();
return With::Construct(with_reader, storage); return With::Construct(with_reader, storage);
} }
case capnp::Clause::MODIFY_USER: { case capnp::Clause::AUTH_QUERY: {
auto mu_reader = reader.getModifyUser(); auto aq_reader = reader.getAuthQuery();
return ModifyUser::Construct(mu_reader, storage); return AuthQuery::Construct(aq_reader, storage);
}
case capnp::Clause::DROP_USER: {
auto du_reader = reader.getDropUser();
return DropUser::Construct(du_reader, storage);
} }
case capnp::Clause::CREATE_STREAM: { case capnp::Clause::CREATE_STREAM: {
auto cs_reader = reader.getCreateStream(); auto cs_reader = reader.getCreateStream();
@ -2067,69 +2063,188 @@ With *With::Construct(const capnp::With::Reader &reader, AstStorage *storage) {
return storage->Create<With>(); return storage->Create<With>();
} }
// ModifyUser. // AuthQuery.
void ModifyUser::Save(capnp::Clause::Builder *clause_builder, void AuthQuery::Save(capnp::Clause::Builder *clause_builder,
std::vector<int> *saved_uids) { std::vector<int> *saved_uids) {
Clause::Save(clause_builder, saved_uids); Clause::Save(clause_builder, saved_uids);
auto builder = clause_builder->initModifyUser(); auto builder = clause_builder->initAuthQuery();
ModifyUser::Save(&builder, saved_uids); AuthQuery::Save(&builder, saved_uids);
} }
void ModifyUser::Save(capnp::ModifyUser::Builder *builder, void AuthQuery::Save(capnp::AuthQuery::Builder *builder,
std::vector<int> *saved_uids) { std::vector<int> *saved_uids) {
builder->setUsername(username_); switch (action_) {
case Action::CREATE_ROLE:
builder->setAction(capnp::AuthQuery::Action::CREATE_ROLE);
break;
case Action::DROP_ROLE:
builder->setAction(capnp::AuthQuery::Action::DROP_ROLE);
break;
case Action::SHOW_ROLES:
builder->setAction(capnp::AuthQuery::Action::SHOW_ROLES);
break;
case Action::CREATE_USER:
builder->setAction(capnp::AuthQuery::Action::CREATE_USER);
break;
case Action::SET_PASSWORD:
builder->setAction(capnp::AuthQuery::Action::SET_PASSWORD);
break;
case Action::DROP_USER:
builder->setAction(capnp::AuthQuery::Action::DROP_USER);
break;
case Action::SHOW_USERS:
builder->setAction(capnp::AuthQuery::Action::SHOW_USERS);
break;
case Action::GRANT_ROLE:
builder->setAction(capnp::AuthQuery::Action::GRANT_ROLE);
break;
case Action::REVOKE_ROLE:
builder->setAction(capnp::AuthQuery::Action::REVOKE_ROLE);
break;
case Action::GRANT_PRIVILEGE:
builder->setAction(capnp::AuthQuery::Action::GRANT_PRIVILEGE);
break;
case Action::DENY_PRIVILEGE:
builder->setAction(capnp::AuthQuery::Action::DENY_PRIVILEGE);
break;
case Action::REVOKE_PRIVILEGE:
builder->setAction(capnp::AuthQuery::Action::REVOKE_PRIVILEGE);
break;
case Action::SHOW_GRANTS:
builder->setAction(capnp::AuthQuery::Action::SHOW_GRANTS);
break;
case Action::SHOW_ROLE_FOR_USER:
builder->setAction(capnp::AuthQuery::Action::SHOW_ROLE_FOR_USER);
break;
case Action::SHOW_USERS_FOR_ROLE:
builder->setAction(capnp::AuthQuery::Action::SHOW_USERS_FOR_ROLE);
break;
}
builder->setUser(user_);
builder->setRole(role_);
builder->setUserOrRole(user_or_role_);
if (password_) { if (password_) {
auto password_builder = builder->getPassword(); auto password_builder = builder->initPassword();
password_->Save(&password_builder, saved_uids); password_->Save(&password_builder, saved_uids);
} }
builder->setIsCreate(is_create_); ::capnp::List<capnp::AuthQuery::Privilege>::Builder privileges_builder =
builder->initPrivileges(privileges_.size());
for (size_t i = 0; i < privileges_.size(); ++i) {
switch (privileges_[i]) {
case Privilege::CREATE:
privileges_builder.set(i, capnp::AuthQuery::Privilege::CREATE);
break;
case Privilege::DELETE:
privileges_builder.set(i, capnp::AuthQuery::Privilege::DELETE);
break;
case Privilege::MATCH:
privileges_builder.set(i, capnp::AuthQuery::Privilege::MATCH);
break;
case Privilege::MERGE:
privileges_builder.set(i, capnp::AuthQuery::Privilege::MERGE);
break;
case Privilege::SET:
privileges_builder.set(i, capnp::AuthQuery::Privilege::SET);
break;
case Privilege::AUTH:
privileges_builder.set(i, capnp::AuthQuery::Privilege::AUTH);
break;
case Privilege::STREAM:
privileges_builder.set(i, capnp::AuthQuery::Privilege::STREAM);
break;
}
}
} }
void ModifyUser::Load(const capnp::Tree::Reader &base_reader, void AuthQuery::Load(const capnp::Tree::Reader &base_reader,
AstStorage *storage, std::vector<int> *loaded_uids) { AstStorage *storage, std::vector<int> *loaded_uids) {
Clause::Load(base_reader, storage, loaded_uids); Clause::Load(base_reader, storage, loaded_uids);
auto reader = base_reader.getClause().getModifyUser(); auto auth_reader = base_reader.getClause().getAuthQuery();
username_ = reader.getUsername(); switch (auth_reader.getAction()) {
if (reader.hasPassword()) { case capnp::AuthQuery::Action::CREATE_ROLE:
const auto password_reader = reader.getPassword(); action_ = Action::CREATE_ROLE;
break;
case capnp::AuthQuery::Action::DROP_ROLE:
action_ = Action::DROP_ROLE;
break;
case capnp::AuthQuery::Action::SHOW_ROLES:
action_ = Action::SHOW_ROLES;
break;
case capnp::AuthQuery::Action::CREATE_USER:
action_ = Action::CREATE_USER;
break;
case capnp::AuthQuery::Action::SET_PASSWORD:
action_ = Action::SET_PASSWORD;
break;
case capnp::AuthQuery::Action::DROP_USER:
action_ = Action::DROP_USER;
break;
case capnp::AuthQuery::Action::SHOW_USERS:
action_ = Action::SHOW_USERS;
break;
case capnp::AuthQuery::Action::GRANT_ROLE:
action_ = Action::GRANT_ROLE;
break;
case capnp::AuthQuery::Action::REVOKE_ROLE:
action_ = Action::REVOKE_ROLE;
break;
case capnp::AuthQuery::Action::GRANT_PRIVILEGE:
action_ = Action::GRANT_PRIVILEGE;
break;
case capnp::AuthQuery::Action::DENY_PRIVILEGE:
action_ = Action::DENY_PRIVILEGE;
break;
case capnp::AuthQuery::Action::REVOKE_PRIVILEGE:
action_ = Action::REVOKE_PRIVILEGE;
break;
case capnp::AuthQuery::Action::SHOW_GRANTS:
action_ = Action::SHOW_GRANTS;
break;
case capnp::AuthQuery::Action::SHOW_ROLE_FOR_USER:
action_ = Action::SHOW_ROLE_FOR_USER;
break;
case capnp::AuthQuery::Action::SHOW_USERS_FOR_ROLE:
action_ = Action::SHOW_USERS_FOR_ROLE;
break;
}
user_ = auth_reader.getUser();
role_ = auth_reader.getRole();
user_or_role_ = auth_reader.getUserOrRole();
if (auth_reader.hasPassword()) {
const auto password_reader = auth_reader.getPassword();
password_ = password_ =
dynamic_cast<Expression *>(storage->Load(password_reader, loaded_uids)); dynamic_cast<Expression *>(storage->Load(password_reader, loaded_uids));
} else {
password_ = nullptr;
} }
is_create_ = reader.getIsCreate(); for (const auto &privilege : auth_reader.getPrivileges()) {
switch (privilege) {
case capnp::AuthQuery::Privilege::CREATE:
privileges_.push_back(Privilege::CREATE);
break;
case capnp::AuthQuery::Privilege::DELETE:
privileges_.push_back(Privilege::DELETE);
break;
case capnp::AuthQuery::Privilege::MATCH:
privileges_.push_back(Privilege::MATCH);
break;
case capnp::AuthQuery::Privilege::MERGE:
privileges_.push_back(Privilege::MERGE);
break;
case capnp::AuthQuery::Privilege::SET:
privileges_.push_back(Privilege::SET);
break;
case capnp::AuthQuery::Privilege::AUTH:
privileges_.push_back(Privilege::AUTH);
break;
case capnp::AuthQuery::Privilege::STREAM:
privileges_.push_back(Privilege::STREAM);
break;
}
}
} }
ModifyUser *ModifyUser::Construct(const capnp::ModifyUser::Reader &reader, AuthQuery *AuthQuery::Construct(const capnp::AuthQuery::Reader &reader,
AstStorage *storage) { AstStorage *storage) {
return storage->Create<ModifyUser>(); return storage->Create<AuthQuery>();
}
// DropUser.
void DropUser::Save(capnp::Clause::Builder *clause_builder,
std::vector<int> *saved_uids) {
Clause::Save(clause_builder, saved_uids);
auto builder = clause_builder->initDropUser();
DropUser::Save(&builder, saved_uids);
}
void DropUser::Save(capnp::DropUser::Builder *builder,
std::vector<int> *saved_uids) {
auto usernames_builder = builder->initUsernames(usernames_.size());
utils::SaveVector(usernames_, &usernames_builder);
}
void DropUser::Load(const capnp::Tree::Reader &base_reader, AstStorage *storage,
std::vector<int> *loaded_uids) {
Clause::Load(base_reader, storage, loaded_uids);
auto reader = base_reader.getClause().getDropUser();
usernames_.clear();
utils::LoadVector(&usernames_, reader.getUsernames());
}
DropUser *DropUser::Construct(const capnp::DropUser::Reader &reader,
AstStorage *storage) {
return storage->Create<DropUser>();
} }
// CypherUnion // CypherUnion

View File

@ -2339,68 +2339,67 @@ class CreateIndex : public Clause {
std::vector<int> *saved_uids); std::vector<int> *saved_uids);
}; };
class ModifyUser : public Clause { class AuthQuery : public Clause {
friend class AstStorage; friend class AstStorage;
public: public:
DEFVISITABLE(TreeVisitor<TypedValue>); enum class Action {
DEFVISITABLE(HierarchicalTreeVisitor); CREATE_ROLE,
DROP_ROLE,
ModifyUser *Clone(AstStorage &storage) const override { SHOW_ROLES,
return storage.Create<ModifyUser>( CREATE_USER,
username_, password_ ? password_->Clone(storage) : nullptr, is_create_); SET_PASSWORD,
} DROP_USER,
SHOW_USERS,
static ModifyUser *Construct(const capnp::ModifyUser::Reader &reader, GRANT_ROLE,
AstStorage *storage); REVOKE_ROLE,
using Clause::Save; GRANT_PRIVILEGE,
DENY_PRIVILEGE,
std::string username_; REVOKE_PRIVILEGE,
Expression *password_; SHOW_GRANTS,
bool is_create_; SHOW_ROLE_FOR_USER,
SHOW_USERS_FOR_ROLE
protected:
explicit ModifyUser(int uid) : Clause(uid) {}
ModifyUser(int uid, std::string username, Expression *password,
bool is_create)
: Clause(uid),
username_(std::move(username)),
password_(password),
is_create_(is_create) {}
void Save(capnp::Clause::Builder *builder,
std::vector<int> *saved_uids) override;
virtual void Save(capnp::ModifyUser::Builder *builder,
std::vector<int> *saved_uids);
void Load(const capnp::Tree::Reader &base_reader, AstStorage *storage,
std::vector<int> *loaded_uids) override;
}; };
class DropUser : public Clause { enum class Privilege { CREATE, DELETE, MATCH, MERGE, SET, AUTH, STREAM };
friend class AstStorage;
Action action_;
std::string user_;
std::string role_;
std::string user_or_role_;
Expression *password_{nullptr};
std::vector<Privilege> privileges_;
public:
DEFVISITABLE(TreeVisitor<TypedValue>); DEFVISITABLE(TreeVisitor<TypedValue>);
DEFVISITABLE(HierarchicalTreeVisitor); DEFVISITABLE(HierarchicalTreeVisitor);
DropUser *Clone(AstStorage &storage) const override { AuthQuery *Clone(AstStorage &storage) const override {
return storage.Create<DropUser>(usernames_); return storage.Create<AuthQuery>(
action_, user_, role_, user_or_role_,
password_ ? password_->Clone(storage) : nullptr, privileges_);
} }
static DropUser *Construct(const capnp::DropUser::Reader &reader, static AuthQuery *Construct(const capnp::AuthQuery::Reader &reader,
AstStorage *storage); AstStorage *storage);
using Clause::Save; using Clause::Save;
std::vector<std::string> usernames_;
protected: protected:
explicit DropUser(int uid) : Clause(uid) {} explicit AuthQuery(int uid) : Clause(uid) {}
DropUser(int uid, std::vector<std::string> usernames)
: Clause(uid), usernames_(usernames) {} explicit AuthQuery(int uid, Action action, std::string user, std::string role,
std::string user_or_role, Expression *password,
std::vector<Privilege> privileges)
: Clause(uid),
action_(action),
user_(user),
role_(role),
user_or_role_(user_or_role),
password_(password),
privileges_(privileges) {}
void Save(capnp::Clause::Builder *builder, void Save(capnp::Clause::Builder *builder,
std::vector<int> *saved_uids) override; std::vector<int> *saved_uids) override;
virtual void Save(capnp::DropUser::Builder *builder, virtual void Save(capnp::AuthQuery::Builder *builder,
std::vector<int> *saved_uids); std::vector<int> *saved_uids);
void Load(const capnp::Tree::Reader &base_reader, AstStorage *storage, void Load(const capnp::Tree::Reader &base_reader, AstStorage *storage,
std::vector<int> *loaded_uids) override; std::vector<int> *loaded_uids) override;

View File

@ -61,8 +61,7 @@ class RemoveLabels;
class Merge; class Merge;
class Unwind; class Unwind;
class CreateIndex; class CreateIndex;
class ModifyUser; class AuthQuery;
class DropUser;
class CreateStream; class CreateStream;
class DropStream; class DropStream;
class ShowStreams; class ShowStreams;
@ -84,9 +83,9 @@ using TreeCompositeVisitor = ::utils::CompositeVisitor<
using TreeLeafVisitor = using TreeLeafVisitor =
::utils::LeafVisitor<Identifier, PrimitiveLiteral, ParameterLookup, ::utils::LeafVisitor<Identifier, PrimitiveLiteral, ParameterLookup,
CreateIndex, ModifyUser, DropUser, CreateStream, CreateIndex, AuthQuery, CreateStream, DropStream,
DropStream, ShowStreams, StartStopStream, ShowStreams, StartStopStream, StartStopAllStreams,
StartStopAllStreams, TestStream>; TestStream>;
class HierarchicalTreeVisitor : public TreeCompositeVisitor, class HierarchicalTreeVisitor : public TreeCompositeVisitor,
public TreeLeafVisitor { public TreeLeafVisitor {
@ -109,8 +108,7 @@ using TreeVisitor = ::utils::Visitor<
Aggregation, Function, Reduce, Extract, All, Single, ParameterLookup, Aggregation, Function, Reduce, Extract, All, Single, ParameterLookup,
Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where,
SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge, SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels, Merge,
Unwind, Identifier, PrimitiveLiteral, CreateIndex, ModifyUser, DropUser, Unwind, Identifier, PrimitiveLiteral, CreateIndex, AuthQuery, CreateStream,
CreateStream, DropStream, ShowStreams, StartStopStream, StartStopAllStreams, DropStream, ShowStreams, StartStopStream, StartStopAllStreams, TestStream>;
TestStream>;
} // namespace query } // namespace query

View File

@ -5,6 +5,7 @@
#include <codecvt> #include <codecvt>
#include <cstring> #include <cstring>
#include <limits> #include <limits>
#include <regex>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <unordered_map> #include <unordered_map>
@ -28,13 +29,10 @@ antlrcpp::Any CypherMainVisitor::visitAuthQuery(
MemgraphCypher::AuthQueryContext *ctx) { MemgraphCypher::AuthQueryContext *ctx) {
query_ = storage_.query(); query_ = storage_.query();
query_->single_query_ = storage_.Create<SingleQuery>(); query_->single_query_ = storage_.Create<SingleQuery>();
Clause *clause = nullptr; CHECK(ctx->children.size() == 1)
if (ctx->modifyUser()) { << "AuthQuery should have exactly one child!";
clause = ctx->modifyUser()->accept(this).as<ModifyUser *>(); query_->single_query_->clauses_.push_back(
} else if (ctx->dropUser()) { ctx->children[0]->accept(this).as<AuthQuery *>());
clause = ctx->dropUser()->accept(this).as<DropUser *>();
}
query_->single_query_->clauses_ = {clause};
return query_; return query_;
} }
@ -259,46 +257,224 @@ antlrcpp::Any CypherMainVisitor::visitCreateIndex(
} }
/** /**
* @return ModifyUser* * @return std::string
*/ */
antlrcpp::Any CypherMainVisitor::visitModifyUser( antlrcpp::Any CypherMainVisitor::visitUserOrRoleName(
MemgraphCypher::ModifyUserContext *ctx) { MemgraphCypher::UserOrRoleNameContext *ctx) {
std::string username(ctx->userName->getText()); std::string value = ctx->symbolicName()->accept(this).as<std::string>();
Expression *password = nullptr; const std::regex NAME_REGEX("[a-zA-Z0-9_.+-]+");
bool is_create = static_cast<bool>(ctx->CREATE()); if (!std::regex_match(value, NAME_REGEX)) {
for (auto option : ctx->modifyUserOption()) { throw SyntaxException("invalid user or role name");
if (option->passwordOption()) {
if (password) {
throw QueryException("password should be set at most once");
} }
password = option->passwordOption()->accept(this); return value;
continue;
}
LOG(FATAL) << "Expected to handle all cases above.";
}
return storage_.Create<ModifyUser>(username, password, is_create);
} }
/** /**
* @return Expression* * @return AuthQuery*
*/ */
antlrcpp::Any CypherMainVisitor::visitPasswordOption( antlrcpp::Any CypherMainVisitor::visitCreateRole(
MemgraphCypher::PasswordOptionContext *ctx) { MemgraphCypher::CreateRoleContext *ctx) {
if (!ctx->literal()->StringLiteral() && !ctx->literal()->CYPHERNULL()) { AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::CREATE_ROLE;
auth->role_ = ctx->role->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitDropRole(
MemgraphCypher::DropRoleContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::DROP_ROLE;
auth->role_ = ctx->role->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowRoles(
MemgraphCypher::ShowRolesContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_ROLES;
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitCreateUser(
MemgraphCypher::CreateUserContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::CREATE_USER;
auth->user_ = ctx->user->accept(this).as<std::string>();
if (ctx->password) {
if (!ctx->password->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
throw SyntaxException("password should be a string literal or NULL"); throw SyntaxException("password should be a string literal or NULL");
} }
return ctx->literal()->accept(this); auth->password_ = ctx->password->accept(this);
}
return auth;
} }
/** /**
* @return DropUser* * @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitSetPassword(
MemgraphCypher::SetPasswordContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SET_PASSWORD;
auth->user_ = ctx->user->accept(this).as<std::string>();
if (!ctx->password->StringLiteral() && !ctx->literal()->CYPHERNULL()) {
throw SyntaxException("password should be a string literal or NULL");
}
auth->password_ = ctx->password->accept(this);
return auth;
}
/**
* @return AuthQuery*
*/ */
antlrcpp::Any CypherMainVisitor::visitDropUser( antlrcpp::Any CypherMainVisitor::visitDropUser(
MemgraphCypher::DropUserContext *ctx) { MemgraphCypher::DropUserContext *ctx) {
std::vector<std::string> usernames; AuthQuery *auth = storage_.Create<AuthQuery>();
for (auto username_ptr : ctx->userName) auth->action_ = AuthQuery::Action::DROP_USER;
usernames.emplace_back(username_ptr->getText()); auth->user_ = ctx->user->accept(this).as<std::string>();
return storage_.Create<DropUser>(usernames); return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowUsers(
MemgraphCypher::ShowUsersContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_USERS;
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitGrantRole(
MemgraphCypher::GrantRoleContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::GRANT_ROLE;
auth->role_ = ctx->role->accept(this).as<std::string>();
auth->user_ = ctx->user->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitRevokeRole(
MemgraphCypher::RevokeRoleContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::REVOKE_ROLE;
auth->role_ = ctx->role->accept(this).as<std::string>();
auth->user_ = ctx->user->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitGrantPrivilege(
MemgraphCypher::GrantPrivilegeContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::GRANT_PRIVILEGE;
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
for (auto *privilege : ctx->privilegeList()->privilege()) {
auth->privileges_.push_back(privilege->accept(this));
}
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitDenyPrivilege(
MemgraphCypher::DenyPrivilegeContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::DENY_PRIVILEGE;
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
for (auto *privilege : ctx->privilegeList()->privilege()) {
auth->privileges_.push_back(privilege->accept(this));
}
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitRevokePrivilege(
MemgraphCypher::RevokePrivilegeContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::REVOKE_PRIVILEGE;
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
if (ctx->privilegeList()) {
for (auto *privilege : ctx->privilegeList()->privilege()) {
auth->privileges_.push_back(privilege->accept(this));
}
} else {
/* revoke all privileges */
auth->privileges_ = {
AuthQuery::Privilege::CREATE, AuthQuery::Privilege::DELETE,
AuthQuery::Privilege::MATCH, AuthQuery::Privilege::MERGE,
AuthQuery::Privilege::SET, AuthQuery::Privilege::AUTH,
AuthQuery::Privilege::STREAM};
}
return auth;
}
/**
* @return AuthQuery::Privilege
*/
antlrcpp::Any CypherMainVisitor::visitPrivilege(
MemgraphCypher::PrivilegeContext *ctx) {
if (ctx->CREATE()) return AuthQuery::Privilege::CREATE;
if (ctx->DELETE()) return AuthQuery::Privilege::DELETE;
if (ctx->MATCH()) return AuthQuery::Privilege::MATCH;
if (ctx->MERGE()) return AuthQuery::Privilege::MERGE;
if (ctx->SET()) return AuthQuery::Privilege::SET;
if (ctx->AUTH()) return AuthQuery::Privilege::AUTH;
if (ctx->STREAM()) return AuthQuery::Privilege::STREAM;
LOG(FATAL) << "Should not get here - unknown privilege!";
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowGrants(
MemgraphCypher::ShowGrantsContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_GRANTS;
auth->user_or_role_ = ctx->userOrRole->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowRoleForUser(
MemgraphCypher::ShowRoleForUserContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_ROLE_FOR_USER;
auth->user_ = ctx->user->accept(this).as<std::string>();
return auth;
}
/**
* @return AuthQuery*
*/
antlrcpp::Any CypherMainVisitor::visitShowUsersForRole(
MemgraphCypher::ShowUsersForRoleContext *ctx) {
AuthQuery *auth = storage_.Create<AuthQuery>();
auth->action_ = AuthQuery::Action::SHOW_USERS_FOR_ROLE;
auth->role_ = ctx->role->accept(this).as<std::string>();
return auth;
} }
/** /**

View File

@ -177,6 +177,28 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
*/ */
antlrcpp::Any visitCreate(MemgraphCypher::CreateContext *ctx) override; antlrcpp::Any visitCreate(MemgraphCypher::CreateContext *ctx) override;
/**
* @return std::string
*/
antlrcpp::Any visitUserOrRoleName(
MemgraphCypher::UserOrRoleNameContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitCreateRole(
MemgraphCypher::CreateRoleContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitDropRole(MemgraphCypher::DropRoleContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowRoles(MemgraphCypher::ShowRolesContext *ctx) override;
/** /**
* @return CreateIndex* * @return CreateIndex*
*/ */
@ -184,19 +206,79 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
MemgraphCypher::CreateIndexContext *ctx) override; MemgraphCypher::CreateIndexContext *ctx) override;
/** /**
* @return ModifyUser* * @return AuthQuery*
*/ */
antlrcpp::Any visitModifyUser( antlrcpp::Any visitCreateUser(
MemgraphCypher::ModifyUserContext *ctx) override; MemgraphCypher::CreateUserContext *ctx) override;
antlrcpp::Any visitPasswordOption(
MemgraphCypher::PasswordOptionContext *ctx) override;
/** /**
* @return DropUser* * @return AuthQuery*
*/
antlrcpp::Any visitSetPassword(
MemgraphCypher::SetPasswordContext *ctx) override;
/**
* @return AuthQuery*
*/ */
antlrcpp::Any visitDropUser(MemgraphCypher::DropUserContext *ctx) override; antlrcpp::Any visitDropUser(MemgraphCypher::DropUserContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowUsers(MemgraphCypher::ShowUsersContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitGrantRole(MemgraphCypher::GrantRoleContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitRevokeRole(
MemgraphCypher::RevokeRoleContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitGrantPrivilege(
MemgraphCypher::GrantPrivilegeContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitDenyPrivilege(
MemgraphCypher::DenyPrivilegeContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitRevokePrivilege(
MemgraphCypher::RevokePrivilegeContext *ctx) override;
/**
* @return AuthQuery::Privilege
*/
antlrcpp::Any visitPrivilege(MemgraphCypher::PrivilegeContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowGrants(
MemgraphCypher::ShowGrantsContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowRoleForUser(
MemgraphCypher::ShowRoleForUserContext *ctx) override;
/**
* @return AuthQuery*
*/
antlrcpp::Any visitShowUsersForRole(
MemgraphCypher::ShowUsersForRoleContext *ctx) override;
/** /**
* @return CreateStream* * @return CreateStream*
*/ */

View File

@ -8,23 +8,36 @@ import Cypher ;
memgraphCypherKeyword : cypherKeyword memgraphCypherKeyword : cypherKeyword
| ALTER | ALTER
| AUTH
| BATCH | BATCH
| BATCHES | BATCHES
| DATA | DATA
| DENY
| DROP | DROP
| FOR
| FROM
| GRANT
| GRANTS
| IDENTIFIED
| INTERVAL | INTERVAL
| K_TEST | K_TEST
| KAFKA | KAFKA
| LOAD | LOAD
| PASSWORD | PASSWORD
| PRIVILEGES
| REVOKE
| ROLE
| ROLES
| SIZE | SIZE
| START | START
| STOP | STOP
| STREAM | STREAM
| STREAMS | STREAMS
| TO
| TOPIC | TOPIC
| TRANSFORM | TRANSFORM
| USER | USER
| USERS
; ;
symbolicName : UnescapedSymbolicName symbolicName : UnescapedSymbolicName
@ -37,19 +50,60 @@ query : regularQuery
| streamQuery | streamQuery
; ;
authQuery : modifyUser authQuery : createRole
| dropRole
| showRoles
| createUser
| setPassword
| dropUser | dropUser
| showUsers
| grantRole
| revokeRole
| grantPrivilege
| denyPrivilege
| revokePrivilege
| showGrants
| showRoleForUser
| showUsersForRole
; ;
modifyUser : ( CREATE | ALTER ) USER userName=UnescapedSymbolicName userOrRoleName : symbolicName ;
( WITH ( modifyUserOption )+ )? ;
modifyUserOption : passwordOption ; createRole : CREATE ROLE role=userOrRoleName ;
passwordOption : PASSWORD literal; dropRole : DROP ROLE role=userOrRoleName ;
dropUser : DROP USER userName+=UnescapedSymbolicName showRoles : SHOW ROLES ;
( ',' userName+=UnescapedSymbolicName )* ;
createUser : CREATE USER user=userOrRoleName
( IDENTIFIED BY password=literal )? ;
setPassword : SET PASSWORD FOR user=userOrRoleName TO password=literal;
dropUser : DROP USER user=userOrRoleName ;
showUsers : SHOW USERS ;
grantRole : GRANT ROLE role=userOrRoleName TO user=userOrRoleName ;
revokeRole : REVOKE ROLE role=userOrRoleName FROM user=userOrRoleName ;
grantPrivilege : GRANT privilegeList TO userOrRole=userOrRoleName ;
denyPrivilege : DENY privilegeList TO userOrRole=userOrRoleName ;
revokePrivilege : REVOKE ( ALL PRIVILEGES | privileges=privilegeList ) FROM userOrRole=userOrRoleName ;
privilege : CREATE | DELETE | MATCH | MERGE | SET
| AUTH | STREAM ;
privilegeList : privilege ( ',' privilege )* ;
showGrants : SHOW GRANTS FOR userOrRole=userOrRoleName ;
showRoleForUser : SHOW ROLE FOR USER user=userOrRoleName ;
showUsersForRole : SHOW USERS FOR ROLE role=userOrRoleName ;
streamQuery : createStream streamQuery : createStream
| dropStream | dropStream

View File

@ -11,20 +11,33 @@ lexer grammar MemgraphCypherLexer ;
import CypherLexer ; import CypherLexer ;
ALTER : A L T E R ; ALTER : A L T E R ;
AUTH : A U T H ;
BATCH : B A T C H ; BATCH : B A T C H ;
BATCHES : B A T C H E S ; BATCHES : B A T C H E S ;
DATA : D A T A ; DATA : D A T A ;
DENY : D E N Y ;
DROP : D R O P ; DROP : D R O P ;
FOR : F O R ;
FROM : F R O M ;
GRANT : G R A N T ;
GRANTS : G R A N T S ;
IDENTIFIED : I D E N T I F I E D ;
INTERVAL : I N T E R V A L ; INTERVAL : I N T E R V A L ;
K_TEST : T E S T ; K_TEST : T E S T ;
KAFKA : K A F K A ; KAFKA : K A F K A ;
LOAD : L O A D ; LOAD : L O A D ;
PASSWORD : P A S S W O R D ; PASSWORD : P A S S W O R D ;
PRIVILEGES : P R I V I L E G E S ;
REVOKE : R E V O K E ;
ROLE : R O L E ;
ROLES : R O L E S ;
SIZE : S I Z E ; SIZE : S I Z E ;
START : S T A R T ; START : S T A R T ;
STOP : S T O P ; STOP : S T O P ;
STREAM : S T R E A M ; STREAM : S T R E A M ;
STREAMS : S T R E A M S ; STREAMS : S T R E A M S ;
TO : T O ;
TOPIC : T O P I C ; TOPIC : T O P I C ;
TRANSFORM : T R A N S F O R M ; TRANSFORM : T R A N S F O R M ;
USER : U S E R ; USER : U S E R ;
USERS : U S E R S ;

View File

@ -220,9 +220,7 @@ bool SymbolGenerator::PostVisit(Match &) {
bool SymbolGenerator::Visit(CreateIndex &) { return true; } bool SymbolGenerator::Visit(CreateIndex &) { return true; }
bool SymbolGenerator::Visit(ModifyUser &) { return true; } bool SymbolGenerator::Visit(AuthQuery &) { return true; }
bool SymbolGenerator::Visit(DropUser &) { return true; }
bool SymbolGenerator::Visit(CreateStream &) { return true; } bool SymbolGenerator::Visit(CreateStream &) { return true; }

View File

@ -47,8 +47,7 @@ class SymbolGenerator : public HierarchicalTreeVisitor {
bool PreVisit(Match &) override; bool PreVisit(Match &) override;
bool PostVisit(Match &) override; bool PostVisit(Match &) override;
bool Visit(CreateIndex &) override; bool Visit(CreateIndex &) override;
bool Visit(ModifyUser &) override; bool Visit(AuthQuery &) override;
bool Visit(DropUser &) override;
bool Visit(CreateStream &) override; bool Visit(CreateStream &) override;
bool Visit(DropStream &) override; bool Visit(DropStream &) override;
bool Visit(ShowStreams &) override; bool Visit(ShowStreams &) override;

View File

@ -49,8 +49,7 @@ class ExpressionEvaluator : public TreeVisitor<TypedValue> {
BLOCK_VISIT(Merge); BLOCK_VISIT(Merge);
BLOCK_VISIT(Unwind); BLOCK_VISIT(Unwind);
BLOCK_VISIT(CreateIndex); BLOCK_VISIT(CreateIndex);
BLOCK_VISIT(ModifyUser); BLOCK_VISIT(AuthQuery);
BLOCK_VISIT(DropUser);
BLOCK_VISIT(CreateStream); BLOCK_VISIT(CreateStream);
BLOCK_VISIT(DropStream); BLOCK_VISIT(DropStream);
BLOCK_VISIT(ShowStreams); BLOCK_VISIT(ShowStreams);

View File

@ -185,8 +185,7 @@ class CostEstimator : public HierarchicalLogicalOperatorVisitor {
bool Visit(Once &) override { return true; } bool Visit(Once &) override { return true; }
bool Visit(CreateIndex &) override { return true; } bool Visit(CreateIndex &) override { return true; }
bool Visit(ModifyUser &) override { return true; } bool Visit(AuthHandler &) override { return true; }
bool Visit(DropUser &) override { return true; }
bool Visit(CreateStream &) override { return true; } bool Visit(CreateStream &) override { return true; }
bool Visit(DropStream &) override { return true; } bool Visit(DropStream &) override { return true; }
bool Visit(ShowStreams &) override { return true; } bool Visit(ShowStreams &) override { return true; }

View File

@ -87,8 +87,7 @@ class IndependentSubtreeFinder : public HierarchicalLogicalOperatorVisitor {
// These don't use any symbols // These don't use any symbols
bool Visit(Once &) override { return true; } bool Visit(Once &) override { return true; }
bool Visit(CreateIndex &) override { return true; } bool Visit(CreateIndex &) override { return true; }
bool Visit(ModifyUser &) override { return true; } bool Visit(AuthHandler &) override { return true; }
bool Visit(DropUser &) override { return true; }
bool Visit(CreateStream &) override { return true; } bool Visit(CreateStream &) override { return true; }
bool Visit(DropStream &) override { return true; } bool Visit(DropStream &) override { return true; }
bool Visit(ShowStreams &) override { return true; } bool Visit(ShowStreams &) override { return true; }
@ -1344,9 +1343,7 @@ class DistributedPlanner : public HierarchicalLogicalOperatorVisitor {
bool Visit(CreateIndex &) override { return true; } bool Visit(CreateIndex &) override { return true; }
bool Visit(ModifyUser &) override { return true; } bool Visit(AuthHandler &) override { return true; }
bool Visit(DropUser &) override { return true; }
bool Visit(CreateStream &) override { return true; } bool Visit(CreateStream &) override { return true; }

View File

@ -1392,7 +1392,8 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
// For the given (edge, vertex, weight, depth) tuple checks if they // For the given (edge, vertex, weight, depth) tuple checks if they
// satisfy the "where" condition. if so, places them in the priority queue. // satisfy the "where" condition. if so, places them in the priority queue.
auto expand_pair = [this, &evaluator, &frame, &create_state]( auto expand_pair = [this, &evaluator, &frame, &create_state](
EdgeAccessor edge, VertexAccessor vertex, double weight, int depth) { EdgeAccessor edge, VertexAccessor vertex,
double weight, int depth) {
SwitchAccessor(edge, self_.graph_view_); SwitchAccessor(edge, self_.graph_view_);
SwitchAccessor(vertex, self_.graph_view_); SwitchAccessor(vertex, self_.graph_view_);
@ -3889,129 +3890,119 @@ std::unique_ptr<Cursor> PullRemoteOrderBy::MakeCursor(
return std::make_unique<PullRemoteOrderByCursor>(*this, db); return std::make_unique<PullRemoteOrderByCursor>(*this, db);
} }
ModifyUser::ModifyUser(std::string username, Expression *password, AuthHandler::AuthHandler(AuthQuery::Action action, std::string user,
bool is_create) std::string role, std::string user_or_role,
: username_(std::move(username)), Expression *password,
std::vector<AuthQuery::Privilege> privileges)
: action_(action),
user_(user),
role_(role),
user_or_role_(user_or_role),
password_(password), password_(password),
is_create_(is_create) {} privileges_(privileges) {}
bool ModifyUser::Accept(HierarchicalLogicalOperatorVisitor &visitor) { bool AuthHandler::Accept(HierarchicalLogicalOperatorVisitor &visitor) {
return visitor.Visit(*this); return visitor.Visit(*this);
} }
WITHOUT_SINGLE_INPUT(ModifyUser) class AuthHandlerCursor : public Cursor {
class ModifyUserCursor : public Cursor {
public: public:
ModifyUserCursor(const ModifyUser &self) : self_(self) {} AuthHandlerCursor(const AuthHandler &self) : self_(self) {}
bool Pull(Frame &frame, Context &ctx) override { bool Pull(Frame &frame, Context &ctx) override {
if (ctx.in_explicit_transaction_) { if (ctx.in_explicit_transaction_) {
throw UserModificationInMulticommandTxException(); throw UserModificationInMulticommandTxException();
} }
ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD); ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD);
std::experimental::optional<std::string> password;
TypedValue password_tv = self_.password()->Accept(evaluator); /* TODO(mferencevic): handle null passwords properly */
if (password_tv.type() != TypedValue::Type::String) { if (self_.password()) {
throw QueryRuntimeException(fmt::format( auto password_tv = self_.password()->Accept(evaluator);
"Password must be a string, not '{}'", password_tv.type())); if (!password_tv.IsString()) {
throw QueryRuntimeException("Password must be a string, not '{}'!",
password_tv.type());
}
password = password_tv.ValueString();
} }
// All of the following operations are done with a lock. auto &auth = *ctx.auth_;
std::lock_guard<std::mutex> guard(ctx.auth_->WithLock()); std::lock_guard<std::mutex> lock(auth.WithLock());
std::experimental::optional<auth::User> user; switch (self_.action()) {
if (self_.is_create()) { case AuthQuery::Action::CREATE_USER: {
// Create a new user. if (!password) {
user = ctx.auth_->AddUser(self_.username()); throw QueryRuntimeException(
"Password must be provided when creating a user!");
}
auto user = auth.AddUser(self_.user());
if (!user) { if (!user) {
throw QueryRuntimeException( throw QueryRuntimeException("User '{}' already exists!",
fmt::format("User '{}' already exists!", self_.username())); self_.user());
} }
} else { user->UpdatePassword(*password);
// Update an existing user. if (!auth.SaveUser(*user)) {
user = ctx.auth_->GetUser(self_.username()); throw QueryRuntimeException("Couldn't save user '{}'!", self_.user());
}
break;
}
case AuthQuery::Action::DROP_USER: {
auto user = auth.GetUser(self_.user());
if (!user) { if (!user) {
throw QueryRuntimeException( throw QueryRuntimeException("User '{}' doesn't exist!", self_.user());
fmt::format("User '{}' doesn't exist!", self_.username()));
} }
if (!auth.RemoveUser(self_.user())) {
throw QueryRuntimeException("Couldn't remove user '{}'!",
self_.user());
} }
break;
// Set the password and save the user. }
user->UpdatePassword(password_tv.Value<std::string>()); case AuthQuery::Action::SET_PASSWORD: {
if (!ctx.auth_->SaveUser(*user)) { if (!password) {
throw QueryRuntimeException( throw QueryRuntimeException("Password must be provided!");
fmt::format("Couldn't save user '{}'!", self_.username())); }
auto user = auth.GetUser(self_.user());
if (!user) {
throw QueryRuntimeException("User '{}' doesn't exist!", self_.user());
}
user->UpdatePassword(*password);
if (!auth.SaveUser(*user)) {
throw QueryRuntimeException("Couldn't set password for user '{}'!",
self_.user());
}
break;
}
case AuthQuery::Action::CREATE_ROLE:
case AuthQuery::Action::DROP_ROLE:
case AuthQuery::Action::SHOW_ROLES:
case AuthQuery::Action::SHOW_USERS:
case AuthQuery::Action::GRANT_ROLE:
case AuthQuery::Action::REVOKE_ROLE:
case AuthQuery::Action::GRANT_PRIVILEGE:
case AuthQuery::Action::DENY_PRIVILEGE:
case AuthQuery::Action::REVOKE_PRIVILEGE:
case AuthQuery::Action::SHOW_GRANTS:
case AuthQuery::Action::SHOW_ROLE_FOR_USER:
case AuthQuery::Action::SHOW_USERS_FOR_ROLE:
throw utils::NotYetImplemented("user auth");
} }
return false; return false;
} }
void Reset() override {} void Reset() override {
LOG(FATAL) << "AuthHandler cursor should never be reset";
}
private: private:
const ModifyUser &self_; const AuthHandler &self_;
}; };
std::unique_ptr<Cursor> ModifyUser::MakeCursor( std::unique_ptr<Cursor> AuthHandler::MakeCursor(
database::GraphDbAccessor &) const { database::GraphDbAccessor &db) const {
return std::make_unique<ModifyUserCursor>(*this); return std::make_unique<AuthHandlerCursor>(*this);
} }
bool DropUser::Accept(HierarchicalLogicalOperatorVisitor &visitor) { WITHOUT_SINGLE_INPUT(AuthHandler)
return visitor.Visit(*this);
}
WITHOUT_SINGLE_INPUT(DropUser)
class DropUserCursor : public Cursor {
public:
DropUserCursor(const DropUser &self) : self_(self) {}
bool Pull(Frame &, Context &ctx) override {
if (ctx.in_explicit_transaction_) {
throw UserModificationInMulticommandTxException();
}
// All of the following operations are done with a lock.
std::lock_guard<std::mutex> guard(ctx.auth_->WithLock());
// Check if all users exist.
for (const auto &username : self_.usernames()) {
auto user = ctx.auth_->GetUser(username);
if (!user) {
throw QueryRuntimeException(
fmt::format("User '{}' doesn't exist!", username));
}
}
// Delete all users.
std::vector<std::string> failed;
for (const auto &username : self_.usernames()) {
if (!ctx.auth_->RemoveUser(username)) {
failed.push_back(username);
}
}
// Check for failures.
if (failed.size() > 0) {
throw QueryRuntimeException(fmt::format("Couldn't remove users: '{}'!",
utils::Join(failed, "', '")));
}
return false;
}
void Reset() override {}
private:
const DropUser &self_;
};
std::unique_ptr<Cursor> DropUser::MakeCursor(
database::GraphDbAccessor &) const {
return std::make_unique<DropUserCursor>(*this);
}
CreateStream::CreateStream(std::string stream_name, Expression *stream_uri, CreateStream::CreateStream(std::string stream_name, Expression *stream_uri,
Expression *stream_topic, Expression *transform_uri, Expression *stream_topic, Expression *transform_uri,

View File

@ -102,8 +102,7 @@ class PullRemote;
class Synchronize; class Synchronize;
class Cartesian; class Cartesian;
class PullRemoteOrderBy; class PullRemoteOrderBy;
class ModifyUser; class AuthHandler;
class DropUser;
class CreateStream; class CreateStream;
class DropStream; class DropStream;
class ShowStreams; class ShowStreams;
@ -122,9 +121,9 @@ using LogicalOperatorCompositeVisitor = ::utils::CompositeVisitor<
Cartesian, PullRemoteOrderBy>; Cartesian, PullRemoteOrderBy>;
using LogicalOperatorLeafVisitor = using LogicalOperatorLeafVisitor =
::utils::LeafVisitor<Once, CreateIndex, ModifyUser, DropUser, ::utils::LeafVisitor<Once, CreateIndex, AuthHandler, CreateStream,
CreateStream, DropStream, ShowStreams, DropStream, ShowStreams, StartStopStream,
StartStopStream, StartStopAllStreams, TestStream>; StartStopAllStreams, TestStream>;
/** /**
* @brief Base class for hierarhical visitors of @c LogicalOperator class * @brief Base class for hierarhical visitors of @c LogicalOperator class
@ -1969,6 +1968,114 @@ and returns true, once.")
cpp<#) cpp<#)
(:serialize :capnp)) (:serialize :capnp))
(lcp:define-class auth-handler (logical-operator)
((action "AuthQuery::Action" :reader t
:capnp-init nil
:capnp-type "Ast.AuthQuery.Action"
:capnp-save (lcp:capnp-save-enum "::query::capnp::AuthQuery::Action"
"AuthQuery::Action"
'(create-role drop-role show-roles
create-user set-password
drop-user show-users grant-role
revoke-role grant-privilege
deny-privilege revoke-privilege
show-grants show-role-for-user
show-users-for-role))
:capnp-load (lcp:capnp-load-enum "::query::capnp::AuthQuery::Action"
"AuthQuery::Action"
'(create-role drop-role show-roles
create-user set-password
drop-user show-users grant-role
revoke-role grant-privilege
deny-privilege revoke-privilege
show-grants show-role-for-user
show-users-for-role)))
(user "std::string" :reader t)
(role "std::string" :reader t)
(user-or-role "std::string" :reader t)
(password "Expression *" :reader t
:capnp-type "Ast.Tree" :capnp-init nil
:capnp-save #'save-ast-pointer
:capnp-load (load-ast-pointer "Expression *"))
(privileges "std::vector<AuthQuery::Privilege>" :reader t
:capnp-type "List(Ast.AuthQuery.Privilege)"
:capnp-save
(lambda (builder member-name)
#>cpp
for (size_t i = 0; i < ${member-name}.size(); ++i) {
switch (privileges_[i]) {
case AuthQuery::Privilege::CREATE:
${builder}.set(i, query::capnp::AuthQuery::Privilege::CREATE);
break;
case AuthQuery::Privilege::DELETE:
${builder}.set(i, query::capnp::AuthQuery::Privilege::DELETE);
break;
case AuthQuery::Privilege::MATCH:
${builder}.set(i, query::capnp::AuthQuery::Privilege::MATCH);
break;
case AuthQuery::Privilege::MERGE:
${builder}.set(i, query::capnp::AuthQuery::Privilege::MERGE);
break;
case AuthQuery::Privilege::SET:
${builder}.set(i, query::capnp::AuthQuery::Privilege::SET);
break;
case AuthQuery::Privilege::AUTH:
${builder}.set(i, query::capnp::AuthQuery::Privilege::AUTH);
break;
case AuthQuery::Privilege::STREAM:
${builder}.set(i, query::capnp::AuthQuery::Privilege::STREAM);
break;
}
}
cpp<#)
:capnp-load
(lambda (reader member-name)
#>cpp
for (auto privilege : ${reader}) {
switch (privilege) {
case query::capnp::AuthQuery::Privilege::CREATE:
${member-name}.push_back(AuthQuery::Privilege::CREATE);
break;
case query::capnp::AuthQuery::Privilege::DELETE:
${member-name}.push_back(AuthQuery::Privilege::DELETE);
break;
case query::capnp::AuthQuery::Privilege::MATCH:
${member-name}.push_back(AuthQuery::Privilege::MATCH);
break;
case query::capnp::AuthQuery::Privilege::MERGE:
${member-name}.push_back(AuthQuery::Privilege::MERGE);
break;
case query::capnp::AuthQuery::Privilege::SET:
${member-name}.push_back(AuthQuery::Privilege::SET);
break;
case query::capnp::AuthQuery::Privilege::AUTH:
${member-name}.push_back(AuthQuery::Privilege::AUTH);
break;
case query::capnp::AuthQuery::Privilege::STREAM:
${member-name}.push_back(AuthQuery::Privilege::STREAM);
break;
}
}
cpp<#)))
(:public
#>cpp
AuthHandler(AuthQuery::Action action, std::string user, std::string role,
std::string user_or_role, Expression * password,
std::vector<AuthQuery::Privilege> privileges);
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
std::unique_ptr<Cursor> MakeCursor(database::GraphDbAccessor & db)
const override;
virtual std::vector<Symbol> ModifiedSymbols(const SymbolTable &)
const override { return {}; }
bool HasSingleInput() const override;
std::shared_ptr<LogicalOperator> input() const override;
void set_input(std::shared_ptr<LogicalOperator>) override;
cpp<#)
(:protected #>cpp AuthHandler() {} cpp<#)
(:serialize :capnp))
(lcp:define-class unwind (logical-operator) (lcp:define-class unwind (logical-operator)
((input "std::shared_ptr<LogicalOperator>" ((input "std::shared_ptr<LogicalOperator>"
:capnp-save #'save-operator-pointer :capnp-save #'save-operator-pointer
@ -1991,8 +2098,10 @@ Input is optional (unwind can be the first clause in a query).")
database::GraphDbAccessor &db) const override; database::GraphDbAccessor &db) const override;
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override; std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override;
bool HasSingleInput() const override { return true; } bool HasSingleInput() const override {
std::shared_ptr<LogicalOperator> input() const override { return input_; } return true; }
std::shared_ptr<LogicalOperator> input() const override {
return input_; }
void set_input(std::shared_ptr<LogicalOperator> input) override { void set_input(std::shared_ptr<LogicalOperator> input) override {
input_ = input; input_ = input;
} }
@ -2338,70 +2447,6 @@ by having only one result from each worker.")
(:private #>cpp PullRemoteOrderBy() {} cpp<#) (:private #>cpp PullRemoteOrderBy() {} cpp<#)
(:serialize :capnp)) (:serialize :capnp))
(lcp:define-class modify-user (logical-operator)
((input "std::shared_ptr<LogicalOperator>"
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(username "std::string" :reader t)
(password "Expression *" :reader t
:capnp-type "Ast.Tree"
:capnp-init nil
:capnp-save #'save-ast-pointer
:capnp-load (load-ast-pointer "Expression *"))
(is-create :bool :reader t))
(:documentation
"Operator that creates a new database user or modifies an existing one.")
(:public
#>cpp
ModifyUser(std::string username, Expression *password, bool is_create);
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
std::unique_ptr<Cursor> MakeCursor(
database::GraphDbAccessor &db) const override;
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
return std::vector<Symbol>();
}
bool HasSingleInput() const override;
std::shared_ptr<LogicalOperator> input() const override;
void set_input(std::shared_ptr<LogicalOperator> input) override;
cpp<#)
(:private
#>cpp
ModifyUser() {}
cpp<#)
(:serialize :capnp))
(lcp:define-class drop-user (logical-operator)
((input "std::shared_ptr<LogicalOperator>"
:capnp-save #'save-operator-pointer
:capnp-load #'load-operator-pointer)
(usernames "std::vector<std::string>" :reader t))
(:documentation
"Operator that deletes one or more existing database users.")
(:public
#>cpp
DropUser(std::vector<std::string> usernames): usernames_(usernames) {}
bool Accept(HierarchicalLogicalOperatorVisitor &visitor) override;
std::unique_ptr<Cursor> MakeCursor(
database::GraphDbAccessor &db) const override;
std::vector<Symbol> ModifiedSymbols(const SymbolTable &) const override {
return std::vector<Symbol>();
}
bool HasSingleInput() const override;
std::shared_ptr<LogicalOperator> input() const override;
void set_input(std::shared_ptr<LogicalOperator> input) override;
cpp<#)
(:private
#>cpp
DropUser() {}
cpp<#)
(:serialize :capnp))
(lcp:define-class create-stream (logical-operator) (lcp:define-class create-stream (logical-operator)
((stream-name "std::string" :reader t) ((stream-name "std::string" :reader t)
(stream-uri "Expression *" (stream-uri "Expression *"

View File

@ -53,8 +53,7 @@ class UsedSymbolsCollector : public HierarchicalTreeVisitor {
bool Visit(PrimitiveLiteral &) override { return true; } bool Visit(PrimitiveLiteral &) override { return true; }
bool Visit(ParameterLookup &) override { return true; } bool Visit(ParameterLookup &) override { return true; }
bool Visit(query::CreateIndex &) override { return true; } bool Visit(query::CreateIndex &) override { return true; }
bool Visit(query::ModifyUser &) override { return true; } bool Visit(query::AuthQuery &) override { return true; }
bool Visit(query::DropUser &) override { return true; }
bool Visit(query::CreateStream &) override { return true; } bool Visit(query::CreateStream &) override { return true; }
bool Visit(query::DropStream &) override { return true; } bool Visit(query::DropStream &) override { return true; }
bool Visit(query::ShowStreams &) override { return true; } bool Visit(query::ShowStreams &) override { return true; }

View File

@ -402,12 +402,7 @@ class ReturnBodyContext : public HierarchicalTreeVisitor {
return true; return true;
} }
bool Visit(query::ModifyUser &) override { bool Visit(query::AuthQuery &) override {
has_aggregation_.emplace_back(false);
return true;
}
bool Visit(query::DropUser &) override {
has_aggregation_.emplace_back(false); has_aggregation_.emplace_back(false);
return true; return true;
} }

View File

@ -182,15 +182,13 @@ class RuleBasedPlanner {
DCHECK(!input_op) << "Unexpected operator before CreateIndex"; DCHECK(!input_op) << "Unexpected operator before CreateIndex";
input_op = std::make_unique<plan::CreateIndex>( input_op = std::make_unique<plan::CreateIndex>(
create_index->label_, create_index->property_); create_index->label_, create_index->property_);
} else if (auto *modify_user = } else if (auto *auth_query =
dynamic_cast<query::ModifyUser *>(clause)) { dynamic_cast<query::AuthQuery *>(clause)) {
DCHECK(!input_op) << "Unexpected operator before ModifyUser"; DCHECK(!input_op) << "Unexpected operator before AuthQuery";
input_op = std::make_unique<plan::ModifyUser>( input_op = std::make_unique<plan::AuthHandler>(
modify_user->username_, modify_user->password_, auth_query->action_, auth_query->user_, auth_query->role_,
modify_user->is_create_); auth_query->user_or_role_, auth_query->password_,
} else if (auto *drop_user = dynamic_cast<query::DropUser *>(clause)) { auth_query->privileges_);
DCHECK(!input_op) << "Unexpected operator before DropUser";
input_op = std::make_unique<plan::DropUser>(drop_user->usernames_);
} else if (auto *create_stream = } else if (auto *create_stream =
dynamic_cast<query::CreateStream *>(clause)) { dynamic_cast<query::CreateStream *>(clause)) {
DCHECK(!input_op) << "Unexpected operator before CreateStream"; DCHECK(!input_op) << "Unexpected operator before CreateStream";

View File

@ -514,13 +514,8 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
return true; return true;
} }
bool Visit(query::plan::ModifyUser &op) override { bool Visit(query::plan::AuthHandler &op) override {
WithPrintLn([](auto &out) { out << "* ModifyUser "; }); WithPrintLn([](auto &out) { out << "* AuthHandler"; });
return true;
}
bool Visit(query::plan::DropUser &op) override {
WithPrintLn([](auto &out) { out << "* DropUser"; });
return true; return true;
} }

View File

@ -1866,72 +1866,203 @@ TYPED_TEST(CypherMainVisitorTest, UnionAll) {
ASSERT_FALSE(return_clause->body_.distinct); ASSERT_FALSE(return_clause->body_.distinct);
} }
TYPED_TEST(CypherMainVisitorTest, ModifyUser) { template <typename AstGeneratorT>
auto check_modify_user = [](std::string input, std::string username, void check_auth_query(std::string input, AuthQuery::Action action,
std::string user, std::string role,
std::string user_or_role,
std::experimental::optional<TypedValue> password, std::experimental::optional<TypedValue> password,
bool is_create) { std::vector<AuthQuery::Privilege> privileges) {
TypeParam ast_generator(input); AstGeneratorT ast_generator(input);
auto *query = ast_generator.query_; auto *query = ast_generator.query_;
ASSERT_TRUE(query->single_query_); ASSERT_TRUE(query->single_query_ &&
auto *single_query = query->single_query_; query->single_query_->clauses_.size() == 1U);
ASSERT_EQ(single_query->clauses_.size(), 1U); auto auth_query =
auto *create_user = dynamic_cast<ModifyUser *>(single_query->clauses_[0]); dynamic_cast<AuthQuery *>(query->single_query_->clauses_[0]);
ASSERT_TRUE(create_user); EXPECT_EQ(auth_query->action_, action);
EXPECT_EQ(create_user->username_, username); EXPECT_EQ(auth_query->user_, user);
EXPECT_EQ(auth_query->role_, role);
EXPECT_EQ(auth_query->user_or_role_, user_or_role);
ASSERT_EQ(static_cast<bool>(auth_query->password_),
static_cast<bool>(password));
if (password) { if (password) {
ASSERT_NE(create_user->password_, nullptr); CheckLiteral(ast_generator.context_, auth_query->password_, *password);
CheckLiteral(ast_generator.context_, create_user->password_, *password); }
} else { EXPECT_EQ(auth_query->privileges_, privileges);
EXPECT_EQ(create_user->password_, nullptr);
} }
EXPECT_EQ(create_user->is_create_, is_create);
};
check_modify_user("CreaTE UsEr dominik", "dominik", TYPED_TEST(CypherMainVisitorTest, UserOrRoleName) {
std::experimental::nullopt, true); ASSERT_THROW(TypeParam("CREATE ROLE `us|er`"), SyntaxException);
check_modify_user("CreaTE UsEr dominik WIth PaSSWORD 'spomenik'", "dominik", ASSERT_THROW(TypeParam("CREATE ROLE `us er`"), SyntaxException);
"spomenik", true); check_auth_query<TypeParam>("CREATE ROLE `user`",
check_modify_user("CreaTE UsEr dominik WIth PaSSWORD NULL", "dominik", AuthQuery::Action::CREATE_ROLE, "", "user", "",
TypedValue::Null, true); {}, {});
check_modify_user("AlTeR UsEr dominik", "dominik", std::experimental::nullopt, check_auth_query<TypeParam>("CREATE ROLE us___er",
false); AuthQuery::Action::CREATE_ROLE, "", "us___er", "",
check_modify_user("ALtEr UsEr dominik", "dominik", std::experimental::nullopt, {}, {});
false); check_auth_query<TypeParam>("CREATE ROLE `us+er`",
check_modify_user("ALtEr UsEr dominik WIth PaSSWORD 'spomenik'", "dominik", AuthQuery::Action::CREATE_ROLE, "", "us+er", "",
"spomenik", false); {}, {});
check_modify_user("ALtEr UsEr dominik WIth PaSSWORD NULL", "dominik", }
TypedValue::Null, false);
EXPECT_THROW( TYPED_TEST(CypherMainVisitorTest, CreateRole) {
check_modify_user( ASSERT_THROW(TypeParam("CREATE ROLE"), SyntaxException);
"CreaTE UsEr dominik WIth PaSSWORD 'spomenik' PaSSwoRD 'u muzeju'", check_auth_query<TypeParam>("CREATE ROLE rola",
"dominik", "spomenik", true), AuthQuery::Action::CREATE_ROLE, "", "rola", "",
QueryException); {}, {});
EXPECT_THROW(check_modify_user("CreaTE UsEr dominik WIth PaSSWORD 12345", ASSERT_THROW(TypeParam("CREATE ROLE lagano rolamo"), SyntaxException);
"dominik", "spomenik", true), }
TYPED_TEST(CypherMainVisitorTest, DropRole) {
ASSERT_THROW(TypeParam("DROP ROLE"), SyntaxException);
check_auth_query<TypeParam>("DROP ROLE rola", AuthQuery::Action::DROP_ROLE,
"", "rola", "", {}, {});
ASSERT_THROW(TypeParam("DROP ROLE lagano rolamo"), SyntaxException);
}
TYPED_TEST(CypherMainVisitorTest, ShowRoles) {
ASSERT_THROW(TypeParam("SHOW ROLES ROLES"), SyntaxException);
check_auth_query<TypeParam>("SHOW ROLES", AuthQuery::Action::SHOW_ROLES, "",
"", "", {}, {});
}
TYPED_TEST(CypherMainVisitorTest, CreateUser) {
ASSERT_THROW(TypeParam("CREATE USER"), SyntaxException);
ASSERT_THROW(TypeParam("CREATE USER 123"), SyntaxException);
check_auth_query<TypeParam>("CREATE USER user",
AuthQuery::Action::CREATE_USER, "user", "", "",
{}, {});
check_auth_query<TypeParam>("CREATE USER user IDENTIFIED BY 'password'",
AuthQuery::Action::CREATE_USER, "user", "", "",
"password", {});
check_auth_query<TypeParam>("CREATE USER user IDENTIFIED BY ''",
AuthQuery::Action::CREATE_USER, "user", "", "",
"", {});
check_auth_query<TypeParam>("CREATE USER user IDENTIFIED BY null",
AuthQuery::Action::CREATE_USER, "user", "", "",
TypedValue::Null, {});
ASSERT_THROW(TypeParam("CRATE USER user IDENTIFIED BY password"),
SyntaxException); SyntaxException);
ASSERT_THROW(TypeParam("CREATE USER user IDENTIFIED BY 5"), SyntaxException);
ASSERT_THROW(TypeParam("CREATE USER user IDENTIFIED BY "), SyntaxException);
}
TYPED_TEST(CypherMainVisitorTest, SetPassword) {
ASSERT_THROW(TypeParam("SET PASSWORD FOR"), SyntaxException);
ASSERT_THROW(TypeParam("SET PASSWORD FOR user "), SyntaxException);
check_auth_query<TypeParam>("SET PASSWORD FOR user TO null",
AuthQuery::Action::SET_PASSWORD, "user", "", "",
TypedValue::Null, {});
check_auth_query<TypeParam>("SET PASSWORD FOR user TO 'password'",
AuthQuery::Action::SET_PASSWORD, "user", "", "",
"password", {});
ASSERT_THROW(TypeParam("SET PASSWORD FOR user To 5"), SyntaxException);
} }
TYPED_TEST(CypherMainVisitorTest, DropUser) { TYPED_TEST(CypherMainVisitorTest, DropUser) {
auto check_drop_user = [](std::string input, ASSERT_THROW(TypeParam("DROP USER"), SyntaxException);
const std::vector<std::string> &usernames) { check_auth_query<TypeParam>("DROP USER user", AuthQuery::Action::DROP_USER,
TypeParam ast_generator(input); "user", "", "", {}, {});
auto *query = ast_generator.query_; ASSERT_THROW(TypeParam("DROP USER lagano rolamo"), SyntaxException);
ASSERT_TRUE(query->single_query_); }
auto *single_query = query->single_query_;
ASSERT_EQ(single_query->clauses_.size(), 1U);
auto *drop_user = dynamic_cast<DropUser *>(single_query->clauses_[0]);
ASSERT_TRUE(drop_user);
EXPECT_EQ(drop_user->usernames_, usernames);
};
EXPECT_THROW(check_drop_user("DrOp USER", {}), SyntaxException); TYPED_TEST(CypherMainVisitorTest, ShowUsers) {
check_drop_user("DrOP UsEr dominik", {"dominik"}); ASSERT_THROW(TypeParam("SHOW USERS ROLES"), SyntaxException);
check_drop_user("DrOP USER dominik , spomenik", {"dominik", "spomenik"}); check_auth_query<TypeParam>("SHOW USERS", AuthQuery::Action::SHOW_USERS, "",
EXPECT_THROW( "", "", {}, {});
check_drop_user("DrOP USER dominik, , spomenik", {"dominik", "spomenik"}), }
SyntaxException);
check_drop_user("DrOP USER dominik , spomenik , jackie, jackie , johnny", TYPED_TEST(CypherMainVisitorTest, GrantRole) {
{"dominik", "spomenik", "jackie", "jackie", "johnny"}); ASSERT_THROW(TypeParam("GRANT ROLE"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT ROLE role"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT ROLE role TO"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT ROLE TO user"), SyntaxException);
check_auth_query<TypeParam>("GRANT ROLE role TO user",
AuthQuery::Action::GRANT_ROLE, "user", "role", "",
{}, {});
}
TYPED_TEST(CypherMainVisitorTest, RevokeRole) {
ASSERT_THROW(TypeParam("REVOKE ROLE"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE ROLE role"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE ROLE role FROM"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE ROLE FROM user"), SyntaxException);
check_auth_query<TypeParam>("REVOKE ROLE role FROM user",
AuthQuery::Action::REVOKE_ROLE, "user", "role",
"", {}, {});
}
TYPED_TEST(CypherMainVisitorTest, GrantPrivilege) {
ASSERT_THROW(TypeParam("GRANT"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT TO user"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT BLABLA TO user"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT MATCH, TO user"), SyntaxException);
ASSERT_THROW(TypeParam("GRANT MATCH, BLABLA TO user"), SyntaxException);
check_auth_query<TypeParam>("GRANT MATCH TO user",
AuthQuery::Action::GRANT_PRIVILEGE, "", "",
"user", {}, {AuthQuery::Privilege::MATCH});
check_auth_query<TypeParam>(
"GRANT MATCH, AUTH TO user", AuthQuery::Action::GRANT_PRIVILEGE, "", "",
"user", {}, {AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
}
TYPED_TEST(CypherMainVisitorTest, DenyPrivilege) {
ASSERT_THROW(TypeParam("DENY"), SyntaxException);
ASSERT_THROW(TypeParam("DENY TO user"), SyntaxException);
ASSERT_THROW(TypeParam("DENY BLABLA TO user"), SyntaxException);
ASSERT_THROW(TypeParam("DENY MATCH, TO user"), SyntaxException);
ASSERT_THROW(TypeParam("DENY MATCH, BLABLA TO user"), SyntaxException);
check_auth_query<TypeParam>("DENY MATCH TO user",
AuthQuery::Action::DENY_PRIVILEGE, "", "",
"user", {}, {AuthQuery::Privilege::MATCH});
check_auth_query<TypeParam>(
"DENY MATCH, AUTH TO user", AuthQuery::Action::DENY_PRIVILEGE, "", "",
"user", {}, {AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
}
TYPED_TEST(CypherMainVisitorTest, RevokePrivilege) {
ASSERT_THROW(TypeParam("REVOKE"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE FROM user"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE BLABLA FROM user"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE MATCH, FROM user"), SyntaxException);
ASSERT_THROW(TypeParam("REVOKE MATCH, BLABLA FROM user"), SyntaxException);
check_auth_query<TypeParam>("REVOKE MATCH FROM user",
AuthQuery::Action::REVOKE_PRIVILEGE, "", "",
"user", {}, {AuthQuery::Privilege::MATCH});
check_auth_query<TypeParam>(
"REVOKE MATCH, AUTH FROM user", AuthQuery::Action::REVOKE_PRIVILEGE, "",
"", "user", {},
{AuthQuery::Privilege::MATCH, AuthQuery::Privilege::AUTH});
check_auth_query<TypeParam>(
"REVOKE ALL PRIVILEGES FROM user", AuthQuery::Action::REVOKE_PRIVILEGE,
"", "", "user", {},
{AuthQuery::Privilege::CREATE, AuthQuery::Privilege::DELETE,
AuthQuery::Privilege::MATCH, AuthQuery::Privilege::MERGE,
AuthQuery::Privilege::SET, AuthQuery::Privilege::AUTH,
AuthQuery::Privilege::STREAM});
}
TYPED_TEST(CypherMainVisitorTest, ShowGrants) {
ASSERT_THROW(TypeParam("SHOW GRANTS FOR"), SyntaxException);
check_auth_query<TypeParam>("SHOW GRANTS FOR user",
AuthQuery::Action::SHOW_GRANTS, "", "", "user",
{}, {});
ASSERT_THROW(TypeParam("SHOW GRANTS FOR user1, user2"), SyntaxException);
}
TYPED_TEST(CypherMainVisitorTest, ShowRoleForUser) {
ASSERT_THROW(TypeParam("SHOW ROLE FOR USER"), SyntaxException);
check_auth_query<TypeParam>("SHOW ROLE FOR USER user",
AuthQuery::Action::SHOW_ROLE_FOR_USER, "user", "",
"", {}, {});
ASSERT_THROW(TypeParam("SHOW ROLE FOR USER user1, user2"), SyntaxException);
}
TYPED_TEST(CypherMainVisitorTest, ShowUsersForRole) {
ASSERT_THROW(TypeParam("SHOW USERS FOR ROLE"), SyntaxException);
check_auth_query<TypeParam>("SHOW USERS FOR ROLE role",
AuthQuery::Action::SHOW_USERS_FOR_ROLE, "",
"role", "", {}, {});
ASSERT_THROW(TypeParam("SHOW USERS FOR ROLE role1, role2"), SyntaxException);
} }
TYPED_TEST(CypherMainVisitorTest, CreateStream) { TYPED_TEST(CypherMainVisitorTest, CreateStream) {

View File

@ -574,10 +574,9 @@ auto GetMerge(AstStorage &storage, Pattern *pattern, OnMatch on_match,
#define EXTRACT(variable, list, expr) \ #define EXTRACT(variable, list, expr) \
storage.Create<query::Extract>(storage.Create<query::Identifier>(variable), \ storage.Create<query::Extract>(storage.Create<query::Identifier>(variable), \
list, expr) list, expr)
#define CREATE_USER(username, password) \ #define AUTH_QUERY(action, user, role, user_or_role, password, privileges) \
storage.Create<query::ModifyUser>((username), LITERAL(password), true) storage.Create<query::AuthQuery>((action), (user), (role), (user_or_role), \
#define ALTER_USER(username, password) \ LITERAL(password), (privileges))
storage.Create<query::ModifyUser>((username), LITERAL(password), false)
#define DROP_USER(usernames) storage.Create<query::DropUser>((usernames)) #define DROP_USER(usernames) storage.Create<query::DropUser>((usernames))
#define CREATE_STREAM(stream_name, stream_uri, stream_topic, transform_uri, \ #define CREATE_STREAM(stream_name, stream_uri, stream_topic, transform_uri, \
batch_interval, batch_size) \ batch_interval, batch_size) \

View File

@ -132,8 +132,7 @@ class PlanChecker : public HierarchicalLogicalOperatorVisitor {
PRE_VISIT(PullRemoteOrderBy); PRE_VISIT(PullRemoteOrderBy);
VISIT(ModifyUser); VISIT(AuthHandler);
VISIT(DropUser);
VISIT(CreateStream); VISIT(CreateStream);
VISIT(DropStream); VISIT(DropStream);
@ -377,6 +376,37 @@ class ExpectScanAllByLabelPropertyRange
std::experimental::optional<Bound> upper_bound_; std::experimental::optional<Bound> upper_bound_;
}; };
class ExpectAuthHandler : public OpChecker<AuthHandler> {
public:
ExpectAuthHandler(query::AuthQuery::Action action, std::string user,
std::string role, std::string user_or_role,
query::Expression *password,
std::vector<query::AuthQuery::Privilege> privileges)
: action_(action),
user_(user),
role_(role),
user_or_role_(user_or_role),
password_(password),
privileges_(privileges) {}
void ExpectOp(AuthHandler &auth_handler, const SymbolTable &) override {
EXPECT_EQ(auth_handler.action(), action_);
EXPECT_EQ(auth_handler.user(), user_);
EXPECT_EQ(auth_handler.role(), role_);
EXPECT_EQ(auth_handler.user_or_role(), user_or_role_);
EXPECT_TRUE(auth_handler.password());
EXPECT_EQ(auth_handler.privileges(), privileges_);
}
private:
query::AuthQuery::Action action_;
std::string user_;
std::string role_;
std::string user_or_role_;
query::Expression *password_{nullptr};
std::vector<query::AuthQuery::Privilege> privileges_;
};
class ExpectCreateIndex : public OpChecker<CreateIndex> { class ExpectCreateIndex : public OpChecker<CreateIndex> {
public: public:
ExpectCreateIndex(storage::Label label, storage::Property property) ExpectCreateIndex(storage::Label label, storage::Property property)
@ -623,36 +653,6 @@ class Planner {
std::unique_ptr<LogicalOperator> plan_; std::unique_ptr<LogicalOperator> plan_;
}; };
class ExpectModifyUser : public OpChecker<ModifyUser> {
public:
ExpectModifyUser(std::string username, bool is_create)
: username_(username), is_create_(is_create) {}
void ExpectOp(ModifyUser &modify_user, const SymbolTable &) override {
EXPECT_EQ(username_, modify_user.username());
// TODO(mtomic): proper password verification
EXPECT_NE(dynamic_cast<query::Expression *>(modify_user.password()),
nullptr);
EXPECT_EQ(is_create_, modify_user.is_create());
}
private:
std::string username_;
bool is_create_;
};
class ExpectDropUser : public OpChecker<DropUser> {
public:
ExpectDropUser(std::vector<std::string> usernames) : usernames_(usernames) {}
void ExpectOp(DropUser &drop_user, const SymbolTable &) override {
EXPECT_EQ(usernames_, drop_user.usernames());
}
private:
std::vector<std::string> usernames_;
};
void SavePlan(const LogicalOperator &plan, ::capnp::MessageBuilder *message) { void SavePlan(const LogicalOperator &plan, ::capnp::MessageBuilder *message) {
auto builder = message->initRoot<query::plan::capnp::LogicalOperator>(); auto builder = message->initRoot<query::plan::capnp::LogicalOperator>();
LogicalOperator::SaveHelper helper; LogicalOperator::SaveHelper helper;
@ -2076,8 +2076,9 @@ TYPED_TEST(TestPlanner, WhereIndexedLabelPropertyRange) {
AstStorage storage; AstStorage storage;
auto lit_42 = LITERAL(42); auto lit_42 = LITERAL(42);
auto n_prop = PROPERTY_LOOKUP("n", property); auto n_prop = PROPERTY_LOOKUP("n", property);
auto check_planned_range = [&label, &property, &dba]( auto check_planned_range = [&label, &property, &dba](const auto &rel_expr,
const auto &rel_expr, auto lower_bound, auto upper_bound) { auto lower_bound,
auto upper_bound) {
// Shadow the first storage, so that the query is created in this one. // Shadow the first storage, so that the query is created in this one.
AstStorage storage; AstStorage storage;
QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", label))), WHERE(rel_expr), QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", label))), WHERE(rel_expr),
@ -2318,36 +2319,25 @@ TYPED_TEST(TestPlanner, ReturnAsteriskOmitsLambdaSymbols) {
} }
} }
TYPED_TEST(TestPlanner, ModifyUser) { TYPED_TEST(TestPlanner, AuthQuery) {
{ // Check if everything is properly forwarded from ast node to the operator
// Test CREATE USER user WITH PASSWORD 'password'
FakeDbAccessor dba; FakeDbAccessor dba;
AstStorage storage; AstStorage storage;
QUERY(SINGLE_QUERY(CREATE_USER("user", "password"))); QUERY(SINGLE_QUERY(AUTH_QUERY(query::AuthQuery::Action::DROP_ROLE, "user",
CheckPlan<TypeParam>(storage, ExpectModifyUser("user", true)); "role", "user_or_role", LITERAL("password"),
auto expected = std::vector<query::AuthQuery::Privilege>(
ExpectDistributed(MakeCheckers(ExpectModifyUser("user", true))); {query::AuthQuery::Privilege::MATCH,
CheckDistributedPlan<TypeParam>(storage, expected); query::AuthQuery::Privilege::AUTH}))));
} CheckPlan<TypeParam>(
{ storage, ExpectAuthHandler(query::AuthQuery::Action::DROP_ROLE, "user",
// Test ALTER USER user WITH PASSWORD 'password' "role", "user_or_role", LITERAL("password"),
FakeDbAccessor dba; {query::AuthQuery::Privilege::MATCH,
AstStorage storage; query::AuthQuery::Privilege::AUTH}));
QUERY(SINGLE_QUERY(ALTER_USER("user", "password"))); auto expected = ExpectDistributed(MakeCheckers(
CheckPlan<TypeParam>(storage, ExpectModifyUser("user", false)); ExpectAuthHandler(query::AuthQuery::Action::DROP_ROLE, "user", "role",
auto expected = "user_or_role", LITERAL("password"),
ExpectDistributed(MakeCheckers(ExpectModifyUser("user", false))); {query::AuthQuery::Privilege::MATCH,
CheckDistributedPlan<TypeParam>(storage, expected); query::AuthQuery::Privilege::AUTH})));
}
}
TYPED_TEST(TestPlanner, DropUser) {
// Test DROP USER user1, user2, user3
AstStorage storage;
std::vector<std::string> usernames({"user1", "user2", "user3"});
QUERY(SINGLE_QUERY(DROP_USER(usernames)));
CheckPlan<TypeParam>(storage, ExpectDropUser(usernames));
auto expected = ExpectDistributed(MakeCheckers(ExpectDropUser(usernames)));
CheckDistributedPlan<TypeParam>(storage, expected); CheckDistributedPlan<TypeParam>(storage, expected);
} }