Make auth library case insensitive

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1881
This commit is contained in:
Matej Ferencevic 2019-02-22 13:20:54 +01:00
parent ce3a7d6fc2
commit 7be23896c2
4 changed files with 256 additions and 20 deletions

View File

@ -1,6 +1,7 @@
#include "auth/auth.hpp"
#include "auth/exceptions.hpp"
#include "utils/string.hpp"
namespace auth {
@ -36,7 +37,9 @@ std::experimental::optional<User> Auth::Authenticate(
return user;
}
std::experimental::optional<User> Auth::GetUser(const std::string &username) {
std::experimental::optional<User> Auth::GetUser(
const std::string &username_orig) {
auto username = utils::ToLowerCase(username_orig);
auto existing_user = storage_.Get(kUserPrefix + username);
if (!existing_user) return std::experimental::nullopt;
@ -88,7 +91,8 @@ std::experimental::optional<User> Auth::AddUser(
return new_user;
}
bool Auth::RemoveUser(const std::string &username) {
bool Auth::RemoveUser(const std::string &username_orig) {
auto username = utils::ToLowerCase(username_orig);
if (!storage_.Get(kUserPrefix + username)) return false;
std::vector<std::string> keys(
{kLinkPrefix + username, kUserPrefix + username});
@ -102,7 +106,9 @@ std::vector<auth::User> Auth::AllUsers() {
std::vector<auth::User> ret;
for (auto it = storage_.begin(kUserPrefix); it != storage_.end(kUserPrefix);
++it) {
auto user = GetUser(it->first.substr(kUserPrefix.size()));
auto username = it->first.substr(kUserPrefix.size());
if (username != utils::ToLowerCase(username)) continue;
auto user = GetUser(username);
if (user) {
ret.push_back(*user);
}
@ -114,7 +120,9 @@ bool Auth::HasUsers() {
return storage_.begin(kUserPrefix) != storage_.end(kUserPrefix);
}
std::experimental::optional<Role> Auth::GetRole(const std::string &rolename) {
std::experimental::optional<Role> Auth::GetRole(
const std::string &rolename_orig) {
auto rolename = utils::ToLowerCase(rolename_orig);
auto existing_role = storage_.Get(kRolePrefix + rolename);
if (!existing_role) return std::experimental::nullopt;
@ -144,12 +152,13 @@ std::experimental::optional<Role> Auth::AddRole(const std::string &rolename) {
return new_role;
}
bool Auth::RemoveRole(const std::string &rolename) {
bool Auth::RemoveRole(const std::string &rolename_orig) {
auto rolename = utils::ToLowerCase(rolename_orig);
if (!storage_.Get(kRolePrefix + rolename)) return false;
std::vector<std::string> keys;
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix);
++it) {
if (it->second == rolename) {
if (utils::ToLowerCase(it->second) == rolename) {
keys.push_back(it->first);
}
}
@ -165,6 +174,7 @@ std::vector<auth::Role> Auth::AllRoles() {
for (auto it = storage_.begin(kRolePrefix); it != storage_.end(kRolePrefix);
++it) {
auto rolename = it->first.substr(kRolePrefix.size());
if (rolename != utils::ToLowerCase(rolename)) continue;
auto role = GetRole(rolename);
if (role) {
ret.push_back(*role);
@ -175,11 +185,14 @@ std::vector<auth::Role> Auth::AllRoles() {
return ret;
}
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename) {
std::vector<auth::User> Auth::AllUsersForRole(const std::string &rolename_orig) {
auto rolename = utils::ToLowerCase(rolename_orig);
std::vector<auth::User> ret;
for (auto it = storage_.begin(kLinkPrefix); it != storage_.end(kLinkPrefix);
++it) {
auto username = it->first.substr(kLinkPrefix.size());
if (username != utils::ToLowerCase(username)) continue;
if (it->second != utils::ToLowerCase(it->second)) continue;
if (it->second == rolename) {
auto user = GetUser(username);
if (user) {

View File

@ -7,6 +7,7 @@
#include "auth/crypto.hpp"
#include "auth/exceptions.hpp"
#include "utils/cast.hpp"
#include "utils/string.hpp"
DEFINE_bool(auth_password_permit_null, true,
"Set to false to disable null passwords.");
@ -140,10 +141,11 @@ bool operator!=(const Permissions &first, const Permissions &second) {
return !(first == second);
}
Role::Role(const std::string &rolename) : rolename_(rolename) {}
Role::Role(const std::string &rolename)
: rolename_(utils::ToLowerCase(rolename)) {}
Role::Role(const std::string &rolename, const Permissions &permissions)
: rolename_(rolename), permissions_(permissions) {}
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
const std::string &Role::rolename() const { return rolename_; }
const Permissions &Role::permissions() const { return permissions_; }
@ -172,11 +174,12 @@ bool operator==(const Role &first, const Role &second) {
first.permissions_ == second.permissions_;
}
User::User(const std::string &username) : username_(username) {}
User::User(const std::string &username)
: username_(utils::ToLowerCase(username)) {}
User::User(const std::string &username, const std::string &password_hash,
const Permissions &permissions)
: username_(username),
: username_(utils::ToLowerCase(username)),
password_hash_(password_hash),
permissions_(permissions) {}

View File

@ -251,9 +251,9 @@ def execute_test(memgraph_binary, tester_binary, checker_binary):
# Prepare all users
execute_admin_queries([
"CREATE USER admin IDENTIFIED BY 'admin'",
"GRANT ALL PRIVILEGES TO admin",
"CREATE USER user IDENTIFIED BY 'user'"
"CREATE USER ADmin IDENTIFIED BY 'admin'",
"GRANT ALL PRIVILEGES TO admIN",
"CREATE USER usEr IDENTIFIED BY 'user'"
])
# Find all existing permissions
@ -268,10 +268,10 @@ def execute_test(memgraph_binary, tester_binary, checker_binary):
user_perms = get_permissions(permissions, mask)
print("\033[1;34m~~ Checking queries with privileges: ",
", ".join(user_perms), " ~~\033[0m")
admin_queries = ["REVOKE ALL PRIVILEGES FROM user"]
admin_queries = ["REVOKE ALL PRIVILEGES FROM uSer"]
if len(user_perms) > 0:
admin_queries.append(
"GRANT {} TO user".format(", ".join(user_perms)))
"GRANT {} TO User".format(", ".join(user_perms)))
execute_admin_queries(admin_queries)
authorized, unauthorized = [], []
for query, query_perms in QUERIES:
@ -288,8 +288,8 @@ def execute_test(memgraph_binary, tester_binary, checker_binary):
# Run the user/role permissions test
print("\033[1;36m~~ Starting permissions test ~~\033[0m")
execute_admin_queries([
"CREATE ROLE role",
"REVOKE ALL PRIVILEGES FROM user",
"CREATE ROLE roLe",
"REVOKE ALL PRIVILEGES FROM uSeR",
])
execute_checker(checker_binary, [])
for user_perm in ["GRANT", "DENY", "REVOKE"]:
@ -299,14 +299,14 @@ def execute_test(memgraph_binary, tester_binary, checker_binary):
user_perm, ", role ", role_perm,
"user mapped to role:", mapped, " ~~\033[0m")
if mapped:
execute_admin_queries(["SET ROLE FOR user TO role"])
execute_admin_queries(["SET ROLE FOR USER TO roLE"])
else:
execute_admin_queries(["CLEAR ROLE FOR user"])
user_prep = "FROM" if user_perm == "REVOKE" else "TO"
role_prep = "FROM" if role_perm == "REVOKE" else "TO"
execute_admin_queries([
"{} MATCH {} user".format(user_perm, user_prep),
"{} MATCH {} role".format(role_perm, role_prep)
"{} MATCH {} rOLe".format(role_perm, role_prep)
])
expected = []
perms = [user_perm, role_perm] if mapped else [user_perm]

View File

@ -1,3 +1,4 @@
#include <algorithm>
#include <iostream>
#include <gflags/gflags.h>
@ -459,6 +460,225 @@ TEST_F(AuthWithStorage, UserRoleUniqueName) {
ASSERT_FALSE(auth.AddUser("role"));
}
TEST(AuthWithoutStorage, CaseInsensitivity) {
{
auto user1 = User("test");
auto user2 = User("Test");
ASSERT_EQ(user1, user2);
ASSERT_EQ(user1.username(), user2.username());
ASSERT_EQ(user1.username(), "test");
ASSERT_EQ(user2.username(), "test");
}
{
auto perms = Permissions();
auto user1 = User("test", "pw", perms);
auto user2 = User("Test", "pw", perms);
ASSERT_EQ(user1, user2);
ASSERT_EQ(user1.username(), user2.username());
ASSERT_EQ(user1.username(), "test");
ASSERT_EQ(user2.username(), "test");
}
{
auto role1 = Role("role");
auto role2 = Role("Role");
ASSERT_EQ(role1, role2);
ASSERT_EQ(role1.rolename(), role2.rolename());
ASSERT_EQ(role1.rolename(), "role");
ASSERT_EQ(role2.rolename(), "role");
}
{
auto perms = Permissions();
auto role1 = Role("role", perms);
auto role2 = Role("Role", perms);
ASSERT_EQ(role1, role2);
ASSERT_EQ(role1.rolename(), role2.rolename());
ASSERT_EQ(role1.rolename(), "role");
ASSERT_EQ(role2.rolename(), "role");
}
}
TEST_F(AuthWithStorage, CaseInsensitivity) {
// AddUser
{
auto user = auth.AddUser("Alice", "alice");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
ASSERT_FALSE(auth.AddUser("alice"));
ASSERT_FALSE(auth.AddUser("alicE"));
}
{
auto user = auth.AddUser("BoB", "bob");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "bob");
ASSERT_FALSE(auth.AddUser("bob"));
ASSERT_FALSE(auth.AddUser("bOb"));
}
// Authenticate
{
auto user = auth.Authenticate("alice", "alice");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
}
{
auto user = auth.Authenticate("alICe", "alice");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
}
// GetUser
{
auto user = auth.GetUser("alice");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
}
{
auto user = auth.GetUser("aLicE");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
}
ASSERT_FALSE(auth.GetUser("carol"));
// RemoveUser
{
auto user = auth.AddUser("caRol", "carol");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "carol");
ASSERT_TRUE(auth.RemoveUser("cAROl"));
ASSERT_FALSE(auth.RemoveUser("carol"));
ASSERT_FALSE(auth.GetUser("CAROL"));
}
// AllUsers
{
auto users = auth.AllUsers();
ASSERT_EQ(users.size(), 2);
std::sort(users.begin(), users.end(), [](const auto &a, const auto &b) {
return a.username() < b.username();
});
ASSERT_EQ(users[0].username(), "alice");
ASSERT_EQ(users[1].username(), "bob");
}
// AddRole
{
auto role = auth.AddRole("Moderator");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
ASSERT_FALSE(auth.AddRole("moderator"));
ASSERT_FALSE(auth.AddRole("MODERATOR"));
}
{
auto role = auth.AddRole("adMIN");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "admin");
ASSERT_FALSE(auth.AddRole("Admin"));
ASSERT_FALSE(auth.AddRole("ADMIn"));
}
ASSERT_FALSE(auth.AddRole("ALICE"));
ASSERT_FALSE(auth.AddUser("ModeRAtor"));
// GetRole
{
auto role = auth.GetRole("moderator");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
}
{
auto role = auth.GetRole("MoDERATOR");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
}
ASSERT_FALSE(auth.GetRole("root"));
// RemoveRole
{
auto role = auth.AddRole("RooT");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "root");
ASSERT_TRUE(auth.RemoveRole("rOOt"));
ASSERT_FALSE(auth.RemoveRole("RoOt"));
ASSERT_FALSE(auth.GetRole("RoOt"));
}
// AllRoles
{
auto roles = auth.AllRoles();
ASSERT_EQ(roles.size(), 2);
std::sort(roles.begin(), roles.end(), [](const auto &a, const auto &b) {
return a.rolename() < b.rolename();
});
ASSERT_EQ(roles[0].rolename(), "admin");
ASSERT_EQ(roles[1].rolename(), "moderator");
}
// SaveRole
{
auto role = auth.GetRole("MODErator");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
role->permissions().Grant(auth::Permission::MATCH);
auth.SaveRole(*role);
}
{
auto role = auth.GetRole("modeRATOR");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
ASSERT_EQ(role->permissions().Has(auth::Permission::MATCH),
auth::PermissionLevel::GRANT);
}
// SaveUser
{
auto user = auth.GetUser("aLice");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
auto role = auth.GetRole("moderAtor");
ASSERT_TRUE(role);
ASSERT_EQ(role->rolename(), "moderator");
user->SetRole(*role);
auth.SaveUser(*user);
}
{
auto user = auth.GetUser("aLIce");
ASSERT_TRUE(user);
ASSERT_EQ(user->username(), "alice");
ASSERT_TRUE(user->role());
ASSERT_EQ(user->role()->rolename(), "moderator");
}
// AllUsersForRole
{
auto carol = auth.AddUser("caROl");
ASSERT_TRUE(carol);
ASSERT_EQ(carol->username(), "carol");
auto dave = auth.AddUser("daVe");
ASSERT_TRUE(dave);
ASSERT_EQ(dave->username(), "dave");
auto admin = auth.GetRole("aDMin");
ASSERT_TRUE(admin);
ASSERT_EQ(admin->rolename(), "admin");
carol->SetRole(*admin);
auth.SaveUser(*carol);
dave->SetRole(*admin);
auth.SaveUser(*dave);
}
{
auto users = auth.AllUsersForRole("modeRAtoR");
ASSERT_EQ(users.size(), 1);
ASSERT_EQ(users[0].username(), "alice");
}
{
auto users = auth.AllUsersForRole("AdmiN");
ASSERT_EQ(users.size(), 2);
std::sort(users.begin(), users.end(), [](const auto &a, const auto &b) {
return a.username() < b.username();
});
ASSERT_EQ(users[0].username(), "carol");
ASSERT_EQ(users[1].username(), "dave");
}
}
TEST(AuthWithoutStorage, Crypto) {
auto hash = EncryptPassword("hello");
ASSERT_TRUE(VerifyPassword("hello", hash));