Search code examples
firebasegoogle-cloud-firestorefirebase-security

Firestore-Rules evaluation error on Timestamp Field


I wrote a firestore rule like this:

function isPublished() {
  return !('publicationAt' in resource.data.keys()) ? true :
    debug(debug(request.time) >= debug(resource.data.publicationAt));
}

and in my app i´m saving several datetimes just with new Date(); Now, I´d like to get a list of all items that are published - but it gives me an evaluation error - with this debug:

constraint_value {
simple_constraints { comparator: LTE value { timestamp_value { seconds: 1669108946 nanos: 684000000 } }
} }

timestamp_value { seconds: 1669108945 nanos: 727000000 }

Can someone give me a hint what I´m doing wrong?


Solution

  • While you may be tempted to think that Security Rules Language (SRL) is JavaScript because of the function() {} part, it is not. It is a special statically evaluated language that gets interpreted once without other common features like iteration.

    Importantly, the SRL has no ternary operator:

    <condition> ? <result-when-true> : <result-when-false>
    

    Instead you must build your conditions as a sequence of Boolean tests making use of the available helper methods and classes.

    The equivalent in basic Boolean algebra of the ternary operator (when using short-circuiting operators like && and ||) is:

    (<condition> && <result-when-true>) || (NOT(<condition>) && <result-when-false>)
    

    Substituting in the value for <result-when-true>, we can reduce down your expression using Boolean algebra laws (where the AND operator (&&) is and the OR operator (||) is ):

    (<condition> && <result-when-true>) || (NOT(<condition>) && <result-when-false>)
    
    // replace <result-when-true> with TRUE, as in your question
    (<condition> && TRUE) || (NOT(<condition>) && <result-when-false>)
    
    // now remove the redundant "&& TRUE" (see "Identity of ∧")
    <condition> || (NOT(<condition>) && <result-when-false>)
    
    // now distribute the OR operator (see "Distributivity of ∨ over ∧")
    (<condition> || NOT(<condition>)) && (<condition> || <result-when-false>)
    
    // now replace (<condition> || NOT(<condition>)) because it will always be TRUE (see "Complementation 2")
    TRUE && (<condition> || <result-when-false>)
    
    // now remove the redundant "TRUE &&" like before (see "Identity of ∧")
    <condition> || <result-when-false>
    

    Subsituting in the parts from your question, this leaves us with:

       !('publicationAt' in resource.data.keys())
    || debug(debug(request.time) >= debug(resource.data.publicationAt))
    

    You can also simplify 'publicationAt' in resource.data.keys() to just 'publicationAt' in resource.data (as the in operator handles what you were expecting for rules.Map objects), giving us:

       !('publicationAt' in resource.data)
    || debug(debug(request.time) >= debug(resource.data.publicationAt));
    

    This means the correct form of your SRL function would look like:

    function isPublished() {
      return !('publicationAt' in resource.data)
          || debug(debug(request.time) >= debug(resource.data.publicationAt));
    }