Is it possible to do privilege elevation as part of a computed field so that only this computed field can do things others can’t?
We have a CMS where we can define a “formula”. With the help of the following code, the “formula” can be executed, but the code is quite sensitive from a security perspective.
So, the idea is to assign a role role_cmsFunctionCaller to the computed field, and this role has admin privileges.
Is that somehow possible? And if not, what options do we have?
There are no special Roles for computed fields, whatever privileges exist in the current context when reading the document will apply when executing the computed field.
call predicates for Functions have the same parameters as the Function itself, so you should be able to have elevated privileges assigned to the Function while being very specific about who can use the Function.
Does that help?
// FSL
@role(FormulaFnRole)
function formula1(doc) { /*...*/ }
@role(FormulaFnRole)
function formula2(doc) { /*...*/ }
// ...
@role(FormulaFnRole)
function formulaN(doc) { /*...*/ }
role FormulaFnRole {
privileges CollectionA { /*...*/ }
privileges CollectionB { /*...*/ }
privileges CollectionC { /*...*/ }
// ...
}
role UserLoggedIn {
membership User
privileges formula1 {
call {
predicate (doc => {
let me = Query.identity()
// Can create logic that depends on the caller's identity and
// the content of the doc passing into the function.
})
}
}
// add `call` privileges for other formulas
}
Yeah, I wanted to avoid assigning all functions to the Formula Role because, with the solution you mentioned, the role would need to be updated repeatedly if new CMS functions were created.
That was the point from which I was coming: a role assigned to a computed function would solve this problem quite nicely. (I learned now from you that this is unfortunately not possible.) Do you have another idea to achieve that?
Alternatively, I would be glad if you could pass this as a feature request to the team @ptpaterson
Even if you could apply a role to the computed field, you would need to update the Role to add calling the new formula every time you create a new one.
What if you created a wrapper function that only calls formula functions? You could give that the “server” role, which is a little scary but should be okay if you put appropriate guards around it. You want to be absolutely sure you’re only using it with authorized “formula” functions and no other functions.
// FSL
@role(server)
function useFormula(formulaName, doc) {
Function(formulaName)(doc)
}
function formula1(doc) { /*...*/ }
function formula2(doc) { /*...*/ }
// ...
function formulaN(doc) { /*...*/ }
role UserLoggedIn {
membership User
privileges useFormula {
call {
predicate ((formulaName, doc) => {
let me = Query.identity()
// between the identity and doc, be CERTAIN that the
// function named may be called
})
}
}
// add `call` privileges for other formulas
}
Regarding the hypothetical example to apply a role to a computed field: I thought to give the role assigned to the computed field the server role; with that, I wouldn’t need to do any repeating updates.
Regarding your suggestion: Our CMS functions follow a specific naming pattern, so are you aiming in a direction like the following?:
Yea, naming convention is one method I was thinking of. Another could be to include a list of allowed formulas within the user doc or somewhere else. I don’t know, for example, if all users get access to all formulas. I also thought you could add meta data to the formula functions that tag them as formulas. Going by naming convention sounds like you can avoid additional reads, saving on latency and cost. You’ll need to determine if whatever method you use is sound.