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:
Boris Taševski
2022-07-21 14:00:56 +02:00
committed by GitHub
parent 92b4e39b21
commit 5914b97f5e
3 changed files with 203 additions and 10 deletions

View File

@@ -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) {

View File

@@ -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_;
};

View File

@@ -255,7 +255,7 @@ privilege : CREATE
| MODULE_READ
| MODULE_WRITE
| WEBSOCKET
| LABELS = labelList
| LABELS labels=labelList
;
labelList : '*' | listOfLabels ;