T0954 mg expand user and role to hold permissions on labels (#465)
* added FineGrainedAccessPermissions class to model * expanded user and role with fine grained access permissions * fixed grammar
This commit is contained in:
@@ -183,19 +183,137 @@ bool operator==(const Permissions &first, const Permissions &second) {
|
||||
|
||||
bool operator!=(const Permissions &first, const Permissions &second) { return !(first == second); }
|
||||
|
||||
const std::string ASTERISK = "*";
|
||||
|
||||
FineGrainedAccessPermissions::FineGrainedAccessPermissions(const std::unordered_set<std::string> &grants,
|
||||
const std::unordered_set<std::string> &denies)
|
||||
: grants_(grants), denies_(denies) {}
|
||||
|
||||
PermissionLevel FineGrainedAccessPermissions::Has(const std::string &permission) const {
|
||||
if ((denies_.size() == 1 && denies_.find(ASTERISK) != denies_.end()) || denies_.find(permission) != denies_.end()) {
|
||||
return PermissionLevel::DENY;
|
||||
}
|
||||
|
||||
if ((grants_.size() == 1 && grants_.find(ASTERISK) != grants_.end()) || grants_.find(permission) != denies_.end()) {
|
||||
return PermissionLevel::GRANT;
|
||||
}
|
||||
|
||||
return PermissionLevel::NEUTRAL;
|
||||
}
|
||||
|
||||
void FineGrainedAccessPermissions::Grant(const std::string &permission) {
|
||||
if (permission == ASTERISK) {
|
||||
grants_.clear();
|
||||
grants_.insert(permission);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto deniedPermissionIter = denies_.find(permission);
|
||||
|
||||
if (deniedPermissionIter != denies_.end()) {
|
||||
denies_.erase(deniedPermissionIter);
|
||||
}
|
||||
|
||||
if (grants_.size() == 1 && grants_.find(ASTERISK) != grants_.end()) {
|
||||
grants_.erase(ASTERISK);
|
||||
}
|
||||
|
||||
if (grants_.find(permission) == grants_.end()) {
|
||||
grants_.insert(permission);
|
||||
}
|
||||
}
|
||||
|
||||
void FineGrainedAccessPermissions::Revoke(const std::string &permission) {
|
||||
if (permission == ASTERISK) {
|
||||
grants_.clear();
|
||||
denies_.clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto deniedPermissionIter = denies_.find(permission);
|
||||
auto grantedPermissionIter = grants_.find(permission);
|
||||
|
||||
if (deniedPermissionIter != denies_.end()) {
|
||||
denies_.erase(deniedPermissionIter);
|
||||
}
|
||||
|
||||
if (grantedPermissionIter != grants_.end()) {
|
||||
grants_.erase(grantedPermissionIter);
|
||||
}
|
||||
}
|
||||
|
||||
void FineGrainedAccessPermissions::Deny(const std::string &permission) {
|
||||
if (permission == ASTERISK) {
|
||||
denies_.clear();
|
||||
denies_.insert(permission);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto grantedPermissionIter = grants_.find(permission);
|
||||
|
||||
if (grantedPermissionIter != grants_.end()) {
|
||||
grants_.erase(grantedPermissionIter);
|
||||
}
|
||||
|
||||
if (denies_.size() == 1 && denies_.find(ASTERISK) != denies_.end()) {
|
||||
denies_.erase(ASTERISK);
|
||||
}
|
||||
|
||||
if (denies_.find(permission) == denies_.end()) {
|
||||
denies_.insert(permission);
|
||||
}
|
||||
}
|
||||
|
||||
nlohmann::json FineGrainedAccessPermissions::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["grants"] = grants_;
|
||||
data["denies"] = denies_;
|
||||
return data;
|
||||
}
|
||||
|
||||
FineGrainedAccessPermissions FineGrainedAccessPermissions::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load permissions data!");
|
||||
}
|
||||
|
||||
return {data["grants"], data["denies"]};
|
||||
}
|
||||
|
||||
const std::unordered_set<std::string> &FineGrainedAccessPermissions::grants() const { return grants_; }
|
||||
const std::unordered_set<std::string> &FineGrainedAccessPermissions::denies() const { return denies_; }
|
||||
|
||||
bool operator==(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second) {
|
||||
return first.grants() == second.grants() && first.denies() == second.denies();
|
||||
}
|
||||
|
||||
bool operator!=(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second) {
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
Role::Role(const std::string &rolename) : rolename_(utils::ToLowerCase(rolename)) {}
|
||||
|
||||
Role::Role(const std::string &rolename, const Permissions &permissions)
|
||||
: rolename_(utils::ToLowerCase(rolename)), permissions_(permissions) {}
|
||||
Role::Role(const std::string &rolename, const Permissions &permissions,
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions)
|
||||
: rolename_(utils::ToLowerCase(rolename)),
|
||||
permissions_(permissions),
|
||||
fine_grained_access_permissions_(fine_grained_access_permissions) {}
|
||||
|
||||
const std::string &Role::rolename() const { return rolename_; }
|
||||
const Permissions &Role::permissions() const { return permissions_; }
|
||||
Permissions &Role::permissions() { return permissions_; }
|
||||
const FineGrainedAccessPermissions &Role::fine_grained_access_permissions() const {
|
||||
return fine_grained_access_permissions_;
|
||||
}
|
||||
FineGrainedAccessPermissions &Role::fine_grained_access_permissions() { return fine_grained_access_permissions_; }
|
||||
|
||||
nlohmann::json Role::Serialize() const {
|
||||
nlohmann::json data = nlohmann::json::object();
|
||||
data["rolename"] = rolename_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
data["fine_grained_access_permissions"] = fine_grained_access_permissions_.Serialize();
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -203,11 +321,14 @@ Role Role::Deserialize(const nlohmann::json &data) {
|
||||
if (!data.is_object()) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
if (!data["rolename"].is_string() || !data["permissions"].is_object()) {
|
||||
if (!data["rolename"].is_string() || !data["permissions"].is_object() ||
|
||||
!data["fine_grained_access_permissions"].is_object()) {
|
||||
throw AuthException("Couldn't load role data!");
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
return {data["rolename"], permissions};
|
||||
auto fine_grained_access_permissions =
|
||||
FineGrainedAccessPermissions::Deserialize(data["fine_grained_access_permissions"]);
|
||||
return {data["rolename"], permissions, fine_grained_access_permissions};
|
||||
}
|
||||
|
||||
bool operator==(const Role &first, const Role &second) {
|
||||
@@ -216,8 +337,12 @@ bool operator==(const Role &first, const Role &second) {
|
||||
|
||||
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_(utils::ToLowerCase(username)), password_hash_(password_hash), permissions_(permissions) {}
|
||||
User::User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions)
|
||||
: username_(utils::ToLowerCase(username)),
|
||||
password_hash_(password_hash),
|
||||
permissions_(permissions),
|
||||
fine_grained_access_permissions_(fine_grained_access_permissions) {}
|
||||
|
||||
bool User::CheckPassword(const std::string &password) {
|
||||
if (password_hash_.empty()) return true;
|
||||
@@ -266,10 +391,35 @@ Permissions User::GetPermissions() const {
|
||||
return permissions_;
|
||||
}
|
||||
|
||||
FineGrainedAccessPermissions User::GetFineGrainedAccessPermissions() const {
|
||||
if (role_) {
|
||||
std::unordered_set<std::string> resultGrants;
|
||||
|
||||
std::set_union(fine_grained_access_permissions_.grants().begin(), fine_grained_access_permissions_.grants().end(),
|
||||
role_->fine_grained_access_permissions().grants().begin(),
|
||||
role_->fine_grained_access_permissions().grants().end(),
|
||||
std::inserter(resultGrants, resultGrants.begin()));
|
||||
|
||||
std::unordered_set<std::string> resultDenies;
|
||||
|
||||
std::set_union(fine_grained_access_permissions_.denies().begin(), fine_grained_access_permissions_.denies().end(),
|
||||
role_->fine_grained_access_permissions().denies().begin(),
|
||||
role_->fine_grained_access_permissions().denies().end(),
|
||||
std::inserter(resultDenies, resultDenies.begin()));
|
||||
|
||||
return {resultGrants, resultDenies};
|
||||
}
|
||||
return fine_grained_access_permissions_;
|
||||
}
|
||||
|
||||
const std::string &User::username() const { return username_; }
|
||||
|
||||
const Permissions &User::permissions() const { return permissions_; }
|
||||
Permissions &User::permissions() { return permissions_; }
|
||||
const FineGrainedAccessPermissions &User::fine_grained_access_permissions() const {
|
||||
return fine_grained_access_permissions_;
|
||||
}
|
||||
FineGrainedAccessPermissions &User::fine_grained_access_permissions() { return fine_grained_access_permissions_; }
|
||||
|
||||
const Role *User::role() const {
|
||||
if (role_.has_value()) {
|
||||
@@ -283,6 +433,7 @@ nlohmann::json User::Serialize() const {
|
||||
data["username"] = username_;
|
||||
data["password_hash"] = password_hash_;
|
||||
data["permissions"] = permissions_.Serialize();
|
||||
data["fine_grained_access_permissions"] = fine_grained_access_permissions_.Serialize();
|
||||
// The role shouldn't be serialized here, it is stored as a foreign key.
|
||||
return data;
|
||||
}
|
||||
@@ -295,7 +446,9 @@ User User::Deserialize(const nlohmann::json &data) {
|
||||
throw AuthException("Couldn't load user data!");
|
||||
}
|
||||
auto permissions = Permissions::Deserialize(data["permissions"]);
|
||||
return {data["username"], data["password_hash"], permissions};
|
||||
auto fine_grained_access_permissions =
|
||||
FineGrainedAccessPermissions::Deserialize(data["fine_grained_access_permissions"]);
|
||||
return {data["username"], data["password_hash"], permissions, fine_grained_access_permissions};
|
||||
}
|
||||
|
||||
bool operator==(const User &first, const User &second) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <json/json.hpp>
|
||||
|
||||
@@ -88,15 +89,48 @@ bool operator==(const Permissions &first, const Permissions &second);
|
||||
|
||||
bool operator!=(const Permissions &first, const Permissions &second);
|
||||
|
||||
class FineGrainedAccessPermissions final {
|
||||
public:
|
||||
FineGrainedAccessPermissions(const std::unordered_set<std::string> &grants = {},
|
||||
const std::unordered_set<std::string> &denies = {});
|
||||
|
||||
PermissionLevel Has(const std::string &permission) const;
|
||||
|
||||
void Grant(const std::string &permission);
|
||||
|
||||
void Revoke(const std::string &permission);
|
||||
|
||||
void Deny(const std::string &permission);
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
|
||||
/// @throw AuthException if unable to deserialize.
|
||||
static FineGrainedAccessPermissions Deserialize(const nlohmann::json &data);
|
||||
|
||||
const std::unordered_set<std::string> &grants() const;
|
||||
const std::unordered_set<std::string> &denies() const;
|
||||
|
||||
private:
|
||||
std::unordered_set<std::string> grants_{};
|
||||
std::unordered_set<std::string> denies_{};
|
||||
};
|
||||
|
||||
bool operator==(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second);
|
||||
|
||||
bool operator!=(const FineGrainedAccessPermissions &first, const FineGrainedAccessPermissions &second);
|
||||
|
||||
class Role final {
|
||||
public:
|
||||
Role(const std::string &rolename);
|
||||
|
||||
Role(const std::string &rolename, const Permissions &permissions);
|
||||
Role(const std::string &rolename, const Permissions &permissions,
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions);
|
||||
|
||||
const std::string &rolename() const;
|
||||
const Permissions &permissions() const;
|
||||
Permissions &permissions();
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions() const;
|
||||
FineGrainedAccessPermissions &fine_grained_access_permissions();
|
||||
|
||||
nlohmann::json Serialize() const;
|
||||
|
||||
@@ -108,6 +142,7 @@ class Role final {
|
||||
private:
|
||||
std::string rolename_;
|
||||
Permissions permissions_;
|
||||
FineGrainedAccessPermissions fine_grained_access_permissions_;
|
||||
};
|
||||
|
||||
bool operator==(const Role &first, const Role &second);
|
||||
@@ -117,7 +152,8 @@ class User final {
|
||||
public:
|
||||
User(const std::string &username);
|
||||
|
||||
User(const std::string &username, const std::string &password_hash, const Permissions &permissions);
|
||||
User(const std::string &username, const std::string &password_hash, const Permissions &permissions,
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions);
|
||||
|
||||
/// @throw AuthException if unable to verify the password.
|
||||
bool CheckPassword(const std::string &password);
|
||||
@@ -130,11 +166,14 @@ class User final {
|
||||
void ClearRole();
|
||||
|
||||
Permissions GetPermissions() const;
|
||||
FineGrainedAccessPermissions GetFineGrainedAccessPermissions() const;
|
||||
|
||||
const std::string &username() const;
|
||||
|
||||
const Permissions &permissions() const;
|
||||
Permissions &permissions();
|
||||
const FineGrainedAccessPermissions &fine_grained_access_permissions() const;
|
||||
FineGrainedAccessPermissions &fine_grained_access_permissions();
|
||||
|
||||
const Role *role() const;
|
||||
|
||||
@@ -149,6 +188,7 @@ class User final {
|
||||
std::string username_;
|
||||
std::string password_hash_;
|
||||
Permissions permissions_;
|
||||
FineGrainedAccessPermissions fine_grained_access_permissions_;
|
||||
std::optional<Role> role_;
|
||||
};
|
||||
|
||||
|
||||
@@ -255,7 +255,7 @@ privilege : CREATE
|
||||
| MODULE_READ
|
||||
| MODULE_WRITE
|
||||
| WEBSOCKET
|
||||
| LABELS = labelList
|
||||
| LABELS labels=labelList
|
||||
;
|
||||
|
||||
labelList : '*' | listOfLabels ;
|
||||
|
||||
Reference in New Issue
Block a user