Assert that every string in a set has an associated key in an object - open-policy-agent

After reading through the Open Policy Agent introduction documentation a few times, I'm having trouble writing a rule which asserts that for every element in a set, the specified object has an associated key.
Here's a simplified example what I'm currently trying to get working
https://play.openpolicyagent.org/p/oWBumjRkWX
package example
my_object = {
"lemon": ""
}
fruits = {
"orange",
"lemon",
"banana"
}
has_key(x, k) { _ = x[k] }
default has_lemon = false
has_lemon = has_key(my_object, "lemon") # this works as you'd expect
default all_fruits_have_entries_in_my_object = false
all_fruits_have_entries_in_my_object { # this is never false for some reason
some fruit
fruits[fruit]
has_key(my_object, fruit) # each fruit have a key in the my_object object
}
From what I understand, has_lemon should be false when the fruits does not contain the "lemon" element and I've tested that this is working. However, I also thought that the all_fruits_have_entries_in_my_object rule should evaluate to false here since my_object is missing keys for "orange" and "banana". Am I doing something stupid here?

Rego is existentially quantified. That means rules are declared to check if there exists some fruit that is a key of the object.
One way to solve the problem: you can first gather all unexpected keys using a comprehension and then count the result:
package example
my_object = {
"lemon": ""
}
fruits = {
"orange",
"lemon",
"banana"
}
has_key(x, k) { _ = x[k] }
default has_lemon = false
has_lemon = has_key(my_object, "lemon") # this works as you'd expect
default all_fruits_have_entries_in_my_object = false
all_fruits_have_entries_in_my_object { # this is never false for some reason
non_fruit_keys := { key | my_object[key]; !fruits[key] }
count(non_fruit_keys) > 0
}
You can evaluate this example in the Rego Playground.
See also the documentation on Universal Quantification in Rego

Related

How to call a value in map element only when it matches another var

I am using the Terraform provider mrparkers/keycloak to attempt to assign Keycloak groups a list of users.
The snippet below creates realms, groups, and users correctly, but I am stumped on the final line for calling a list of users which should belong to the group being looped through.
Anything to point me in the right direction would be hugely appreciated. :)
vars
variable "realms" {
description = "realms"
type = set(string)
default = ["mrpc"]
}
variable "mrpc-groups" {
type = map(object({
name = string
realm = string
members = set(string)
}))
default = {
"0" = {
realm = "mrpc"
name = "mrpc-admins"
members = ["hellfire", "hellfire2"]
},
"1" = {
realm = "mrpc"
name = "mrpc-mods"
members = ["hellfire2"]
}
}
}
variable "mrpc-users" {
type = map(object({
username = string
email = string
first_name = string
last_name = string
realm = string
}))
default = {
"0" = {
realm = "mrpc"
username = "hellfire"
email = "bla#bla.bla"
first_name = "hell"
last_name = "fire"
}
"1" = {
realm = "mrpc"
username = "hellfire2"
email = "bla2#bla.bla"
first_name = "hell2"
last_name = "fire2"
}
}
}
resources
resource "keycloak_realm" "realm" {
for_each = var.realms
realm = each.value
}
resource "keycloak_group" "group" {
for_each = var.mrpc-groups
realm_id = each.value["realm"]
name = each.value["name"]
depends_on = [keycloak_realm.realm]
}
resource "keycloak_user" "user" {
for_each = var.mrpc-users
realm_id = each.value["realm"]
username = each.value["username"]
email = each.value["email"]
first_name = each.value["first_name"]
last_name = each.value["last_name"]
}
resource "keycloak_group_memberships" "group_members" {
for_each = keycloak_group.group
realm_id = each.value["realm_id"]
group_id = each.value["name"]
members = [ "hellfire2" ]
# i want this to be var.mrpc-groups.*.members (* used incorrectly here i think)
# if
# var.mrpc-groups.*.name == each.value["name"]
#
# so that the correct member list in the vars is used when the matching group is being looped over
# any method to get the final outcome is good :)
}
We can use the distinct and flatten functions in conjunction with a for expression within a list constructor to solve this:
distinct(flatten([for key, attrs in var.mrpc_groups : attrs.members]))
As tested locally, this will return the following for your values exactly as requested in the question indicated by var.mrpc-groups.*.members:
members = [
"hellfire",
"hellfire2",
]
The for expression iterates through the variable mrpc_groups map and returns the list(string) value assigned to the members key within each group's key value pairs. The lambda/closure scope variables are simply key and attrs because the context is unclear to me, so I was unsure what a more descriptive name would be.
The returned structure would be a list where each element would be the list assigned to the members key (i.e. [["hellfire", "hellfire2"], ["hellfire2"]]). We use flatten to flatten the list of lists into a single list comprised of the elements of each nested list.
There would still be duplicates in this flattened list, and therefore we use the distinct function to return a list comprised of only unique elements.
For the additional question about assigning the members associated with the group at the current iteration, we can simply implement the following:
members = flatten([for key, attrs in var.mrpc_groups : attrs.members if attrs.name == each.value["name"]])
This will similarly iterate through the map variable of var.mrpc_groups, and construct a list of the members list filtered to only the group matching the name of the current group iterated through keycloak_group.group. We then flatten again because it is also a nested list similar to the first question and answer.
Note that for this additional question it would be easier for you in general and for this answer if you restructured the variable keys to be the name of the group instead of as a nested key value pair.

How to assign variable an alternative value if first assigned value is not present in groovy?

I have a situation where I would like to get the output of a readJson and assign it to the variable but I would like to check if different key is present in case first one is not present. Here if key "Name" is not present then I would like to check if "address" is present and add it as collection.
followig works for key Name" but I would like to check for "address" in case there is no "Name kry in json object
def originals = readJSON text: sourceStagesText
originalconfign = originals.collectEntries { [(it.Name):it] }.asImmutable()
I tired using || operator but it gives true and false value, instead of true or false how can I assign the value of the command that gives the value to the variable for example
originalconfign = (originals.collectEntries { [(it.Name):it] }.asImmutable() || originals.collectEntries { [(it.address):it] }.asImmutable())
how can I assign the value to originalconfign if it finds Name to name and if it does not then to the address?
This corresponds to your code and just a bit shorter:
originalconfig = originals.collectEntries { [it.Name,it] }.asImmutable() ?: originals.collectEntries { [it.address,it] }.asImmutable()
I think there's an issue in your logic.
The second part will work only if first returns null or empty map.
But second will always return an empty map if first one is empty...
I solved it with if else statment but please let me know if anyone have better short option to acheive this:
if (originals.collectEntries { [(it.Name):it] }.asImmutable()){
originalconfig = originals.collectEntries { [(it.Name):it] }.asImmutable()
} else {
originalconfig = originals.collectEntries { [(it.address):it] }.asImmutable()
}

Lua table.insert - strange behaviour when inserting to table

I'm having a bit of a hard time figuring out why my code is behaving the way it is. I have a table named players, structured as key, value pairs. The values are also nested tables (items). Each item, should contain at least one unique identifier, named 'id', contained withing the nested table (key name) 'specs'.
players = {
['identifier_one'] = {
{
["label"] = "Flashlight",
["weight"] = 1,
["name"] = "WEAPON_FLASHLIGHT",
["specs"] = {
["pockets"] = false,
["id"] = "ZT345", --This is our unique identifier
["equip"] = true,
["stack"] = 1,
["craft"] = true
}
},
{
["label"] = "Flashlight",
["weight"] = 1,
["name"] = "WEAPON_FLASHLIGHT",
["specs"] = {
["pockets"] = false,
["id"] = "ACF124",
["equip"] = true,
["stack"] = 1,
["craft"] = true
}
}
},
['another_item'] = {
...
},
}
I've got the data structure writing correctly. However, when I insert a new nested item, it's adding the newItem to the next increment within the table as expected, and at the same time it's overwriting all of the previus indexes. So the previous items ['spec']['id'] becomes the newItem id if that makes sense? So looking at the above layout, the previous id 'ZT345' would become 'ACF124'. Thus making the two items identical.
When I print out what the newItem consists off, everything is perfect and should write to the table as intended. At first, I thought it may have been something related to the encoding the table into json when writing to the database. However, if I print out the players[identifier] table, after inserting the newItem, it has then overwritten all previous indexes. So I'm pretty certain the issue is upon inserting the newItem to my players[identifier]
My code is as follows:
ESX.RegisterServerCallback('zombie-inventory:itemCheck', function(source, cb, item)
--check our item is allowed to be added.
if items[item] then
local src = source
local identifier = getIdentifier(src)
local newItem = items[item]
if string.match(item, 'WEAPON') then
newItem['specs'][1].id = genId(players[identifier])
end
--This point is where the code falls apart.
table.insert(players[identifier], newItem)
MySQL.Async.execute('UPDATE `user_inventory` SET `inventory` = #inventory WHERE `identifier` = #identifier', {
['#identifier'] = identifier,
['#inventory'] = json.encode(players[identifier])
})
cb(true)
else
print(('[inventory] [^3WARNING^7] invalid item for itemCheck cb: %s'):format(item))
end
end)
Just to add, the genId() function is 100% returning a unique id, I've printed the id numerous times, and it's always different.

Parse a complex hash and return changes to keys

I'm using json-compare gem to compare two different json files.
Example file 1:
{"suggestions": [
{
"id1": 1,
"title1": "Test",
"body1": "Test"
}
]
}
Example file 2:
{"suggestions": [
{
"id2": 1,
"title2": "Test",
"body2": "Test"
}
]
}
The gem works well and spits out a hash that looks like this:
{:update=>
{"suggestions" =>
{:update=>
{0=>
{:append=>
{"id2"=>1, "title2"=>"Test", "body2"=>"Test"},
:remove=>
{"id1"=>1, "title1"=>"Test", "body1"=>"Test"},
}
}
}
}
}
How can I parse this and return all the places where json Keys were changed? For the sake of simplicity, how would I put to the console:
id1 changed to id2
title1 changed to title2
body1 changed to body2
For the purpose of what I'm building I don't need to know changes to the values. I just need to know that id1 became id2, etc.
Except if you are relaying on key ordering there is no way to tell that id1 got replaced by id2 and title2 by title1, or that id1 became title1 and id2 became title2. Sounds like you would need specific logic related to the actual key names (in this example searching for different integer suffixes).
Maybe this can be enough for the purpose:
def find_what_changed_in(mhash, result = [])
result << mhash
return if mhash.keys == [:append, :remove]
mhash.keys.each { |k| find_what_changed_in(mhash[k], result) }
result.last
end
find_what_changed_in(changes)
#=> {:append=>{"id2"=>1, "title2"=>"Test", "body2"=>"Test"}, :remove=>{"id1"=>1, "title1"=>"Test", "body1"=>"Test"}}
Where:
changes = {:update=>
{"suggestions" =>
{:update=>
{0=>
{:append=>
{"id2"=>1, "title2"=>"Test", "body2"=>"Test"},
:remove=>
{"id1"=>1, "title1"=>"Test", "body1"=>"Test"},
...

Guide me in writing aggregation (groupBy,orderBy) Lua script for Aerospike

I have a following lua script that groups the data with 'sensorType' and print 'clientId' in each group of 'sensorType'.
function orderby(touples)
local function mapper(rec)
local element = map()
element["clientId"] = rec["clientId"];
element["sensorType"] = rec["sensorType"]
return element
end
local function accumulate(currentList, nextElement)
local sensorType = nextElement["sensorType"]
local clientId = nextElement["clientId"]
if currentList[sensorType] == nil then
currentList[sensorType] = list()
end
list.append(currentList[sensorType],clientId)
return currentList
end
local function mymerge(a, b)
return list.merge(a, b)
end
local function reducer(this, that)
return map.merge(this, that, mymerge)
end
return touples:map(mapper):aggregate(map{}, accumulate):reduce(reducer)
end
I also want groupBy with clientId like 'groupBy sensorType, clientId'. Please help me to prepare a script that can accept any number of columns for groupBy clause and do grouping with that.
currently my result is-
{ BEACON: [ 'client2', 'client2', 'client2', 'client2', 'client2', 'client2' ],
SSID:
[ '100',
'100',
'100',
'100',
'100',
'100',
'100',
'102',
'100',
'100',
'101',
'100' ] }
I want the result in this format -
{ BEACON: [ 'client2' ],
SSID:
[ '100',
'102',
'101', ] }
In your function accumulate, clientId is unconditionally added to currentList. If you don't want redundant data in currentList, you need to check for membership of clientId within currentList.
This is a little tricky if you use a list rather than a set; you'll have to test each element individually:
local t = currentList[sensorType]
local alreadyInList = false
for i = 1, #t do
if t[i] == clientId then
alreadyInList = true
end
end
if not alreadyInList then
list.append(t, clientId)
end
This is rather slow--as currentList[sensorType] grows, it takes that much longer to test if it already contains an element you're trying to add. In many cases it won't make much of a difference, but using a set of elements instead of a list will be much faster (and easier). Sets in Lua are very easy since anything can be a key for a Lua table, even another table. Here's how you can use a table as a set instead of a list:
-- initialize a set as an empty table
if currentSet[sensorType] == nil then
currentSet[sensorType] = {}
end
-- add the data to the set `currentList[sensorType]`
currentSet[sensorType][clientId] = true
-- when appropriate, convert the set back into a list
convertedList = {}
i = 0
for key in pairs(currentSet[sensorType]) do
i = i + 1
convertedList[i] = key
end
Before conversion, currentSet looks like:
{ [sensorType] =
{ ["100"] = true
, ["101"] = true
, ["102"] = true
}
}
After conversion, convertedList looks like:
{ "100", "102", "101" }
(Note that convertedList could come out in any order, since the order of keys within a Lua table is undefined.)

Resources