In my rule, I want to ensure that the data.id field is the hash of the doc id. The following is not working.
function hashGood(a, b) {
let x = hashing.sha256(a);
return x == b;
}
match /users/{userId} {
allow create: if request.auth != null && hashGood(userId, request.resource.data.id);
}
I have also tried hashing.sha256(a.toUtf8()).toBase64() and variations thereof.
When I try to create /users/B in the 'Rules Playground', it shows x == "B" . But x should be the hash of "B", not equal to "B".
What I am doing wrong?
Edit: I am using rules_version = '2';
hashing.sha256(a).toHexString() worked for me.
Though I would like to still understand why
let x = hashing.sha256(a) prints x=<value of a>
Related
I learned early on that there is no reason to use the return keyword in Scala (as far as I'm aware). That being said I found an example where simply changing adding the return keyword made my function work, where it previously didn't.
The code in question comes from my solution to the Advent of Code day 7 challenge.
def containsShinyGoldBag(bagContents: Map[String, List[String]], currentBag: String): Boolean = {
val contents = bagContents(currentBag)
if (bagContents(currentBag).contains("shiny gold") ) {
// Base Case: Bag Found in list of bags
true
} else if (contents == List.empty){
// Base Case: Dead End
false
} else {
// Continue searching down list
// Ideal solution ( gives same result as the working solution without return keyword )
// for (b <- contents) containsShinyGoldBag(bagContents, b)
// Working solution
for (b <- contents) {
if (containsShinyGoldBag(bagContents, b)) {
println(s"Found one! $b inside a $currentBag")
return true // <--- culprit
}
else false
}
false
}
}
// In the main function
var count = 0
for (bag <- bagContents.keys) {
if (containsShinyGoldBag(bagContents, bag)) {
count = count + 1
}
}
println(s"There are $count way to bring a shiny gold bag!")
When I run the code without return I end up with count = 7, which is the number of bags directly containing a shiny gold bag, rather than the correct number which counts bags that contain a shiny gold bag somewhere inside of one of their other bags down the line.
A function returns the value of the last expression it evaluates; in your case that will be one of:
true after if (bagContents(currentBag).contains("shiny gold") );
false after else if (contents == List.empty);
the last false.
true is not in such a position, so you need return to, well, make the function return it. Otherwise it's evaluated and ignored because you don't do anything with it. So is else false in the same for, actually, it can be removed without changing the meaning.
The alternative to avoid return here is
contents.exists(b => containsShinyGoldBag(bagContents, b))
I'm trying to create a Security Rule based upon request.time as given in an example on AngularFirebase website.
My function is
function isThrottled() {
return request.time < resource.data.lastUpdate + duration.value(1, 'm')
}
Where I'm trying to allow update: if isThrottled() == false
However, when I try to update a document with this rule, it fails due to time being not defined on the object.
Error: simulator.rules line [169], column [12]. Property time is
undefined on object.
Shouldn't every request have a time or TimeStamp attached to it? Is this something to do with how I'm initializing my Cloud Functions or client app?
Screenshots below:
EDIT
A snippet for the rest of the update security rules are:
service cloud.firestore {
match /databases/{db}/documents {
match /users/{userId} {
match /username/{id} {
allow update: if isSelf(userId)
&& usernameAvailable(incomingData().username)
&& incomingData().username is string
&& incomingData().username.size() <= 25
&& incomingFields().size() == 1
&& isThrottled() == false;
}
}
function incomingData() {
return request.resource.data
}
function isThrottled() {
return request.time < resource.data.lastUpdate + duration.value(1, 'm')
}
function incomingFields() {
return incomingData().keys()
}
function isSelf(userId) {
return userId == currentUser().uid;
}
function usernameAvailable(username) {
return !exists(/databases/$(db)/documents/usernames/$(username));
}
}
}
The username collection is a subcollection under each user document (in the users root collection. Each username document only has 1 field called username that users can update).
This might not be useful for your case in particular, but I had the same error when checking a custom claim on the token object.
Before accessing the field you can use in to check whether the property exists on the object. This code generates the error if agent is not defined:
allow write: if request.auth != null && request.auth.token.agent == true;
This code works fine if agent is not defined:
allow write: if request.auth != null && "agent" in request.auth.token && request.auth.token.agent == true;
In various environment, the user name is case insensitive. We query admin equal to ADMIN. I have searched LiteDB called CompareTo to compare two objects that I can't find a point to make string compared as case insensitive.
The code in QueryEquals.cs
internal override IEnumerable<IndexNode> ExecuteIndex(IndexService indexer, CollectionIndex index)
{
var node = indexer.Find(index, _value, false, Query.Ascending);
if (node == null) yield break;
yield return node;
if (index.Unique == false)
{
// navigate using next[0] do next node - if equals, returns
while (!node.Next[0].IsEmpty && ((node = indexer.GetNode(node.Next[0])).Key.CompareTo(_value) == 0))
{
if (node.IsHeadTail(index)) yield break;
yield return node;
}
}
}
The propositional we can case insensitive
using (var db = new LiteRepository("lite.db"))
{
db.Insert(new User { Name = "John" });
var user = db.Query<User>()
.Where(x => x.Name == "JOHN")
.FirstOrDefault(); // proposal return John
var fail = db.Query<User>()
.Where(x => string.Equals(x.Name, "JOHN", StringComparison.OrdinalIgnoreCase))
.FirstOrDefault(); // throw exception
}
Another consideration, it is a possible to execute lambda expression in LiteDB without conversion by visitor?
In LiteDB v4, you can use expressions to make sure that the index of username is stored as lowercase, then you can perform a "lowercase-compare"
// Create index
entity.EnsureIndex(e => e.Username, "LOWER($.Username)");
...
// Find user
collection.Find(e => e.Username == username.ToLower);
If you dont want to use expressions, I guess that you could make sure the username is lowercase before saving in LiteDB or you can use an index less query (slower) by using collection.Where (not quite sure on that method though).
Consider the following rule for "locations" r and s:
∀r,s[(danger(r)∧adjacent(r,s))→danger(s)]
I tried to implement as follows:
function boolean adjacent(Location l1, Location l2) {
if (l1.x == l2.x)
return Math.abs(l1.y - l2.y) == 1;
if (l1.y == l2.y)
return Math.abs(l1.x - l2.x) == 1;
return false;
}
rule danger
when
$s : Location(danger == true)
$r : Location()
adjacent($s,$r)
then
modify($r) { setDanger(true) }
end;
But it does not compile saying that adjacent cannot be resolved.
I tried eval(adjacent($s,$r)) but it does not work because rete keeps visiting the same combinations of $s and $r forever.
I tried implementing adjacent() method on Location, but it does not compile either:
$r : Location(adjacent($s) == true)
I thought on some alternatives like: making each Location has a list of adjacent locations; etc. But none of them sounded right for me.
How would be the right way to implement this?
rule danger
when
$s : Location( danger )
$r : Location(! danger, adjacent($s,$r) )
then
modify($r) { setDanger(true) }
end
You can write a boolean expression as a constraint, or inside an eval CE (but never as a pattern all by itself, as you tried).
To avoid the loop, add a constraint that fails after the modify.
I am trying to look up record using if I have the key then use Find if not use Where
private ApplicationDbContext db = new ApplicationDbContext();
public bool DeactivatePrice(int priceId = 0, string sponsorUserName = "")
{
var prices = db.BeveragePrices;
// if we have an id then find
if (priceId != 0)
{
prices = prices.Find(priceId);
}
else
{
prices = prices.Where(b => b.UserCreated == sponsorUserName);
}
if (prices != null)
{
// do something
}
return true;
I get the following error for
prices = prices.Find(priceId);
Cannot convert app.Model.BeveragePrices from system.data.entity.dbset
I am copying the pattern from this answer but something must be different.
Seems you forgot to put a predicate inside the Find function call. Also you need to do ToList on the collection. The second option is a lot more efficient. The first one gets the whole collection before selection.
Another note commented by #Alla is that the find returns a single element. So I assume another declaration had been made for 'price' in the first option I state down here.
price = prices.ToList.Find(b => b.PriceId == priceId);
Or
prices = prices.Select(b => b.PriceId == priceId);
I assume the field name is PriceId.