mirror of https://github.com/Icinga/icinga2.git
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)
|
||||
*permissionFilter = nullptr;
|
||||
|
||||
if (permission.IsEmpty())
|
||||
return;
|
||||
return true;
|
||||
|
||||
bool foundPermission = false;
|
||||
String requiredPermission = permission.ToLower();
|
||||
|
@ -172,8 +186,15 @@ void FilterUtility::CheckPermission(const ApiUser::Ptr& user, const String& perm
|
|||
if (!foundPermission) {
|
||||
Log(LogWarning, "FilterUtility")
|
||||
<< "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:
|
||||
static Type::Ptr TypeFromPluralName(const String& pluralName);
|
||||
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,
|
||||
const ApiUser::Ptr& user, const String& variableName = String());
|
||||
static bool EvaluateFilter(ScriptFrame& frame, Expression *filter,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "base/configtype.hpp"
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
|
@ -189,6 +190,9 @@ bool ObjectQueryHandler::HandleRequest(
|
|||
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) {
|
||||
DictionaryData result1{
|
||||
{ "name", obj->GetName() },
|
||||
|
@ -257,6 +261,51 @@ bool ObjectQueryHandler::HandleRequest(
|
|||
if (!joinedObj)
|
||||
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;
|
||||
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue