mirror of
https://github.com/Icinga/icinga2.git
synced 2025-07-25 14:44:32 +02:00
Merge pull request #9408 from Icinga/bugfix/match-api-permissions-against-join-relations
ObjectQueryHandler: Check user permissions on joined relations
This commit is contained in:
commit
363f4d3fde
@ -124,13 +124,27 @@ static void FilteredAddTarget(ScriptFrame& permissionFrame, Expression *permissi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& permission, Expression **permissionFilter)
|
/**
|
||||||
|
* Checks whether the given API user is granted the given permission
|
||||||
|
*
|
||||||
|
* When you desire an exception to be raised when the given user doesn't have the given permission,
|
||||||
|
* you need to use FilterUtility::CheckPermission().
|
||||||
|
*
|
||||||
|
* @param user ApiUser pointer to the user object you want to check the permission of
|
||||||
|
* @param permission The actual permission you want to check the user permission against
|
||||||
|
* @param permissionFilter Expression pointer that is used as an output buffer for all the filter expressions of the
|
||||||
|
* individual permissions of the given user to be evaluated. It's up to the caller to delete
|
||||||
|
* this pointer when it's not needed any more.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
bool FilterUtility::HasPermission(const ApiUser::Ptr& user, const String& permission, Expression **permissionFilter)
|
||||||
{
|
{
|
||||||
if (permissionFilter)
|
if (permissionFilter)
|
||||||
*permissionFilter = nullptr;
|
*permissionFilter = nullptr;
|
||||||
|
|
||||||
if (permission.IsEmpty())
|
if (permission.IsEmpty())
|
||||||
return;
|
return true;
|
||||||
|
|
||||||
bool foundPermission = false;
|
bool foundPermission = false;
|
||||||
String requiredPermission = permission.ToLower();
|
String requiredPermission = permission.ToLower();
|
||||||
@ -172,8 +186,15 @@ void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& perm
|
|||||||
if (!foundPermission) {
|
if (!foundPermission) {
|
||||||
Log(LogWarning, "FilterUtility")
|
Log(LogWarning, "FilterUtility")
|
||||||
<< "Missing permission: " << requiredPermission;
|
<< "Missing permission: " << requiredPermission;
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_THROW_EXCEPTION(ScriptError("Missing permission: " + requiredPermission));
|
return foundPermission;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& permission, Expression **permissionFilter)
|
||||||
|
{
|
||||||
|
if (!HasPermission(user, permission, permissionFilter)) {
|
||||||
|
BOOST_THROW_EXCEPTION(ScriptError("Missing permission: " + permission.ToLower()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,6 +52,7 @@ class FilterUtility
|
|||||||
public:
|
public:
|
||||||
static Type::Ptr TypeFromPluralName(const String& pluralName);
|
static Type::Ptr TypeFromPluralName(const String& pluralName);
|
||||||
static void CheckPermission(const ApiUser::Ptr& user, const String& permission, Expression **filter = nullptr);
|
static void CheckPermission(const ApiUser::Ptr& user, const String& permission, Expression **filter = nullptr);
|
||||||
|
static bool HasPermission(const ApiUser::Ptr& user, const String& permission, Expression **permissionFilter = nullptr);
|
||||||
static std::vector<Value> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query,
|
static std::vector<Value> GetFilterTargets(const QueryDescription& qd, const Dictionary::Ptr& query,
|
||||||
const ApiUser::Ptr& user, const String& variableName = String());
|
const ApiUser::Ptr& user, const String& variableName = String());
|
||||||
static bool EvaluateFilter(ScriptFrame& frame, Expression *filter,
|
static bool EvaluateFilter(ScriptFrame& frame, Expression *filter,
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "base/configtype.hpp"
|
#include "base/configtype.hpp"
|
||||||
#include <boost/algorithm/string/case_conv.hpp>
|
#include <boost/algorithm/string/case_conv.hpp>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
using namespace icinga;
|
using namespace icinga;
|
||||||
|
|
||||||
@ -189,6 +190,9 @@ bool ObjectQueryHandler::HandleRequest(
|
|||||||
joinAttrs.insert(field.Name);
|
joinAttrs.insert(field.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<Type*, std::pair<bool, Expression::Ptr>> typePermissions;
|
||||||
|
std::unordered_map<Object*, bool> objectAccessAllowed;
|
||||||
|
|
||||||
for (const ConfigObject::Ptr& obj : objs) {
|
for (const ConfigObject::Ptr& obj : objs) {
|
||||||
DictionaryData result1{
|
DictionaryData result1{
|
||||||
{ "name", obj->GetName() },
|
{ "name", obj->GetName() },
|
||||||
@ -257,6 +261,51 @@ bool ObjectQueryHandler::HandleRequest(
|
|||||||
if (!joinedObj)
|
if (!joinedObj)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
Type::Ptr reflectionType = joinedObj->GetReflectionType();
|
||||||
|
Expression::Ptr permissionFilter;
|
||||||
|
|
||||||
|
auto it = typePermissions.find(reflectionType.get());
|
||||||
|
bool granted;
|
||||||
|
|
||||||
|
if (it == typePermissions.end()) {
|
||||||
|
String permission = "objects/query/" + reflectionType->GetName();
|
||||||
|
|
||||||
|
Expression *filter = nullptr;
|
||||||
|
granted = FilterUtility::HasPermission(user, permission, &filter);
|
||||||
|
permissionFilter = filter;
|
||||||
|
|
||||||
|
typePermissions.insert({reflectionType.get(), std::make_pair(granted, permissionFilter)});
|
||||||
|
} else {
|
||||||
|
std::tie(granted, permissionFilter) = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!granted) {
|
||||||
|
// Not authorized
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto relation = objectAccessAllowed.find(joinedObj.get());
|
||||||
|
bool accessAllowed;
|
||||||
|
|
||||||
|
if (relation == objectAccessAllowed.end()) {
|
||||||
|
ScriptFrame permissionFrame(false, new Namespace());
|
||||||
|
|
||||||
|
try {
|
||||||
|
accessAllowed = FilterUtility::EvaluateFilter(permissionFrame, permissionFilter.get(), joinedObj);
|
||||||
|
} catch (const ScriptError& err) {
|
||||||
|
accessAllowed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
objectAccessAllowed.insert({joinedObj.get(), accessAllowed});
|
||||||
|
} else {
|
||||||
|
accessAllowed = relation->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!accessAllowed) {
|
||||||
|
// Access denied
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
String prefix = field.NavigationName;
|
String prefix = field.NavigationName;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user