Referensi Aturan Keamanan Firebase untuk Cloud Storage

Firebase Security Rules untuk Cloud Storage digunakan untuk menentukan siapa yang memiliki akses baca dan tulis ke file yang disimpan di Cloud Storage, serta bagaimana file disusun dan metadata apa yang dikandungnya. Cloud Storage Security Rules terdiri dari aturan yang pertimbangkan request dan resource untuk mengizinkan atau menolak tindakan yang diinginkan, seperti seperti mengunggah file atau mengambil metadata file. Dokumen referensi ini mencakup jenis aturan, properti request dan resource, data jenis yang digunakan oleh Cloud Storage Security Rules, dan bagaimana error terjadi.

Aturan

rule adalah ekspresi yang dievaluasi untuk menentukan apakah request diizinkan untuk melakukan tindakan yang diinginkan.

Jenis

Allow

Aturan allow terdiri dari metode, seperti read atau write, serta kondisi opsional. Ketika aturan dijalankan, kondisi akan dievaluasi, dan jika kondisinya bernilai true, metode yang diinginkan akan diizinkan; sebaliknya, metode tersebut ditolak. Aturan allow tanpa kondisi akan selalu mengizinkan metode yang diinginkan.

// Always allow method
allow <method>;

// Allow method if condition is true
allow <method>: if <condition>;

Saat ini, allow adalah satu-satunya jenis aturan yang didukung.

Metode Permintaan

Baca

Metode read mencakup semua permintaan yang membaca metadata atau data file. termasuk download file dan pembacaan metadata file.

// Always allow reads
allow read;

// Allow reads if condition evaluates to true
allow read: if <condition>;

Tulis

Metode write mencakup semua permintaan yang menuliskan data file atau metadatanya, termasuk upload file, penghapusan file, dan pembaruan metadata file.

// Always allow writes
allow write;

// Allow writes if condition evaluates to true
allow write: if <condition>;

Match

Aturan dijalankan saat pengguna request (seperti mengupload atau mendownload file) cocok dengan jalur file yang dicakup oleh aturan. match terdiri dari jalur dan isi, yang harus berisi setidaknya satu aturan allow. Jika tidak ada jalur yang cocok, permintaan ditolak.

Anda dapat match jalur yang diberi nama lengkap, atau Anda dapat memasukkan karakter pengganti untuk mencocokkan semua jalur yang sesuai dengan pola tertentu.

Segmen Jalur

single_segment

Anda dapat menggunakan segmen jalur tunggal untuk membuat aturan yang cocok dengan file yang disimpan di Cloud Storage.

// Allow read at "path" if condition evaluates to true
match /path {
  allow read: if <condition>;
}

Beberapa segmen jalur dan jalur bertingkat juga diizinkan:

// Allow read at "path/to/object" if condition evaluates to true
match /path {
  match /to {
    match /object {
      allow read: if <condition>;
    }
  }
}

{single_segment_wildcard}

Jika ingin menerapkan aturan ke beberapa file di jalur yang sama, Anda dapat menggunakan segmen jalur {i>wildcard<i} untuk mencocokkan semua file di jalur tertentu. Variabel karakter pengganti dideklarasikan di jalur dengan menggabungkan variabel dalam tanda kurung kurawal: {variable}. Variabel ini dapat diakses dalam pernyataan kecocokan sebagai string.

// Allow read at any path "/*", if condition evaluates to true
match /{single_path} {
  // Matches "path", "to", or "object" but not "path/to/object"
  allow read: if <condition>;
}

Beberapa segmen jalur dan jalur bertingkat juga dapat memiliki karakter pengganti:

// Allow read at any path "/path/*/newPath/*", if condition evaluates to true
match /path/{first_wildcard} {
  match /newPath/{second_wildcard} {
    // Matches "path/to/newPath/newObject" or "path/from/newPath/oldObject"
    allow read: if <condition>;
  }
}

{multi_segment_wildcard=**}

Jika ingin mencocokkan sejumlah segmen jalur pada atau di bawah jalur, Anda dapat menggunakan karakter pengganti multisegmen, yang akan mencocokkan semua permintaan ke dan di bawah lokasi HTTP/HTTPS. Ini dapat berguna untuk memberikan format bebas kepada pengguna ruang penyimpanan, atau membuat aturan yang cocok dengan banyak segmen jalur yang berbeda (seperti seperti membuat serangkaian file yang dapat dibaca umum, atau memerlukan otentikasi untuk semua operasi tulis).

Jalur karakter pengganti multi-segmen dideklarasikan serupa dengan satu segmen karakter pengganti, dengan penambahan =** di akhir variabel: {variable=**}. Variabel karakter pengganti multi-segmen tersedia dalam pencocokan sebagai objek path.

// Allow read at any path "/**", if condition evaluates to true
match /{multi_path=**} {
  // Matches anything at or below this, from "path", "path/to", "path/to/object", ...
  allow read: if <condition>;
}

Permintaan

Variabel request disediakan dalam suatu kondisi untuk mewakili atau permintaan yang dibuat di jalur tersebut. Variabel request memiliki sejumlah properti yang dapat digunakan untuk memutuskan apakah akan mengizinkan permintaan masuk atau tidak.

Properti

auth

Saat pengguna terautentikasi melakukan permintaan terhadap Cloud Storage, variabel auth diisi dengan uid pengguna (request.auth.uid) sebagai serta klaim JWT Firebase Authentication (request.auth.token).

request.auth.token berisi beberapa atau semua kunci berikut:

Kolom Deskripsi
email Alamat email yang terhubung dengan akun, jika ada.
email_verified true jika pengguna telah memverifikasi bahwa mereka memiliki akses ke alamat email. Beberapa penyedia secara otomatis memverifikasi alamat email yang mereka miliki.
phone_number Nomor telepon yang terkait dengan akun, jika ada.
name Nama tampilan pengguna, jika ditetapkan.
sub UID Firebase pengguna. UID ini bersifat unik dalam sebuah project.
firebase.identities Kamus yang memuat semua identitas terkait akun pengguna ini. Kunci kamus dapat berupa salah satu dari berikut ini: email, phone, google.com, facebook.com, github.com, twitter.com. Nilai kamus adalah array ID unik untuk setiap penyedia identitas yang terkait dengan akun. Misalnya, auth.token.firebase.identities["google.com"][0] berisi ID pengguna Google pertama yang dikaitkan dengan akun.
firebase.sign_in_provider Penyedia login yang digunakan untuk mendapatkan token ini. Dapat berupa salah satu string berikut: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant tenantId yang terkait dengan akun, jika ada. misalnya, tenant2-m6tyz

Jika menggunakan autentikasi kustom, request.auth.token juga berisi klaim yang ditentukan oleh developer.

Saat pengguna yang tidak diautentikasi melakukan permintaan, request.auth adalah null.

// Allow requests from authenticated users
allow read, write: if request.auth != null;

path

Variabel path berisi jalur tempat request sedang dijalankan lawan.

// Allow a request if the first path segment equals "images"
allow read, write: if request.path[0] == 'images';

resource

Variabel resource berisi metadata file yang sedang diupload atau metadata yang telah diperbarui untuk file yang sudah ada. Hal ini terkait dengan Variabel resource, yang berisi metadata file saat ini di jalur yang diminta, bukan {i>metadata<i} baru.

// Allow a request if the new value is smaller than 5MB
allow read, write: if request.resource.size < 5 * 1024 * 1024;

request.resource berisi properti berikut dari resource:

Properti
name
bucket
metadata
size
contentType

time

Variabel time berisi stempel waktu yang menunjukkan waktu server saat ini permintaan sedang dievaluasi. Anda dapat menggunakannya untuk memberikan akses berbasis waktu ke file, seperti: hanya mengizinkan file diupload hingga tanggal tertentu, atau hanya mengizinkan file dibaca hingga satu jam setelah diunggah.

// Allow a read if the file was created less than one hour ago
allow read: if request.time < resource.timeCreated + duration.value(1, 'h');

Banyak fungsi disediakan untuk menulis aturan menggunakan stempel waktu dan durasi.

Resource

Variabel resource berisi metadata file untuk file dalam Cloud Storage, seperti nama file, ukuran, waktu pembuatan, dan metadata kustom.

Properti

name

String yang berisi nama lengkap file, termasuk jalur ke file tersebut.

// Allow reads if the resource name is "path/to/object"
allow read: if resource.name == 'path/to/object'

bucket

String yang berisi informasi Google Cloud Storage bucket tempat file ini disimpan.

// Allow reads of all resources in your bucket
allow read: if resource.bucket == '<your-cloud-storage-bucket>'

generation

Int yang berisi Google Cloud Storage pembuatan objek file tersebut. Digunakan untuk pembuatan versi objek.

// Allow reads if the resource matches a known object version
allow read: if resource.generation == <known-generation>

metageneration

Int yang berisi Google Cloud Storage metagenerasi objek file tersebut. Digunakan untuk pembuatan versi objek.

// Allow reads if the resource matches a known object metadata version
allow read: if resource.metageneration == <known-generation>

size

Int yang berisi ukuran file dalam byte.

// Allow reads if the resource is less than 10 MB
allow read: if resource.size < 10 * 1024 * 1024;

timeCreated

Stempel waktu yang menunjukkan kapan file dibuat.

// Allow reads if the resource was created less than an hour ago
allow read: if resource.timeCreated < request.time + duration.value(60, "m")

updated

Stempel waktu yang menunjukkan kapan file terakhir diperbarui.

// Allow reads if the resource was updated less than an hour ago
allow read: if resource.updated < request.time + duration.value(60, "m")

md5Hash

String yang berisi hash MD5 dari .

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.md5Hash == resource.md5Hash;

crc32c

String yang berisi atribut hash crc32c dari .

// Allow writes if the hash of the uploaded file is the same as the existing file
allow write: if request.resource.crc32c == resource.crc32c;

etag

String yang berisi etag dari .

// Allow writes if the etag matches a known object etag
allow write: if resource.etag == <known-generation>

contentDisposition

String yang berisi disposisi konten file.

// Allow reads if the content disposition matches a certain value
allow read: if resource.contentDisposition == 'inlined';

contentEncoding

String yang berisi encoding konten file.

// Allow reads if the content is encoded with gzip
allow read: if resource.contentEncoding == 'gzip';

contentLanguage

String yang berisi bahasa konten file.

// Allow reads if the content language is Japanese
allow read: if resource.contentLanguage == 'ja';

contentType

String yang berisi jenis konten file.

// Allow reads if the content type is PNG.
allow read: if resource.contentType == 'image/png';

metadata

Map<String, String> yang berisi metadata tambahan yang disediakan developer kolom.

// Allow reads if a certain metadata field matches a desired value
allow read: if resource.metadata.customProperty == 'customValue';

firestore.get dan firestore.exists

Fungsi firestore.get() dan firestore.exists() memungkinkan Anda mengakses dokumen dalam Cloud Firestore untuk mengevaluasi kriteria otorisasi yang kompleks.

Fungsi firestore.get() dan firestore.exists() memerlukan jalur dokumen yang sepenuhnya ditentukan. Saat menggunakan variabel untuk membangun jalur untuk firestore.get() dan firestore.exists(), Anda harus meng-escape secara eksplisit variabel menggunakan sintaksis $(variable).

firestore.get

Dapatkan konten dokumen Cloud Firestore.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{club}/files/{fileId} {
      allow read: if club in
        firestore.get(/databases/(default)/documents/users/$(request.auth.uid)).data.memberships
    }
  }
}

firestore.exists

Periksa apakah dokumen Cloud Firestore ada.

service firebase.storage {
  match /b/{bucket}/o {
    match /users/{userId}/photos/{fileId} {
      allow read: if
        firestore.exists(/databases/(default)/documents/users/$(userId)/friends/$(request.auth.uid))
    }
  }
}

Service

service adalah deklarasi pertama dalam file Cloud Storage Security Rules, dan menentukan layanan tempat aturan akan diterapkan.

Nama

name

Nama aturan layanan yang akan diterapkan. Satu-satunya nilai saat ini adalah firebase.storage.

// Specify the service name
service firebase.storage {
  match /b/{bucket}/o {
    ...
  }
}

Jenis Data

Bahasa Rules memungkinkan Anda memeriksa jenis menggunakan operator is.

// For example
a is null
a is string

null

Jenis data null mewakili nilai yang tidak ada.

allow read: if request.auth != null;

bool

Jenis bool merepresentasikan nilai boolean true atau false.

allow read: if true;   // always succeeds
allow write: if false; // always fails

Perbandingan

Nilai Boolean dapat dibandingkan menggunakan operator == !=.

Operasi Boolean

Operasi Ekspresi
AND x && y
OR x || y
NOT !x

Operasi mengalami hubungan pendek, dan dapat menampilkan true, false, atau Error.

allow read: if true || false;   // always succeeds, short circuits at true
allow write: if false && true; // always fails, short circuits at false

int dan float

Jenis int dan float mewakili angka. Int adalah: 0, 1, -2, dll. , sedangkan float adalah: 1.0, -2.0, 3.33, dll.

Ints ditandai dengan nilai 64-bit, dan float adalah nilai yang sesuai dengan 64-bit IEEE 754. Nilai jenis int akan dikonversi menjadi float saat digunakan dalam perbandingan dan operasi aritmatika dengan nilai float.

Perbandingan

Int dan float dapat dibandingkan dan diurutkan menggunakan ==, !=, >, <, operator >=, dan <=.

Aritmetika

Int dan float dapat ditambahkan, dikurangi, dikalikan, dibagi, dimodulus, dan diabaikan:

Operasi Ekspresi
Penambahan x + y
Pengurangan x - y
Perkalian x * y
Pembagian x / y
Modulus x % y
Negasi -x

Fungsi matematika

Firebase Security Rules untuk Cloud Storage juga menyediakan sejumlah helper matematika fungsi untuk menyederhanakan ekspresi:

Fungsi Deskripsi
math.ceil(x) Batas nilai numerik
math.floor(x) Lantai dari nilai numerik
math.round(x) Membulatkan nilai input ke int terdekat
math.abs(x) Nilai absolut input
math.isInfinite(x) Uji apakah nilainya ±∞, menampilkan bool
math.isNaN(x) Uji apakah nilai bukan angka NaN, yang menampilkan bool

string

Perbandingan

String dapat dibandingkan dan diurutkan secara leksografis menggunakan ==, !=, >, <, >=, dan operator <=.

Penyambungan

String dapat digabungkan menggunakan operator +.

// Concatenate a file name and extension
'file' + '.txt'

Indeks dan Rentang

Operator index, string[], menampilkan string yang berisi karakter pada indeks yang disediakan dalam string.

// Allow reads of files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

Operator range, string[i:j], menampilkan string yang berisi karakter di antara indeks yang ditentukan, mulai dari i (inklusif) hingga j (eksklusif). Jika i atau j tidak ditentukan, defaultnya adalah 0 dan ukuran string, masing-masing, tetapi setidaknya i atau j harus ditentukan agar rentang valid.

// Allow reads of files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

Operator index dan range akan menghasilkan error jika indeks yang diberikan melampaui batas string.

size

Menampilkan jumlah karakter dalam string.

// Allow files with names less than 10 characters
match /{fileName} {
  allow write: if fileName.size() < 10;
}

matches

Melakukan pencocokan ekspresi reguler, menampilkan true jika string cocok dengan ekspresi reguler yang diberikan. Penggunaan Sintaksis Google RE2.

// Allow writes to files which end in ".txt"
match /{fileName} {
  allow write: if fileName.matches('.*\\.txt')
}

split

Memisahkan string sesuai dengan ekspresi reguler yang disediakan dan menampilkan list penggunaan {i>string<i}. Menggunakan sintaksis Google RE2.

// Allow files named "file.*" to be uploaded
match /{fileName} {
  allow write: if fileName.split('.*\\..*')[0] == 'file'
}

path

Jalur adalah nama seperti direktori dengan pencocokan pola opsional. Tujuan adanya garis miring / menunjukkan awal segmen jalur.

path

Mengonversi argumen string menjadi path.

// Allow reads on a specific file path
match /{allFiles=**} {
  allow read: if allFiles == path('/path/to/file');
}

timestamp

Stempel waktu menggunakan UTC, dengan nilai yang mungkin dimulai pada 0001-01-01T00.00.00Z dan berakhir di 9999-12-31T23.59.59Z.

Perbandingan

Stempel waktu dapat dibandingkan dan diurutkan menggunakan ==, !=, >, <, >=, dan operator <=.

Aritmetika

Stempel waktu mendukung penambahan dan pengurangan antara stempel waktu dan durasi sebagai berikut ini:

Ekspresi Hasil
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

date

Nilai timestamp yang hanya berisi year, month, dan day.

// Allow reads on the same day that the resource was created.
allow read: if request.time.date() == resource.timeCreated.date()

year

Nilai tahun sebagai int, dari 1 hingga 9999.

// Allow reads on all requests made before 2017
allow read: if request.time.year() < 2017

month

Nilai bulan sebagai int, dari 1 hingga 12.

// Allow reads on all requests made during the month of January
allow read: if request.time.month() == 1;

day

Hari ini dalam sebulan sebagai int, dari 1 sampai 31.

// Allow reads on all requests made during the first day of each month
allow read: if request.time.day() == 1;

time

Nilai duration yang berisi waktu saat ini.

// Allow reads on all requests made before 12PM
allow read: if request.time.time() < duration.time(12, 0, 0, 0);

hours

Nilai jam sebagai int, dari 0 hingga 23.

// Allow reads on all requests made before 12PM
allow read: if request.time.hours() < 12;

minutes

Nilai menit sebagai int, dari 0 hingga 59.

// Allow reads during even minutes of every hour
allow read: if request.time.minutes() % 2 == 0;

seconds

Nilai detik sebagai int, dari 0 hingga 59.

// Allow reads during the second half of each minute
allow read: if request.time.seconds() > 29;

nanos

Detik pecahan dalam nano sebagai int.

// Allow reads during the first 0.1 seconds of each second
allow read: if request.time.nanos() < 100000000;

dayOfWeek

Hari dalam seminggu, dari 1 (Senin) hingga 7 (Minggu).

// Allow reads on weekdays (Monday to Friday)
allow read: if request.time.dayOfWeek() < 6;

dayOfYear

Hari dalam tahun ini, dari 1 hingga 366.

// Allow reads every fourth day
allow read: if request.time.dayOfYear() % 4 == 0;

toMillis

Menampilkan jumlah milidetik saat ini sejak epoch Unix.

// Allow reads if the request is made before a specified time
allow read: if request.time.toMillis() < <milliseconds>;

duration

Nilai durasi direpresentasikan sebagai detik ditambah detik pecahan dalam dalam nanodetik.

Perbandingan

Durasi dapat dibandingkan dan diurutkan menggunakan ==, !=, >, <, >=, dan operator <=.

Aritmetika

Durasi mendukung penambahan dan pengurangan antara stempel waktu dan durasi sebagai berikut ini:

Ekspresi Hasil
timestamp + duration timestamp
duration + timestamp timestamp
timestamp - duration timestamp
timestamp - timestamp duration
duration + duration duration
duration - duration duration

seconds

Jumlah detik dalam durasi saat ini. Harus antara -315.576.000.000 dan +315.576.000.000 inklusif.

nanos

Jumlah detik pecahan (dalam nanodetik) dari durasi saat ini. Harus antara -999.999.999 dan +999.999.999 inklusif. Untuk detik bukan nol dan nanondetik bukan nol, tanda-tanda keduanya harus sesuai.

duration.value

Durasi dapat dibuat menggunakan duration.value(int magnitude, string units) , yang membuat durasi waktu dari magnitudo dan satuan yang diberikan.

// All of these durations represent one hour:
duration.value(1, "h")
duration.value(60, "m")
duration.value(3600, "s")

Kemungkinan unit adalah:

Durasi unit
Minggu w
Hari d
Jam h
Menit m
Detik s
Milidetik ms
Nanodetik ns

duration.time

Durasi dapat dibuat menggunakan fungsi duration.time(int hours, int minutes, int seconds, int nanoseconds), yang membuat durasi waktu dari jam, menit, detik, dan dalam nanodetik.

// Create a four hour, three minute, two second, one nanosecond duration
duration.time(4, 3, 2, 1)

list

Daftar berisi array nilai yang diurutkan, yang dapat dari jenis: null, bool, int, float, string, path, list, map, timestamp, atau duration.

Dengan x dan y dari jenis list dan i dan j dari jenis int

Kreasi

Untuk membuat daftar, tambahkan nilai di antara tanda kurung:

// Create a list of strings
['apples', 'grapes', 'bananas', 'cheese', 'goats']

Perbandingan

Daftar dapat dibandingkan menggunakan operator == !=. Kesetaraan dua daftar mengharuskan semua nilai sama.

Indeks dan Rentang

Operator index, list[], menampilkan item pada indeks yang disediakan di daftar.

// Allow reads of all files that begin with 'a'
match /{fileName} {
  allow read: if fileName[0] == 'a';
}

Operator range, list[i:j], menampilkan semua item dalam daftar di antara indeks yang ditentukan, dari i (inklusif) hingga j (eksklusif). Jika i atau j adalah tidak ditentukan, masing-masing secara {i>default<i} adalah 0 dan ukuran daftar, tetapi setidaknya i atau j harus ditentukan agar rentang valid.

// Allow reads of all files that begin with 'abcdef'
match /{fileName} {
  allow read: if fileName[0:6] == 'abcdef';
}

in

Menampilkan true jika nilai yang diinginkan ada dalam daftar atau false jika tidak ada saat ini.

// Allow read if a filename has the string 'txt' in it
match /{fileName} {
  allow read: if 'txt' in fileName.split('\\.');
}

join

Menggabungkan daftar string menjadi satu string, dipisahkan oleh string yang diberikan.

// Allow reads if the joined array is 'file.txt'
allow read: if ['file', 'txt'].join('.') == 'file.txt';

size

Jumlah item dalam daftar.

// Allow read if there are three items in our list
allow read: if ['foo', 'bar', 'baz'].size() == 3;

hasAll

Menampilkan true jika semua nilai ada dalam daftar.

// Allow read if one list has all items in the other list
allow read: if ['file', 'txt'].hasAll(['file', 'txt']);

map

Peta berisi key-value pair, dengan kunci berupa string dan nilai dapat berupa apa pun dari: null, bool, int, float, string, path, list, map, timestamp, atau duration.

Kreasi

Untuk membuat peta, tambahkan key-value pair di antara tanda kurung kurawal:

// Create a map of strings to strings
{
  'mercury': 'mars',
  'rain': 'cloud',
  'cats': 'dogs',
}

Perbandingan

Peta dapat dibandingkan menggunakan operator == !=. Persamaan dua peta mengharuskan semua kunci ada di peta dan semua nilainya sama.

Indeks

Nilai dalam peta diakses dengan menggunakan notasi tanda kurung atau titik:

// Access custom metadata properties
allow read: if resource.metadata.property == 'property'
allow write: if resource.metadata['otherProperty'] == 'otherProperty'

Jika kunci tidak ada, error akan ditampilkan.

in

Menampilkan true jika kunci yang diinginkan ada di peta atau false jika tidak ada saat ini.

// Allow reads if a property is present in the custom metadata
allow read: if property in resource.metadata;

size

Jumlah kunci pada peta.

// Allow reads if there's exactly one custom metadata key
allow read: if resource.metadata.size() == 1;

keys

Daftar semua kunci di peta.

// Allow reads if the first metadata key is 'myKey'
allow read: if resource.metadata.keys()[0] == 'myKey';

values

Daftar semua nilai di peta, dalam urutan kunci.

// Allow reads if the first metadata value is 'myValue'
allow read: if resource.metadata.values()[0] == 'myValue';

Error

Evaluasi Error

Firebase Security Rules untuk Cloud Storage melanjutkan evaluasi saat terjadi error. Hal ini berguna karena ekspresi && dan || bersyarat mungkin menyerap error jika kondisional akan menyebabkan hubungan pendek ke false atau true secara berurutan. Misalnya:

Ekspresi Hasil
error && true error
error && false false
error || true true
error || false error

Contoh error yang sering terjadi adalah: pembagian dengan nol, akses nilai dalam daftar atau peta yang tidak ada, dan meneruskan nilai dari jenis yang salah tentang suatu fungsi.

// Error if resource.size is zero
allow read: if 1000000 / resource.size;

// Error, key doesn't exist
allow read: if resource.metadata.nonExistentKey == 'value';

// Error, no unit 'y' exists
allow read: if request.time < resource.timeCreated + duration.value(1, 'y');