API правил безопасности базы данных Firebase

Правило: Типы

.читать

Предоставляет клиенту доступ на чтение к местоположению базы данных Firebase Realtime.

Правило .read — это тип правила безопасности, которое предоставляет клиенту доступ на чтение к местоположению базы данных Firebase Realtime. Например:

 ".read": "auth != null && auth.provider == 'twitter'"

Значением правила .read является строка, которая оценивается как подмножество синтаксиса выражений JavaScript с некоторыми поведенческими изменениями для повышения ясности и правильности. Правило .read , которое разрешает чтение местоположения, также позволит читать любых потомков этого местоположения, даже если у потомков есть свои собственные правила .read , которые не работают.

Правило .read имеет доступ ко всем переменным правил базы данных Firebase Realtime, кроме newData .

.писать

Предоставляет клиенту доступ на запись к местоположению базы данных Firebase Realtime.

Правило .write — это тип правила безопасности, которое предоставляет клиенту доступ на запись к местоположению базы данных Firebase Realtime. Например:

".write": "auth != null && auth.token.isAdmin == true"

Значением правила .write является строка, которая оценивается как подмножество синтаксиса выражений JavaScript с некоторыми поведенческими изменениями для повышения ясности и правильности. Правило .write , которое предоставляет разрешение на запись в местоположение, также позволит писать любым потомкам этого местоположения, даже если у потомков есть свои собственные правила .write , которые не работают.

Правило .write имеет доступ ко всем переменным правил базы данных Firebase Realtime.

.валидатировать

Используется после того, как правило .write предоставило доступ, чтобы гарантировать, что записываемые данные соответствуют определенной схеме.

Правило .validate используется после того, как правило .write предоставило доступ, чтобы гарантировать, что записываемые данные соответствуют определенному стандарту. В дополнение к разрешению доступа .write все соответствующие правила .validate должны пройти успешно, прежде чем запись будет разрешена. Например:

".validate": "newData.hasChildren(['name', 'age'])"

Значением правила .validate является строка, которая оценивается как подмножество синтаксиса выражений JavaScript с некоторыми поведенческими изменениями для повышения ясности и правильности.

Правило .validate имеет доступ ко всем переменным правил базы данных Firebase Realtime.

.indexOn

Повышает производительность запросов, сообщая базе данных Firebase Realtime, какие ключи вы хотите проиндексировать.

Правило .indexOn предписывает серверам базы данных Firebase Realtime индексировать определенные ключи в ваших данных, чтобы повысить производительность ваших запросов. Например, учитывая базу данных с коллекцией данных о динозаврах, мы можем указать базе данных Firebase Realtime оптимизировать запросы до того, как они будут возвращены с серверов, добавив это правило:

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

Дополнительную информацию о правиле .indexOn вы можете узнать, обратившись к разделу руководства по безопасности, посвященному индексированию ваших данных .

Правило: переменные

авторизация

Переменная, содержащая полезные данные токена, если клиент аутентифицирован, или null если клиент не аутентифицирован.

База данных Firebase Realtime позволяет легко аутентифицироваться у нескольких встроенных провайдеров и генерировать для них токены аутентификации. После аутентификации пользователя с помощью одного из встроенных провайдеров переменная auth будет содержать следующее:

Поле Описание
provider Используемый метод аутентификации (например, «пароль», «анонимный», «facebook», «github», «google» или «twitter»).
uid Уникальный идентификатор пользователя, который гарантированно будет уникальным для всех поставщиков.
token Содержимое токена идентификатора аутентификации Firebase. См. auth.token .

В качестве примера мы могли бы создать правило, подобное следующему, позволяющее пользователям создавать комментарии, если они сохраняют свой идентификатор пользователя вместе с комментарием:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && newData.child('user_id').val() == auth.uid"
    }
  }
}

Мы также могли бы создать правило, подобное следующему, позволяющее пользователям создавать комментарии, если они вошли в систему с помощью Facebook:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && auth.provider == 'facebook'"
    }
  }
}

токен авторизации

Переменная, содержащая содержимое токена идентификатора аутентификации Firebase.

Токен содержит некоторые или все следующие ключи:

Поле Описание
email Адрес электронной почты, связанный с учетной записью, если таковой имеется.
email_verified true , если пользователь подтвердил, что у него есть доступ к адресу email . Некоторые провайдеры автоматически проверяют принадлежащие им адреса электронной почты.
phone_number Номер телефона, связанный с учетной записью, если он присутствует.
name Отображаемое имя пользователя, если оно установлено.
sub UID пользователя Firebase. Это уникально в рамках проекта.
firebase.identities Словарь всех идентификаторов, связанных с учетной записью этого пользователя. Ключами словаря могут быть любые из следующих: email , phone , google.com , facebook.com , github.com , twitter.com . Значения словаря представляют собой массивы уникальных идентификаторов для каждого поставщика удостоверений, связанного с учетной записью. Например, auth.token.firebase.identities["google.com"][0] содержит первый идентификатор пользователя Google, связанный с учетной записью.
firebase.sign_in_provider Поставщик входа, использованный для получения этого токена. Может быть одной из следующих строк: custom , password , phone , anonymous , google.com , facebook.com , github.com , twitter.com .
firebase.tenant TenantId, связанный с учетной записью, если он присутствует. например, tenant2-m6tyz

При использовании пользовательской аутентификации auth.token также содержит любые пользовательские утверждения, указанные разработчиком.

Все эти значения можно использовать в правилах. Например, чтобы ограничить доступ к учетным записям Google, связанным с адресом gmail.com, мы могли бы добавить правило:

{
  "rules": {
    ".read": "auth != null",
    "gmailUsers": {
      "$uid": {
        ".write": "auth.token.email_verified == true && auth.token.email.matches(/.*@gmail.com$/)"
      }
    }
  }
}

Для полноты в auth.token также включены следующие поля, но они вряд ли будут полезны для правил.

Поле Описание
iss Эмитент токена.
aud Аудитория токена.
auth_time Последний раз, когда пользователь проходил аутентификацию с использованием учетных данных с помощью устройства, получающего токен.
iat Время выдачи токена.
exp Время истечения срока действия токена.

$location

Переменная, которую можно использовать для ссылки на ключ $location , который использовался ранее в структуре правила.

Если в вашей структуре правил есть $location , вы можете использовать соответствующую переменную $ в выражении вашего правила, чтобы получить имя фактического дочернего элемента, который читается или записывается. Итак, предположим, мы хотим предоставить каждому пользователю доступ на чтение и запись к его собственному местоположению /users/<user> . Мы могли бы использовать:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "auth.uid === $user",
        ".write": "auth.uid === $user"
      }
    }
  }
}

Когда клиент пытается получить доступ к /users/barney , местоположение $user по умолчанию будет соответствовать значению $user , равному «barney». Таким образом, правило .read проверит, есть ли auth.uid === 'barney' . В результате чтение /users/barney будет успешным, только если клиент аутентифицирован с помощью uid «barney».

сейчас

Содержит количество миллисекунд, прошедших с эпохи Unix, согласно серверам базы данных Firebase Realtime.

Переменная now содержит количество миллисекунд, прошедших с эпохи UNIX, согласно серверам базы данных Firebase Realtime. Например, вы можете использовать это для проверки того, что created пользователем время никогда не будет установлено на время в будущем:

{
  "rules": {
    "users": {
      "$user": {
        "created": {
          ".validate": "newData.val() < now"
        }
      }
    }
  }
}

корень

RuleDataSnapshot , соответствующий текущим данным в корне вашей базы данных Firebase Realtime.

Корневая переменная предоставляет вам RuleDataSnapshot , соответствующий текущим данным в корне вашей базы данных Firebase Realtime. Вы можете использовать это для чтения любых данных в вашей базе данных в выражениях правил. Например, если бы мы хотели разрешить пользователям читать /comments только в том случае, если для их /users/<id>/active установлено значение true, мы могли бы использовать:

{
  "rules": {
    "comments": {
      ".read": "root.child('users').child(auth.uid).child('active').val() == true"
    }
  }
}

Затем, если /users/barney/active содержало значение true, пользователь, прошедший аутентификацию с идентификатором uid «barney», мог писать в узел /comments .

данные

RuleDataSnapshot , соответствующий текущим данным в базе данных Firebase Realtime в местоположении текущего исполняемого правила.

Переменная данных предоставляет вам RuleDataSnapshot , соответствующий текущим данным в расположении базы данных текущего исполняемого правила (в отличие от root, который предоставляет вам данные для корня вашей базы данных).

Например, если вы хотите разрешить любому клиенту доступ к /users/<user> если /users/<user>/public установлено значение true, вы можете использовать:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "data.child('public').val() == true"
      }
    }
  }
}

Переменная данных доступна в правилах .read , .write и .validate .

новые данные

RuleDataSnapshot , соответствующий данным, которые будут получены, если запись разрешена.

Для правил .write и .validate переменная newData предоставляет RuleDataSnapshot , соответствующий данным, которые будут получены, если запись разрешена (это «объединение» существующих данных и записываемых новых данных). Итак, если вы хотите убедиться, что у каждого пользователя есть имя и возраст, вы можете использовать:

{
  "rules": {
    "users": {
      "$user": {
        ".read": true,
        ".write": true,
        ".validate": "newData.hasChildren(['name', 'age'])"
      }
    }
  }
}

Поскольку newData объединяет существующие и новые данные, он ведет себя правильно даже при «частичных» обновлениях. Например:

var fredRef = firebase.database().ref("users/fred");
// Valid since we have a name and age.
fredRef.set({ name: "Fred", age: 19 });
// Valid since we are updating the name but there's already an age.
fredRef.child("age").set(27);
// Invalid since the .validate rule will no longer be true.
fredRef.child("name").remove();

Переменная newData недоступна в правилах .read , поскольку новые данные не записываются. Вам следует просто использовать данные .

RuleDataSnapshot: методы

вал()

Получает примитивное значение ( string , number , boolean или null ) из этого RuleDataSnapshot .

Возвращаемое значение : ( String , Number , Boolean , Null ) — примитивное значение из этого RuleDataSnapshot .

В отличие от DataSnapshot.val() вызов val() для RuleDataSnapshot, имеющего дочерние данные, не вернет объект, содержащий дочерние элементы. Вместо этого он вернет специальное контрольное значение. Это гарантирует, что правила всегда будут работать чрезвычайно эффективно.

Как следствие, вы всегда должны использовать child() для доступа к дочерним элементам (например data.child('name').val() , а не data.val().name ).

В этом примере чтение разрешено только в том случае, если для дочернего элемента isReadable установлено значение true в читаемом месте.

".read": "data.child('isReadable').val() == true"

ребенок()

Получает RuleDataSnapshot для местоположения по указанному относительному пути.

Аргументы : childPath String — относительный путь к местоположению дочерних данных.

Возвращаемое значение : RuleDataSnapshotRuleDataSnapshot для дочернего расположения.

Относительный путь может быть либо простым дочерним именем (например, «fred»), либо более глубоким путем, разделенным косой чертой (например, «fred/name/first»). Если в дочернем расположении нет данных, возвращается пустой RuleDataSnapshot.

В этом примере чтение разрешено только в том случае, если для дочернего элемента isReadable установлено значение true в читаемом месте.

".read": "data.child('isReadable').val() == true"

родитель()

Получает RuleDataSnapshot для родительского расположения.

Возвращаемое значение : RuleDataSnapshotRuleDataSnapshot для родительского расположения.

Если этот экземпляр ссылается на корень вашей базы данных Firebase Realtime, у него нет родителя, и parent() завершится ошибкой, что приведет к пропуску текущего выражения правила (как сбоя).

В этом примере чтение разрешено только в том случае, если для родственного элемента isReadable установлено значение true.

".read": "data.parent().child('isReadable').val() == true"

имеетChild(childPath)

Возвращает true, если указанный дочерний элемент существует.

Аргументы : childPath String — относительный путь к местоположению потенциального дочернего элемента.

Возвращаемое значение : Booleantrue , если данные существуют по указанному дочернему пути; иначе false .

В этом примере допускается запись данных, только если они содержат дочернее «имя».

".validate": "newData.hasChild('name')"

hasChildren([дети])

Проверяет наличие детей.

Аргументы : children Array необязательный — массив дочерних ключей, которые все должны существовать.

Возвращаемое значение : Booleantrue , если (указанные) дочерние элементы существуют; иначе false .

Если аргументы не указаны, он вернет true, если у RuleDataSnapshot есть дочерние элементы. Если предоставлен массив имен дочерних элементов, он вернет true только в том случае, если все указанные дочерние элементы существуют в RuleDataSnapshot .

В этом примере допускается запись данных, только если они содержат одного или нескольких дочерних элементов.

".validate": "newData.hasChildren()"

В этом примере допускается запись данных, только если они содержат дочерние элементы «имя» и «возраст».

".validate": "newData.hasChildren(['name', 'age'])"

существует()

Возвращает true, если этот RuleDataSnapshot содержит какие-либо данные.

Возвращаемое значение : Booleantrue , если RuleDataSnapshot содержит какие-либо данные; иначе false .

Функция существует возвращает значение true, если этот RuleDataSnapshot содержит какие-либо данные. Это чисто удобная функция, поскольку data.exists() эквивалентна data.val() != null .

В этом примере разрешена запись в это место, пока нет существующих данных.

".write": "!data.exists()"

getPriority()

Получает приоритет данных в RuleDataSnapshot .

Возвращаемое значение : ( String , Number , Null ) — приоритет данных в этом RuleDataSnapshot .

Этот пример гарантирует, что новые записываемые данные имеют приоритет.

".validate": "newData.getPriority() != null"

isNumber()

Возвращает true, если этот RuleDataSnapshot содержит числовое значение.

Возвращаемое значение : Booleantrue , если данные числовые; иначе false .

В этом примере гарантируется, что новые записываемые данные имеют дочерний «возраст» с числовым значением.

".validate": "newData.child('age').isNumber()"

isString()

Возвращает true, если этот RuleDataSnapshot содержит строковое значение.

Возвращаемое значение : Booleantrue если данные представляют собой String ; иначе false .

В этом примере гарантируется, что новые записываемые данные имеют дочернее «имя» со строковым значением.

".validate": "newData.child('name').isString()

isBoolean()

Возвращает true, если этот RuleDataSnapshot содержит логическое значение.

Возвращаемое значение : Booleantrue если данные являются Boolean ; иначе false .

В этом примере гарантируется, что новые записываемые данные имеют дочерний «активный» элемент с логическим значением.

".validate": "newData.child('active').isBoolean()"

Строка: Свойства

длина

Возвращает длину строки.

Возвращаемое значение : Number — количество символов в строке.

В этом примере требуется, чтобы строка содержала не менее 10 символов.

".validate": "newData.isString() && newData.val().length >= 10"

Строка: Методы

содержит (подстроку)

Возвращает true, если строка содержит указанную подстроку.

Аргументы : substring String — подстрока для поиска.

Возвращаемое значение : Booleantrue , если строка содержит указанную подстроку; иначе false .

В этом примере требуется, чтобы данные представляли собой строку, содержащую «@».

".validate": "newData.isString() && newData.val().contains('@')"

начинается с (подстрока)

Возвращает true, если строка начинается с указанной подстроки.

Аргументы : substring String — подстрока, которую нужно искать в начале.

Возвращаемое значение : Booleantrue , если строка содержит указанную подстроку; иначе false .

В этом примере разрешен доступ на чтение, если auth.token.identifier начинается с «internal-».

".read": "auth.token.identifier.beginsWith('internal-')"

заканчиваетсяС(подстрока)

Возвращает true, если строка заканчивается указанной подстрокой.

Аргументы : substring String — подстрока, которую нужно искать в конце.

Возвращаемое значение : Booleantrue , если строка заканчивается указанной подстрокой; иначе false .

В этом примере разрешен доступ на чтение, если auth.token.identifier заканчивается на «@company.com».

".read": "auth.token.identifier.endsWith('@company.com')"

replace(подстрока, замена)

Возвращает копию строки, в которой все экземпляры указанной подстроки заменены указанной строкой замены.

Аргументы : substring String — подстрока для поиска. replacement String — строка, на которую заменяется подстрока.

Возвращаемое значение : String — новая строка после замены подстроки заменой.

Метод replace() немного отличается от метода replace() в JavaScript тем, что он заменяет все экземпляры указанной подстроки указанной строкой замены, а не только первый экземпляр.

Поскольку точки в ключах не допускаются, нам необходимо экранировать строки с точками перед их сохранением. Примером этого могут быть адреса электронной почты. Предположим, у нас есть список адресов электронной почты, занесенных в белый список, в нашем узле /whitelist/ :

{
 "user": {
   "$uid": {
     "email": <email>
   }
 },
 "whitelist": {
   "fred@gmail%2Ecom": true,
   "barney@aol%2Ecom": true
 }
}

Мы можем создать правило, которое позволит добавлять пользователей только в том случае, если их адрес электронной почты находится в узле /whitelist/ :

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "true",
        ".write": "root.child('whitelist').child(newData.child('email').val().replace('.', '%2E')).exists()"
      }
    }
  }
}

toLowerCase()

Возвращает копию строки, преобразованную в нижний регистр.

Возвращаемое значение : String — строка, преобразованная в нижний регистр.

Этот пример разрешает доступ для чтения, если auth.token.identifier , поскольку все строчные буквы существуют в /users .

".read": "root.child('users').child(auth.token.identifier.toLowerCase()).exists()"

toUpperCase()

Возвращает копию строки, преобразованную в верхний регистр.

Возвращаемое значение : String — строка, преобразованная в верхний регистр.

Этот пример разрешает доступ на чтение, если auth.token.identifier , поскольку все прописные буквы существуют в /users .

".read": "root.child('users').child(auth.token.identifier.toUpperCase()).exists()"

совпадения (регулярное выражение)

Возвращает true, если строка соответствует указанному литералу регулярного выражения.

Возвращаемое значение : Booleantrue , если строка соответствует литералу регулярного выражения, регулярному выражению; иначе false .

См. полную документацию по регулярным выражениям правил .

Операторы

+ (добавить)

Используется для добавления переменных или объединения строк.

В следующем примере гарантируется, что новое значение увеличивает существующее значение ровно на единицу. Это полезно для реализации счетчика:

".write": "newData.val() === data.val() + 1"
".validate": "root.child('room_names/' + $room_id).exists()"

- (отрицать или вычитать)

Используется для отрицания значения или вычитания двух значений в выражении правил.

Это правило проверки проверяет, является ли новое значение обратным дочернему значению в данном местоположении:

".validate": "newData.val() === -(data.child('quantity').val())"

В следующем примере используется вычитание, чтобы гарантировать возможность чтения только сообщений за последние десять минут:

".read": "newData.child('timestamp').val() > (now - 600000)"

* (умножить)

Используется для умножения переменных в выражении правил.

Это правило проверки проверяет, равно ли новое значение произведению цены и количества (двум существующим значениям):

".validate": "newData.val() === data.child('price').val() * data.child('quantity').val()"

/ (разделять)

Используется для разделения переменных в выражении правил.

В следующем примере правило проверки гарантирует, что сохраненные данные являются средним значением общего объема данных, хранящихся в другом месте:

".validate": "newData.val() === data.parent().child('sum').val() / data.parent().child('numItems').val()"

% (модуль)

Используется для нахождения остатка от деления одной переменной на другую в выражении правил.

Это правило подтверждает, что можно записывать только четные числа:

".validate": "newData.val() % 2 === 0"

=== (равно)

Используется для проверки того, имеют ли две переменные в выражении правил одинаковый тип и значение.

Следующее правило использует оператор ===, чтобы предоставить доступ на запись только владельцу учетной записи пользователя. Чтобы правило имело значение true, идентификатор пользователя должен точно совпадать с ключом ( $user_id ).

"users": {
  ".write": "$user_id === auth.uid"
}

!== (не равно)

Используется для проверки того, не равны ли две переменные в выражении правил.

Следующее правило чтения гарантирует, что только вошедшие в систему пользователи смогут читать данные:

".read": "auth !== null"

&& (И)

Возвращает значение true, если оба операнда верны. Используется для оценки нескольких условий в выражении правил.

Следующее правило проверки проверяет, что новые данные представляют собой строку длиной менее 100 символов:

".validate": "newData.isString() && newData.val().length < 100"

|| (ИЛИ)

Возвращает значение true, если один операнд в выражении правил имеет значение true.

В этом примере мы можем писать до тех пор, пока не существуют старые данные или новые данные. Другими словами, мы можем писать, удаляем или создаем данные, но не обновляем данные.

".write": "!data.exists() || !newData.exists()"

! (НЕТ)

Возвращает значение true, если его единственный операнд имеет значение false. В выражениях правил оператор ! Оператор часто используется, чтобы проверить, были ли записаны данные в определенное место.

Следующее правило разрешает доступ на запись только в том случае, если в указанном месте нет данных:

".write": "!data.exists()"

> (больше чем)

Используется для проверки того, превышает ли значение другое значение в выражении правил.

Это правило проверки проверяет, что записываемая строка не является пустой строкой:

".validate": "newData.isString() && newData.val().length > 0"

< (меньше чем)

Используется для проверки того, меньше ли значение другого значения в выражении правил.

Это правило проверки проверяет, что строка содержит менее 20 символов:

".validate": "newData.isString() && newData.val().length < 20"

>= (больше или равно)

Используется для проверки того, больше ли значение или равно другому значению в выражении правил.

Это правило проверки проверяет, что записываемая строка не является пустой строкой:

".validate": "newData.isString() && newData.val().length >= 1"

<= (меньше или равно)

Используется для проверки того, меньше ли значение или равно другому значению в выражении правил.

Это правило проверки гарантирует, что новые данные не могут быть добавлены в будущем:

".validate": "newData.val() <= now"

? (тройной оператор)

Используется для оценки выражения условных правил.

Тернарный оператор принимает три операнда. Операнд перед ? это условие. Если условие оценивается как истинное, оценивается второй операнд. Если условие ложно, оценивается третий операнд.

Для следующего правила проверки новое значение может быть числом или логическим значением. Если это число, оно должно быть больше 0.

".validate": "newData.isNumber() ? newData.val() > 0 : newData.isBoolean()"