Rego to check if a map, contains a key that is defined in a separate list - open-policy-agent

Given a map, we would like to check if any of the keys in that map matches any item in a list.
In the case of kubernetes annotations being the "map":
metadata:
annotations:
external-dns.alpha.kubernetes.io/hostname: something
external-dns.alpha.kubernetes.io/ttl: 60
Let's say that we want to trigger a violation if external-dns.alpha.kubernetes.io/ttl is used. For that, we would add that item in a list of denied annotations. The value is not relevant.
denied:
- external-dns.alpha.kubernetes.io/ttl
- other-denied-annotation-1
- other-denied-annotation-2
Violation should trigger if any of the denied annotations is present in the .metadata.annotations of the resource.
Although I searched and tried it, I was not able to. The already asked questions do not address this exactly and I was not able to make it work. We are using Gatekeeper.

You'd normally use a set for the "deny" list, and then simply check each annotation for membership:
package play
import future.keywords
denied_annotations := {
"external-dns.alpha.kubernetes.io/ttl",
"other-denied-annotation-1",
"other-denied-annotation-2",
}
deny[reason] {
# iterate over all annotations in input
input.review.object.metadata.annotations[key]
# for each key, check if it's present in deny list -
# if using older OPA, you may need to use
# `denied_annotations[key]` instead, which does the same
key in denied_annotations
reason = sprintf("%s in denied annotations", [key])
}
Playground example here.

Related

freeradius 3 mac auth user group (for configuration simplicity)

I have implemented Radius MAC authentication with Unifi AP and freeradius. I am using Radius primarily to set the vlan that the device should sent to allowing a consolidated SSID to handle multiple vlans. (This is a home network, not an enterprise so I am not concerned with the mac spoofing situation). Any MAC connecting will get a vlan (but non-radius users will go to a guest vlan by default)
The freeradius question is how I can go about assigning a group value to each MAC user definition and then post-auth use that group name to define the specific attributes such as Tunnel-Type, Tunnel-Medium-Type, and Tunnel-Private-Group-Id. I simply want to do this to avoid having to repeat all these tunnel values for each device.
Example (not sure if syntax is right)
authorize file
AABBCCDDEEFFGG Group := "iot", Cleartext-Password := "AABBCCDDEEFFGG"
site-enabled/default (I think it might go here)
if (group == "iot) { #update reply, set Tunnel-* values }
Any guidance someone could provide would be great. All the examples I have found seem to be using the mysql backend and I don't have a need for the additional complexity.
I was able to figure this out using control variables. Here is the solution.
Create a custom attribute in the dictionary file.
ATTRIBUTE VLAN-Group-Name 3000 string
Add users to the authorize file like so
AA-BB-CC-DD-EE-FF Cleartext-Password := "AA-BB-CC-DD-EE-FF", VLAN-Group-Name := "iot"
In your virtual server, in my case it was sites-enabled/default, look for the post-auth section and add code similar to this. You will replace the group-id XXX with your vlan # you want sent back to the AP to be assigned. I placed the code right above the -sql portion.
switch "&control:VLAN-Group-Name" {
case "iot" {
update reply {
Tunnel-Type = 13,
Tunnel-Medium-Type = 6,
Tunnel-Private-Group-Id = XXX
}
}
case "general" {
update reply {
Tunnel-Type = 13,
Tunnel-Medium-Type = 6,
Tunnel-Private-Group-Id = XXX
}
}
}
This allows you to keep the authorize file clean with just users and assign them a group and keep the group values simplified in the post-auth. If you ever needed to change the vlan # of any group just one update and restart.
Cheers!

Is it possible to apply [Rule Chain] after [Data Converter]?

I am currently working on a POC by using ThingsBoard PE.
Our raw data contains [Asset] [Attributes].
Data flow:
[IoT cloud] --https webhook carry raw data--> [ThingsBoard PE HTTP INTEGRATION] --uplink--> [ThingsBoard PE Data Converter]
My question is: is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]? Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
Example data after data converter process:
{
"deviceName": "ABC",
"deviceType": "temperature",
"attributes": {
"asset_id": 6 // <-- the id is used in asset attribute
},
"telemetry": {
"temperature": 39.43
}
}
Answering your two, separate questions:
is it possible to apply [Rule Chain] after [ThingsBoard PE Data Converter]?
Yes it is possible. Once your data is successfully integrated and you are receiving it, you can access it using the [Input] Rule Node (the default green one that is always there when you create a Rule) and route it to any other node you need.
Therefore, the device can auto create relationship with [Asset] by the [Attribute], instead of [AssetName].
So, you want the relationship to take your custom attribute and use that as the pattern that identifies the Asset you want to create the relationship from.
The PE edition already has the Create Relation Node. However, seems that as it is one is not able to do what you seek (has no option to specify custom Asset id).
However, two options you got are:
Create a Custom Rule Node that does what you want. For that try checking the Rule Node Development page from Thingsboard. You can use the Create Relation Node as base and work from there. This can be a longer solution than doing...
Enrich your incoming message's metadata, adding your desired attribute. The Create Relation Node allows you to use variables on your message's metadata in your Name and Type patterns, as seen from this screenshot I took from that node:
This allows us a workaround to what you want to do: Add a Script Transformation Node that adds attributes.asset_id to the metadata, for example as metadata.asset_id, so you can then use it as ${asset_id} on your Name and Type patterns.
For example, your Transform() method of such Script Transformation Node should look something like this:
function Transform(msg, metadata, msgType){
//assumming your desired id is msg.attributes.asset_id, add it to the metadata
metadata.asset_id = msg.attributes.asset_id;
//return the message, in your case to the Create Relation Node
return {msg: msg, metadata:metadata, msgType:msgType};
}
Finally, your Rule should be connected like this:
[Input] -> [Script Node] -> [Create Relation Node] -> [...whatever else you like]

firestore allow write for only one field

I have the following security rule set up in Firestore:
match /ads/{adId} {
allow read: if true;
allow write: if (request.auth.uid == request.resource.data.ownerId) ||
(request.auth != null &&
request.resource.data.size() == 1 &&
request.resource.data.keys().hasAll(['markedFavoriteBy']));
}
The owner of the document has all write permissions but other users can only write in one field - which is called 'markedFavoriteBy'. It seems to work in the Firestore simulator but not in Xcode (with iPhone simulator).
Important to note that the path does not go down to the document field - just to the document. I know that it is not possible to attach a security rule to a single field. However, with this workaround it should still work.
The iOS client code to update the field is the following:
let adRef = Firestore.firestore().collection("ads").document(ad.key!)
let value: FieldValue = ad.isFavorite ? FieldValue.arrayUnion([uid]) : FieldValue.arrayRemove([uid])
adRef.updateData([DatabaseKeys.markedFavoriteBy : value]) { (error) in
if let error = error {
print(error.localizedDescription)
}
}
The error printed to the console is of course: "Missing or insufficient permissions."
I can't find an error in the code - is this a bug that is somehow related to the very recent iOS Firestore SDK update? the functions arrayUnion and arrayRemove have been added only in that recent update.
Sanity check: I have changed the write rule to
allow write: if true;
Result: The array 'markedFavoriteBy' can be changed without any problem.
thanks for any hints in the right direction.
I'm not able to reproduce the entire scenario as I'm not sure of the actual contents of your database. However, what I suspect is the problem is that it doesn't grant permission because request.resource.data represents the new version of the entire document (which will be stored after the update is approved).
You probably want to use request.writeFields doc
Bear in mind that request.writeFields is not available in the simulator.

Is there a way in deployd to map a collection to a different endpoint?

I have a collection called customer_devices and I can't change the name. Can I expose it via deployd as /devices ? How?
There are a few ways that I can think of. If you really just want to rename the collection, you can do so from the dashboard, as #thomasb mentioned in his answer.
Alternatively, you can create a "proxy" event resource devices and forward all queries to customer_devices. For example, in devices/get.js you would say
dpd.customer_devices.get(query, function(res, err) {
if (err) cancel(err);
setResult(res);
});
Update
Finally, here is a "hack" to redirect all requests from one resource path to a different path. This is poorly tested so use at your own risk. This requires that you set up your own server as explained here. Once you have that, you can modify the routing behaviour using this snippet:
server.on('listening', function() {
var customer_devices = server.router.resources.filter(function (res) {
return res.path === '/customer_devices';
})[0];
// Make a copy of the Object's prototype
var devices = Object.create(customer_devices);
// Shallow copy the properties
devices = extend(devices, customer_devices);
// Change the routing path
devices.path = "/devices";
// Add back to routing cache
server.router.resources.push(devices);
});
This will take your customer_devices resource, copy it, change the path, and re-insert it into the cached routing table. I tested it and it works, but I won't guarantee that it's safe or a good idea...
Can't you change the name via dashboard?
Mouseover your collection customer_devices
Click the down arrow
Select 'Rename'
Enter the new name and click 'Rename'

API to modify Firefox downloads list

I am looking to write a small firefox add-on that detects when files that were downloaded are (or have been) deleted locally and removes the corresponding entry in the firefox download list.
Can anybody point me to the relevant api to manipulate the download list? I cannot seem to find it.
The relevant API is PlacesUtils which abstracts the complexity of the Places database.
If your code runs in the context of a chrome window then you get a PlacesUtils glabal variable for free. Otherwise (bootstrapped, Add-on SDK, whatever) you have to import PlacesUtils.jsm.
Cu.import("resource://gre/modules/PlacesUtils.jsm");
As far as Places is concerned, downloaded files are nothing more than a special kind of visited pages, annotated accordingly. It's a matter of just one line of code to get an array of all downloaded files.
var results = PlacesUtils.annotations.getAnnotationsWithName("downloads/destinationFileURI");
Since we asked for the destinationFileURI annotation, each element of the resultarray holds the download location in the annotationValue property as a file: URI spec string.
With that you can check if the file actually exists
function getFileFromURIspec(fileurispec){
// if Services is not available in your context Cu.import("resource://gre/modules/Services.jsm");
var filehandler = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
try{
return filehandler.getFileFromURLSpec(fileurispec);
}
catch(e){
return null;
}
}
getFileFromURIspec will return an instance of nsIFile, or null if the spec is invalid which shouldn't happen in this case but a sanity check never hurts. With that you can call the exists() method and if it returns false then the associated page entry in Places is eligible for removal. We can tell which is that page by its uri, which conveniently is also a property of each element of the results.
PlacesUtils.bhistory.removePage(result.uri);
To sum it up
var results = PlacesUtils.annotations.getAnnotationsWithName("downloads/destinationFileURI");
results.forEach(function(result){
var file = getFileFromURIspec(result.annotationValue);
if(!file){
// I don't know how you should treat this edge case
// ask the user, just log, remove, some combination?
}
else if(!file.exists()){
PlacesUtils.bhistory.removePage(result.uri);
}
});

Resources