Firebase Cross Service Security Rules - google-cloud-firestore

I am trying to implement a Firestore check in my Storage rules following:
https://firebase.blog/posts/2022/09/announcing-cross-service-security-rules
The exists function works fine but I keep getting errors on get. I am using the emulators and running firebase-tools 11.17.0
This returns true or false correctly:
function found(datasetId) {
return firestore.exists(/databases/(default)/documents/datasets/$(datasetId))
}
This fails every time:
function shared(datasetId) {
return firestore.get(/databases/(default)/documents/datasets/$(datasetId)).data.meta.privacy == 'public';
}
An error shows up in emulators on initial load and with every request:
com.google.firebase.rules.runtime.common.EvaluationException: Error: /home/max/Projects/test/websites/namespace/test-web/storage.rules line [10], column [24]. Service call error. Function: [firestore.get], Argument: [path_value {
segments {
simple: "databases"
}
segments {
simple: "(default)"
}
segments {
simple: "documents"
}
segments {
simple: "datasets"
}
segments {
simple: "13ypyc346atp3gqywoqB"
}
}
].
The actual rule is very simple, and works fine if I remove the shared function:
allow read: if found(datasetId) && shared(datasetId)

This was bug in firebase-tools that was fixed in v11.19.0
https://github.com/firebase/firebase-tools/releases/tag/v11.19.0
Fixed bug with Cross-Service Rules integration for Firestore documents containing nulls

Related

Flutter Future timeouts not always working correctly

Hey I need some help here for How to use timeouts in flutter correctly. First of all to explain what the main goal is:
I want to recive data from my Firebase RealTime Database but need to secure this request api call with an time out of 15 sec. So after 15 sec my timeout should throw an exception that will return to the Users frontend the alert for reasons of time out.
So I used the simple way to call timeouts on future functions:
This functions should only check if on some firebase node an ID is existing or not:
Inside this class where I have declared this functions I also have an instance which called : timeoutControl this is a class which contains a duration and some reasons for the exceptions.
Future<bool> isUserCheckedIn(String oid, String maybeCheckedInUserIdentifier, String onGateId) async {
try {
databaseReference = _firebaseDatabase.ref("Boarding").child(oid).child(onGateId);
final snapshot = await databaseReference.get().timeout(Duration(seconds: timeoutControl.durationForTimeOutInSec), onTimeout: () => timeoutControl.onEppTimeoutForTask());
if(snapshot.hasChild(maybeCheckedInUserIdentifier)) {
return true;
}
else {
return false;
}
}
catch (exception) {
return false;
}
}
The TimeOutClass where the instance timeoutControl comes from:
class CustomTimeouts {
int durationForTimeOutInSec = 15; // The seconds for how long to try until we throw an timeout exception
CustomTimeouts();
// TODO: Implement the exception reasons here later ...
onEppTimeoutForUpload() {
throw Exception("Some reason ...");
}
onEppTimeoutForTask() {
throw Exception("Some reason ...");
}
onEppTimeoutForDownload() {
throw Exception("Some reason ...");
}
}
So as you can see for example I tried to use this implementation above. This works fine ... sometimes I need to fight with un explain able things -_-. Let me try to introduce what in somecases are the problem:
Inside the frontend class make this call:
bool isUserCheckedIn = await service.isUserCheckedIn(placeIdentifier, userId, gateId);
Map<String, dynamic> data = {"gateIdActive" : isUserCheckedIn};
/*
The response here is an Custom transaction handler which contains an error or an returned param
etc. so this isn't relevant for you ...
*/
_gateService.updateGate(placeIdentifier, gateId, data).then((response) {
if(response.hasError()) {
setState(() {
EppDialog.showErrorToast(response.getErrorMessage()); // Shows an error message
isSendButtonDiabled = false; /*Reset buttons state*/
});
}
else {
// Create an gate process here ...
createGateEntrys(); // <-- If the closures update was successful we also handle some
// other data inside the RTDB for other reasons here ...
}
});
IMPORTANT to know for you guys is that I am gonna use the returned "boolean" value from this function call to update some other data which will be pushed and uploaded into another RTDB other node location for other reasons. And if this was also successful the application is going on to update some entrys also inside the RTDB -->createGateEntrys()<-- This function is called as the last one and is also marked as an async function and called with its closures context and no await statement.
The Data inside my Firebase RTDB:
"GateCheckIns" / "4mrithabdaofgnL39238nH" (The place identifier) / "NFdxcfadaies45a" (The Gate Identifier)/ "nHz2mhagadzadzgadHjoeua334" : 1 (as top of the key some users id who is checked in)
So on real devices this works always without any problems... But the case of an real device or simulator could not be the reason why I'am faceing with this problem now. Sometimes inside the Simulator this Function returns always false no matter if the currentUsers Identifier is inside the this child nodes or not. Therefore I realized the timeout is always called immediately so right after 1-2 sec because the exception was always one of these I was calling from my CustomTimeouts class and the function which throws the exception inside the .timeout(duration, onTimeout: () => ...) call. I couldn't figure it out because as I said on real devices I was not faceing with this problem.
Hope I was able to explain the problem it's a little bit complicated I know but for me is important that someone could explain me for what should I pay attention to if I am useing timeouts in this style etc.
( This is my first question here on StackOverFlow :) )

Azure Mobile Services for Xamarin Forms - Conflict Resolution

I'm supporting a production Xamarin Forms app with offline sync feature implemented using Azure Mobile Services.
We have a lot of production issues related to users losing data or general instability that goes away if the reinstall the app. After having a look through, I think the issues are around how the conflict resolution is handled in the app.
For every entity that tries to sync we handle MobileServicePushFailedException and then traverse through the errors returned and take action.
catch (MobileServicePushFailedException ex)
{
foreach (var error in ex.PushResult.Errors) // These are MobileServiceTableOpearationErrors
{
var status = error.Status; // HttpStatus code returned
// Take Action based on this status
// If its 409 or 412, we go in to conflict resolving and tries to decide whether the client or server version wins
}
}
The conflict resolving seems too custom to me and I'm checking to see whether there are general guidelines.
For example, we seem to be getting empty values for 'CreatedAt' & 'UpdatedAt' timestamps for local and server versions of the entities returned, which is weird.
var serverItem = error.Result;
var clientItem = error.Item;
// sometimes serverItem.UpdatedAt or clientItem.UpdatedAt is NULL. Since we use these 2 fields to determine who wins, we are stumped here
If anyone can point me to some guideline or sample code on how these conflicts should be generally handled using information from the MobileServiceTableOperationError, that will be highly appreciated
I came across the following code snippet from the following doc.
// Simple error/conflict handling.
if (syncErrors != null)
{
foreach (var error in syncErrors)
{
if (error.OperationKind == MobileServiceTableOperationKind.Update && error.Result != null)
{
//Update failed, reverting to server's copy.
await error.CancelAndUpdateItemAsync(error.Result);
}
else
{
// Discard local change.
await error.CancelAndDiscardItemAsync();
}
Debug.WriteLine(#"Error executing sync operation. Item: {0} ({1}). Operation discarded.",
error.TableName, error.Item["id"]);
}
}
Surfacing conflicts to the UI I found in this doc
private async Task ResolveConflict(TodoItem localItem, TodoItem serverItem)
{
//Ask user to choose the resolution between versions
MessageDialog msgDialog = new MessageDialog(
String.Format("Server Text: \"{0}\" \nLocal Text: \"{1}\"\n",
serverItem.Text, localItem.Text),
"CONFLICT DETECTED - Select a resolution:");
UICommand localBtn = new UICommand("Commit Local Text");
UICommand ServerBtn = new UICommand("Leave Server Text");
msgDialog.Commands.Add(localBtn);
msgDialog.Commands.Add(ServerBtn);
localBtn.Invoked = async (IUICommand command) =>
{
// To resolve the conflict, update the version of the item being committed. Otherwise, you will keep
// catching a MobileServicePreConditionFailedException.
localItem.Version = serverItem.Version;
// Updating recursively here just in case another change happened while the user was making a decision
UpdateToDoItem(localItem);
};
ServerBtn.Invoked = async (IUICommand command) =>
{
RefreshTodoItems();
};
await msgDialog.ShowAsync();
}
I hope this helps provide some direction. Although the Azure Mobile docs have been deprecated, the SDK hasn't changed and should still be relevant. If this doesn't help, let me know what you're using for a backend store.

After Insert trigger on ContentDocumentLink runs twice for 1 record. How to prevent the same?

When iterated over Trigger.new for ContentDocumentLink, I'm trying to filter out some ContentDocumentLink records basis on the entity it is linked to.( Code snippet below. ) The system.debug shows same results but twice after some ms time interval. It is causing my functionality to run twice, how do I prevent the same?
if(govAgreementIds !=null){
for(ContentDocumentLink att:(List<ContentDocumentLink>)Trigger.new){
if(govAgreementIds.contains(att.LinkedEntityId)){
finalcdId.add(att.ContentDocumentId);
}
}
}
System.debug('finalcdId>> '+finalcdId);
Debugs logs :
17:08:07:232 USER_DEBUG [251]|DEBUG|finalcdId>> {0698A000000eg6bQAA}
17:08:08:528 USER_DEBUG [251]|DEBUG|finalcdId>> {0698A000000eg6bQAA}
Same ContentDocumentId can be shared across multiple LinkedEntityId's. your if condition checking LinkedEntityId. There may be multiple linkentity ID's for same ContentDocumentId. You need to implement code as below
if(govAgreementIds !=null){
for(ContentDocumentLink att:(List<ContentDocumentLink>)Trigger.new){
if(govAgreementIds.contains(att.LinkedEntityId)){
if (!finalcdId.contains(att.ContentDocumentId)) {
finalcdId.add(att.ContentDocumentId);
}
}
}
}
This is standard Salesforce behaviour.
This documentation on Triggers and Order of Execution mentions that triggers can be called twice (update triggers) once before and once after execution of workflow rules if something needs to be updated (see point #4, #8 and #11c)
To work around this for some reason, create a simple boolean class variable (a "flag") and use it to control/avoid the second execution:
public class HelperClass {
public static boolean firstRun = true;
}
trigger affectedTrigger on Account (before delete, after delete, after undelete) {
if(Trigger.isBefore){
if(Trigger.isDelete){
if(HelperClass.firstRun){ // check if running for first time
Trigger.old[0].addError('Before Account Delete Error');
HelperClass.firstRun=false; // falsify the flag to denote it has already run before
}
}
}
}
Source for the code: Answer #26 from Jitendra's blog
Hope this helps. Thanks.

Unlang write to file FreeRADIUS

I know I might be facing an impossible mission. What I do want is radiusd to write down every mac received in an Acces-Request, for later on deny access to those MAC.
I know the policies file is written in unlang, bad news are that radiusd does not have any write permissions on any of the conf files...
Anyway, was anyone capable of WRITTING to a file in the POLICY PROCESSING of FreeRADIUS?
What I want to achieve would be something like this:
raddb/sites-available/default
authorize {
rewrite_calling_station_id
unauthorized_macs
if(ok) {
reject
}
else {
update control {
Auth-Type := Accept
}
GET MAC FROM CALLIN_STATION_ID ATTRIBUTE
WRITE THIS F***ING MAC TO unauthorized_macs FILE
}
}
Thanks to Arran, I could solve this the following way:
authorize {
rewrite_calling_station_id
authMac
if(ok) {
reject
}
else {
linelog
update control {
Auth-Type := Accept
}
}
}
Where linelog is configured as follows:
raddb/mods-enabled/linelog
linelog {
filename = /path/to/hell/authMac
format = "%{Calling-Station-ID}"
}
update request {
Tmp-String-0 := `echo "%{Calling-Station-ID}" >> "/path/to/f___ing_unauthorized_macs_file"`
}
There's also the linelog module which would be better in >= v3.0.x as it implements internal locking (in addition to flock) to prevent line interleaving.
See /etc/raddb/mods-available/linelog for examples.

Raising events in KRL without using explicit

I'm writing an app that raises events, similar to how Phil Windley's personal data manager application works. However, if I try to use any event domain but explicit, the events don't get propagated. The following rules work fine with explicit as the domain, but not with driverreg.
rule driver_info_submit {
select when web pageview ".*"
pre {
driver_name = "Joe Driver";
driver_phone = "111-555-1212";
msg = <<
Current driver info: #{ent:driver_name}, #{ent:driver_phone}
>>;
}
notify("Started", msg);
fired {
raise explicit event new_driver_data with driver_name=driver_name and driver_phone=driver_phone;
}
}
// Save driver name
rule save_driver_name {
select when explicit new_driver_data
pre {
driver_name = event:param("driver_name") || ent:driver_name;
driver_phone = event:param("driver_phone") || ent:driver_phone;
}
noop();
always {
set ent:driver_name driver_name;
set ent:driver_phone driver_phone;
raise explicit event driver_data_updated;
}
}
rule driver_info_updated {
select when explicit driver_data_updated
{
notify("Driver name", ent:driver_name);
notify("Driver phone", ent:driver_phone);
}
}
It doesn't seem to be a problem with whether the app is deployed, as I've tried it both ways. What am I missing?
Only certain domains are allowed as domains in the raise statement:
explicit
http
system
notification
error
pds
This may be relaxed in the future.
This is covered in the documents here: https://kynetxdoc.atlassian.net/wiki/display/docs/Raising+Explicit+Events+in+the+Postlude
(note that this is a temporary home for the documentation)