I am trying to use OPA as authorization service for Trino. I wrote a rego file according to my needs.
package mytrino
import rego.v1
default allow := false
allow if {
input.action.operation == "ExecuteQuery"
not input.action.resource
}
allow if {
input.action.operation == "AccessCatalog"
}
allow if {
input.action.operation == "FilterCatalogs"
}
allow if {
input.action.operation == "FilterSchemas"
}
allow if {
input.action.operation == "SelectFromColumns"
input.action.resource.table.catalogName == "system"
}
allow if {
response := http.send({
"method": "post",
"url": "http://host.docker.internal:8085/api/products/check-table-access",
"headers": {"Content-Type": "application/json"},
"body": {
"user": input.context.identity.user,
"table": input.action.resource.table.tableName
}
})
response.status_code == 200
}
I was checking my API logs and I saw there have been requests that are 100% percent true without the last allow if statement. So I think even though the previous allow if's are true, it evaluate subsequent statements?
How can I prevent final allow if statement to make http requests if the previous ones are true?
Thank you a lot!
Rego rules are all evaluated at once, so if you want to block the calling of http.send
then you can introduce a dependency on the other rules before calling it with a function like this:
package play
import rego.v1
default allow := false
# this will be true if the conditions that can be checked locally are validated ok
allow if deterministic_allow
allow if check_table_access(
# this enforces a dependency of this rule on deterministic_allow
# and so it will not be called until deterministic_allow is known
deterministic_allow,
input.context.identity.user,
input.action.resource.table.tableName,
)
default deterministic_allow := false
deterministic_allow if {
input.action.operation == "ExecuteQuery"
not input.action.resource
}
deterministic_allow if {
input.action.operation == "AccessCatalog"
}
deterministic_allow if {
input.action.operation == "FilterCatalogs"
}
deterministic_allow if {
input.action.operation == "FilterSchemas"
}
deterministic_allow if {
input.action.operation == "SelectFromColumns"
input.action.resource.table.catalogName == "system"
}
# when deterministic_allow is true, then this function just short circuits and returns true
check_table_access(true, _, _) := true
# when deterministic_allow is false, the http call is made to perform the final chance check
check_table_access(false, user, table_name) := result if {
response := http.send({
"method": "post",
"url": "http://host.docker.internal:8085/api/products/check-table-access",
"headers": {"Content-Type": "application/json"},
"body": {
"user": user,
"table": table_name,
},
})
result := response.status_code == 200
}
This is based on 'Expressing OR using helper functions'.