Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Cloud development security rules


May 22, 2021 Mini Program Cloud Development Advanced



Security rules are a flexible way to customize the permission control of database and cloud storage read and write permissions, by configuring security rules, developers can control all records in the small terminal, web side of the cloud storage and collection of addition, deletion, correction, check permissions, automatically reject non-compliant security rules of the front-end database and cloud storage requests, to ensure data and file security.

One, the variable of the .openid

Earlier, we recommended using security rules instead of simple versions of permission settings, and when you use security rules, an important core here is the openid variable, which is essential when querying on the front end (small terminal, web side) (that is, cloud functions, cloud development consoles are not controlled by security rules).

1, query writes need to explicitly specify openid

When used on a small terminal, you don't need to get the user's openid through a cloud function first, just use '{openid}' we all need to explicitly pass in openid when we query. We didn't need to do this before when we used a simple permission configuration, because querying adds a _openid to the query condition by default_openid it must be equal to the user openid, but with security rules, there is no such default query condition.

For example, when we query college, we all need to add the following conditions in where, the variable will come with the current user's openid.

  1. db.collection('china').where({
  2. _openid: '{openid}', //安全规则里有auth.openid时都需要添加
  3. })

Write requests for databases such as updates, deletions, etc. also need to explicitly add such a condition to where (after using security rules, bulk updates and deletions can also be made on the small terminal).

  1. db.collection('goods').where({
  2. _openid: '{openid}',
  3. category: 'mobile'
  4. }).update({ //批量更新
  5. data:{
  6. price: _.inc(1)
  7. }
  8. })

When security rules are turned on, you need to specify _openid in the where query _openid: '{openid}' security rules have auth.openid that is, the identity of the user is required, where the query condition is a subset of the security rules, so you need to add. Of course, depending on your situation, security rules do not require the _openid: '{openid}'

2, doc operation needs to be converted to where operation

Since we db.collection('china').doc(id) how do we control permissions? At this point, we can convert the doc operation into where operation, specify the value of _id in the where query, so that only one record can be queried:

  1. db.collection('china').where({
  2. _id: 'tcb20200501', //条件里面加_id
  3. _openid: '{openid}', //安全规则里有auth.openid时都需要添加
  4. })

As for the other doc operations, they all need to be converted to action-based where operations, which means that you will no longer use db.collection('china').doc(id) future. D oc.update, doc.get, and doc.remove can be replaced with action-based updates, get, and remove, and doc.set can be replaced _.set Of course, security rules apply only to the front end (small terminal or Web side), which is not restricted by the permissions of the security rule.

3, nested array objects in the openid

When using a simple permission configuration, when a user writes data to a database on the applet side, a _openid field is added to the record doc to record the user's openid, as is the case after security rules are used. When you create a record, you can assign the variable to a field that is not _openid or write it to a nested array, which is automatically replaced with the openid of the applet user when the string is found when the record is written in the background:

  1. db.collection('posts').add({
  2. data:{
  3. books:[{
  4. title:"云开发快速入门",
  5. author:'{openid}'
  6. },{
  7. title:"数据库入门与实战",
  8. author:'{openid}'
  9. }]
  10. }
  11. })

In the past, to write to openid, you need to return the user openid through the cloud function, after using security rules, you can directly use the variable, but this method only supports add a record, do not support the way update.

Second, the writing of safety rules

After using security rules, we can configure security rules separately for each collection (developer tools and web pages) and folders for cloud storage, i.e. custom permissions, configured in a json format, and still strictly follow the writing of the jason profile (e.g., the last item in the array cannot have a comma, the configuration file cannot have comments, etc.).

1, more fine-grained additions and deletions

Let's first look at the simple 所有用户可读,仅创建者可写 configuration All user readable, only the 所有用户不可读写 creator can write, only the creator can read 仅创建者可读写 所有用户可读 all users read, all users unreadable write corresponding to the security rules of the writing, the key of this johnson key the type of operation, value is an value is also a condition, resolved to true means that the corresponding action conforms to the security rules.

  1. // 所有人可读,仅创建者可读写
  2. {
  3. "read": true,
  4. "write": "doc._openid == auth.openid"
  5. }
  6. //仅创建者可读写
  7. {
  8. "read": "doc._openid == auth.openid",
  9. "write": "doc._openid == auth.openid"
  10. }
  11. //所有人可读
  12. {
  13. "read": true,
  14. "write": false
  15. }
  16. //所有用户不可读写
  17. {
  18. "read": false,
  19. "write": false
  20. }

Simple permission configuration only read and writewrite, and after using security rules, support permission operations in addition to reading and writing, but also write permissions subdivided into creative new, update update, delete delete, that is, can only use write, can also be subdivided into addition, deletion, change, such as the following case for everyone to read, the creator can write can be 所有人可读,创建者可写可更新,但是不能删除

  1. "read": true,
  2. "create":"auth.openid == doc._openid",
  3. "update":"auth.openid == doc._openid",
  4. "delete":false

The type of operation is nothing more than addition and deletion, but the value of the security rule is a conditional expression, written a lot, so that the security rule is more flexible. It's worth noting that if we don't assign read or white, their default value is false.

2, all users can read and write the app

Security rules can also configure all readable and writeable types, which is written as follows, so that all logged-in users (who log on before openid, i.e. openid is not empty) can read and write to the data.

  1. {
  2. "read": "auth.openid != null",
  3. "write": "auth.openid != null"
  4. }

On the small terminal, we can write the security rules of the database collection both read and write as true (this is readable and writeable for everyone, and all users are emphasized here), because as long as the user uses a small program that opens up cloud development, there is an openid for unqualifical login, but the above security rules are written for cloud storage, web-side security rules.

The data in the collection makes all users readable and writeable and applied in many ways, especially if we want other users to update fields in nested arrays and nested objects. For example, the collection posts store all the information articles, and we nest the comments of the articles in the collection.

  1. {
  2. _id:"tcb20200503112",
  3. _openid:"用户A", //用户A也是作者,他发表的文章
  4. title:"云开发安全规则的使用经验总结",
  5. stars:223,
  6. comments:[{
  7. _openid:"用户B",
  8. comment:"好文章,作者有心了",
  9. }]
  10. }

This record is also created when User A publishes an article, and if User B wishes to comment (update data to arrays), like articles (update the value of stars with inc atomic updates), the record needs to be readable and writeable (at least can be updated). This is not possible in a simple permission configuration (only with cloud functions), and with security rules, a record can have permissions maintained by multiple people at the same time, a scenario that is common in a document database like cloud development (because it involves nesting array nested objects).

Security rules and the conditions in the query where are mutually matched, but there is a difference between the two. S tatements for all security rules point to eligible document records, not collections. W here the security rule is used, the security rule is matched first, for example, if the small terminal does not query errCode: -502003 database permission denied | errMsg: Permission denied then a conditional match, such as when the security rule is set to readable by all, when there are no eligible results, the result of the query is 0. We should pay attention to the difference between not being authorized to query and query resulting in 0.

3, global variables

To understand what security rule writing means, we also need to understand some global variables, such as auth.openid which represents the doc._openid _openid field, which has permission when the user's openid is the same as the _openid value of the current record. Global variables also have now (current timestamp) and resource (cloud storage related).

Variable Type Description
auth object User login information, auth.openid is also the user's openid, if it is on the web side it also has login Type login method, uid equivalent
doc object Represents the content of the current record, which is used to match the record content/query criteria
now number The timestamp of the current time, which is the millisecond calculated from the timing origin
resource object Resource.openid is the private attribution identity of the cloud storage file, marking the owner's openid

4, operator

The expression of the security rule == also supports operators, such as equal >= to , not < equal to != <= || > && Wait, there will be a specific introduction later.

Operator Description Example
== Equals auth.openid == 'zzz' The user's openid is zzz
!= Not equal to auth.openid != 'zzz' The user's openid is not zzz
\> Greater than doc.age>10 The age property of the query condition is greater than 10
\>= Is greater than or equal to doc.age>=10 The age property of the query condition is greater than or equal to 10
< Less than doc.age<10 The age property of the query condition is less than 10
<= Less than or equal to doc.age<=10 The age property of the query condition is less than or equal to 10
in Exists in the collection auth.openid in ['zzz','aaa'] The user's openid is one of the .zzz, 'aaa'
! (xx in []) Does not exist in the collection, described in the way !(a in [1,2,3]) ! (auth.openid in ['zzz','aaa']) The user's openid is not any of the 'zzz', 'aaa'
&& And auth.openid == 'zzz' && doc.age>10 The user's openid is zzz and the age property of the query condition is greater than 10
|| Or auth.openid == 'zzz'|| doc.age>10 The user's openid is zzz or the age property of the query condition is greater than 10
. The object element accessor auth.openid The user's openid
[] Array accessor properties doc.favorites[0] == 'zzz' The value of the first item in the favorites array field for query criteria is zzz

Fourth, authentication

The combination of global variables auth and doc allows the permissions of the logged-in user to depend on a field of the record, auth represents the logged-in user, while doc and resources are resource-dependent in the cloud development environment, and there is a connection between the user and the database and cloud storage after the security rules are used. Resource only resource.openid, and doc not only _openid, but also have a lot of fields, which gives the database permissions a lot of flexibility, and then we are more doc global variables as an example.

1, the creator of the record

auth.openid is the current logged-in user, and the openid in the record doc allows the record to be closely linked to the logged-in user, or, arguably, to have an identity verification of the record. In general doc._openid this represents the openid of the creator of the record, and the easy permission control compares whether the current logged-in user is the creator of the record (or a more open and extensive permission).

  1. //登录用户为记录的创建者时,才有权限读
  2. "read": "auth.openid == doc._openid",
  3. //不允许记录的创建者删除记录(只允许其他人删除)
  4. "delete": "auth.openid != doc._openid",

Security rules and where queries are used in combination, and if you specify that the permissions of the record are related to the creator's openid, you cannot have a bigger scope of query criteria on the front end than the security rules database permission denied :

  1. db.collection('集合id').where({
  2. _openid:'{openid}' //有doc._openid,因此查询条件里就需要有_openid这个条件,
  3. key:"value"
  4. })
  5. .get().then(res=>{
  6. console.log(res)
  7. })

2, specify the role of the record

1. Give permissions to someone

Authentication of security rules is not limited to the creator of the record, login user permissions can also rely on other fields of the record, we can also give the permission of the record as a person (non-record creator), such as many students submitted the assignment, will be given to a teacher to review the revision, the teacher needs to have read and write permissions on the record, in the processing, when the student submits the assignment (create a record doc) can specify the tester's openid, For this teacher only, here's an example of the structure and security rules of the document:

  1. //文档的结构
  2. {
  3. _id:"handwork20201020",
  4. _openid:"学生的openid", //学生为记录的创建者,
  5. teacher:"老师的openid" //该学生被指定的老师的openid
  6. }
  7. //安全规则
  8. {
  9. "read": "doc.teacher == auth.openid || doc._openid == auth.openid",
  10. "write": "doc.teacher == auth.openid || doc._openid == auth.openid",
  11. }

Letting the logged-in user auth.openid rely on other fields of the record is functionally equivalent to assigning a role to the record, such as direct teacher, reviewer, direct supervisor, honey, husband and wife, direct assignment of tasks, and so on.

For query or update operations, the input where query criteria must be a subset of the security rules, such as if your security rules are doc.teacher . . doc.teacher == auth.openid and you do not have a condition in teacher:'{openid}' will be a permission error.

Since security rules and where queries need to be used in a companion way, there are doc.teacher doc._openid doc._openid security rules, and a subset of security rules are written in where, such as _openid:'{openid}' or teacher:'{openid}' and teachers to share a database request:

  1. const db = wx.cloud.database()
  2. const _ = db.command
  3. //一条记录可以同时被创建者(学生)和被指定的角色(老师)读取
  4. db.collection('集合id').where(_.or([
  5. {_openid:'{openid}' }, //与安全规则doc._openid == auth.openid对应
  6. {teacher:'{openid}' } //与安全规则doc.teacher == auth.openid对应
  7. ]))
  8. .get().then(res=>{
  9. console.log(res)
  10. })

2, the permission to some people

This role designation above is one-to-one, or many-to-one, or one-to-many, and you can in !(xx in []) operator. For example, here are the roles that can be assigned to a record (records created by students, which multiple teachers have permission to read and write):

  1. //文档的结构
  2. {
  3. _id:"handwork20201020",
  4. _openid:"学生的openid", //学生为记录的创建者,
  5. teacher:["老师1的openid","老师2的openid","老师3的openid"]
  6. }
  7. //安全规则
  8. {
  9. "read": "auth.openid in doc.teacher || doc._openid == auth.openid",
  10. "write": "auth.openid in doc.teacher || doc._openid == auth.openid",
  11. }

It should be emphasized here that the where condition of the front end (small terminal) must be a subset of the security rule permissions, for example, we make the following query for the teacher on the small terminal '{openid}' support query instructions and requires back-end acquisition)

  1. db.collection('集合id').where({
  2. _openid:'{openid}',
  3. teacher:_.elemMatch(_.eq('老师的openid'))
  4. }).get()
  5. .then(res=>{
  6. console.log(res)
  7. })

Previously we implemented the right to specify a record to one person or several people, so how to give permissions to a record to a certain type of person? For example, taxi software for the security of data will have drivers, passengers, administrators, developers, operations personnel, marketing personnel, etc. , which requires us to create a new field in the {role:3} type of user, such as the type of user, such as the number of 1, 2, 3, 4 and so on, or with the number of . The {isManager:true} type indicates that this new field can be doc.role in the doc.role a separate collection (that is, the collection of storage permissions and the collection to be queried are separated, which requires the use of get functions to query across collections), which is described later.

3, doc.auth and the creator of the document

Here's an example to deepen our understanding of security rules, such as when we specify the auth of a document in a record as someone else's openid, with the corresponding security rules, even if the current user is actually the creator of the record, which has _openid of the creator, and he has no permission to operate. Security rules evaluate query conditions, as long as the security rules are met, the query will succeed, the security rules will be violated, the query will fail.

  1. //文档的结构,比如以下为一条记录
  2. {
  3. _id:"handwork20201020",
  4. _openid:"创建者的openid",
  5. auth:"指定的auth的openid"
  6. }
  7. //安全规则
  8. {
  9. "权限操作": "auth.openid == doc.auth" //权限操作为read、write、update等
  10. }
  11. //前端查询,不符合安全规则,即使是记录的创建者也没有权限
  12. db.collection('集合id').where({
  13. auth:'{openid}'
  14. })

Fourth, security rules commonly used scenes

Simple version permission settings do not implement write permissions (including update, create, delete) for records across users on the front end, which means that records can only be written by the creator. W hile a document database can host a lot of information for anti-paradigm nesting reasons, it is very common for B users to manipulate records created by A users, especially in scenarios such as using update instruction update fields and the values of embedded fields. In addition, only security rules enable bulk updates and deletions of front-end pairs of records.

For example, we can comments, favorites, likes, forwarding, reading and other information embedded in the collection of articles, in the past we in the small terminal (only through the cloud function) is not able to let B users on the A user created records, such as likes, favorites, forwarding with update instructions inc update times, such as can not directly update instructions to write comments push to the record:

  1. {
  2. _id:"post20200515001",
  3. title:"云开发安全规则实战",
  4. star:221, //点赞数
  5. comments:[{ //评论和子评论
  6. content:"安全规则确实是非常好用",
  7. nickName:"小明"
  8. subcomment:[{
  9. content:"我也这么觉得",
  10. nickName:"小军"
  11. }]
  12. }],
  13. share:12, //转发数
  14. collect:15 //收藏数
  15. readNum:2335 //阅读量
  16. }

When security rules are turned on, we can let B users modify records created by A users directly at the front end, so that when users read, like, comment, forward, favorite articles, etc., they can update the articles at the field level directly using update instructions.

  1. "read":"auth.openid != null",
  2. "update":"auth.openid != null"

This security rule is 所有人可读,仅创建者可读写 and written only by the creator, opens update permissions, and has limits on the small terminal. If you don't use security rules, putting these in a cloud function for processing is not only slower, but also very draining of the resources of the cloud function.

  1. db.collection('post').where({
  2. _id:"post20200515001",
  3. openid:'{openid}'
  4. }).update({
  5. data:{
  6. //更新指令的应用
  7. }
  8. })

Fifth, data validation doc rules match

We can also store access control information as a field in the database's collection document, while security rules can dynamically allow or deny access based on document data, which means that doc rule matching allows the permissions of a record to dynamically depend on the value of a field in the record.

Doc rule matching security rules for the entire collection, and requires that all records in the collection have the appropriate permission field, and only when the permission field meets certain conditions, the record has permissions to be added and deleted, is a collection of permissions in accordance with the requirements of the process, where the conditions of the query can not be longer than the scope specified by the security rules (query conditions are a subset of security rules);

It is still emphasized here that when using where queries, query conditions are required to be a subset of security rules, the rules are parsed and checked before the where query is made, and if the where conditions are not a subset of security rules, permissions are reported as errors, and security rules cannot be regarded as a filter, but as an insurmountable rule to protect the security of recorded data.

1, the status of the record permissions

Doc's rules match, especially if each record has multiple states or each record has consistent permission conditions (either all, or none), and only one state or meets the criteria to have permission to be added or deleted by the user, such as when the file approval is in effect (previously there were multiple states where the approval did not take effect), and the article is published with a pub status ic (previously private or other status), the shelf of the goods (there are multiple states before the shelf), the security detection of text image content is not illegal (previously after-checking), whether the message is withdrawn, whether the file is deleted, because each record we need to mark permissions, and only eligible records have the opportunity to be added and deleted.

For example, the fields of an information article are as follows, each record corresponds to an article, while status stores multiple states of the article, and only when the article is public can the article be accessed by the user, we can use the security rule "read": "doc.status=='public'" For soft deletion (article fake deletion), deletion can be as a state, but the article is still in the database.

  1. {
  2. _id:"post2020051314",
  3. title:"云开发发布新能力,支持微信支付云调用",
  4. status:"public"
  5. },
  6. {
  7. _id:"post2020051312",
  8. title:"云函数灰度能力上线",
  9. status:"edit"
  10. },
  11. {
  12. _id:"post2020051311",
  13. title:"云开发安全规则深度研究",
  14. status:"delete"
  15. }

The database query criteria corresponding to the front end (small terminal) must be a subset of the security rules, that is, the security rules cannot be the filtering criteria for your query, the security rules evaluate the query, and if the query does not meet the constraints set by the security rules (non-subset), the query request at the front end does not have permission to read the document, not filter the document:

  1. db.collection('集合id').where({
  2. status:"public" //你不能不写这个条件,而指望安全规则给你过滤
  3. }).get()
  4. .then(res=>{
  5. console.log(res)
  6. })

2, the record is prohibited to empty

Sometimes we need to have very strict requirements for certain records, prohibit empty, how to empty will not be deleted by the front end, such as the shopping collection has been put on the shelves of the list of goods, some core data such as price, profit, inventory, etc. can not be empty, causing losses to enterprises, the corresponding security rules and queries are as follows:

  1. //安全规则
  2. {
  3. "权限操作": "doc.profit != null",
  4. }
  5. //权限操作,profit = 0.65就是安全规则的子集
  6. db.collection('shop').where({
  7. profit:_.eq(0.65)
  8. })

3, a subset of the record permissions

The field values recorded by security rules are not limited to one state > but can also be range values that can be performed, such as greater than , less < in the customer unit price of goods are more than 100, the administrator in the back end (console, cloud function, etc.) to the original price of 190 yuan written as 19, or mistake to write the price as a negative number, in this case we use the security rules doc.price > 100 100 goods operating rights, including query.

  1. //安全规则
  2. "操作权限":"doc.price > 100"
  3. //相应的查询
  4. db.collection('shop').where({
  5. price:_eq(125)
  6. })

The global variable of the security rule now represents the timestamp of the current time, which allows the security rule to set some rules for the time node of the permission and the timeliness of the permission, which is not specified here.

Fifth, the global function get build permission system

The global function get can implement cross-collections to restrict permissions. D oc's permission match is more document-based, i.e. all documents in the collection have the same field, which is divided according to the value of the field. B ut sometimes we want to implement multiple users and multiple user roles to manage a collection of documents, with different permissions, and it can be very difficult to manage if both users and roles are written into every record in the document. This means that doc permission matching is not appropriate for complex user-managed documents.

We can split a single complex collection document (anti-paradigm design) into multiple collection documents (paradigm design) and separate users and roles from the document. F or example, blogs have post collections of articles, and user collections can be divided into authors, editors, contributors and other user identity, but also can be administrator groups, editing groups and so on. If we give more permissions to records or more complex groups, we need to store roles in their separate collections, rather than as a field in the target document, using the global function get to implement permission limits across collections.

The get function is a global function that can get the specified record across collections, which is used to get records across collections in security rules to participate in the matching of security rules, and the parameter format of the get function database.集合名.记录id

For example, we can set the following security rules for the article post collection, only the administrator can delete the record, and to determine whether the user is an administrator, we need to use the field values in the user collection across the collection to determine:

  1. //user集合的结构
  2. {
  3. _id:"oUL-m5FuRmuVmxvbYOGuXbuEDsn8", //用户的openid
  4. isManager:true
  5. }
  6. //post集合的权限
  7. {
  8. "read": "true",
  9. "delete": "get(`database.user.${auth.openid}`).isManager== true"
  10. }
  11. db.collection('post').where({
  12. //相应的条件,并不受子集的限制
  13. })

The get function can also receive variables, values can be obtained in a variety of calculations, such as using string templates for stitching, which is a query process, if there is a record in the corresponding document, the function returns the contents of the record, otherwise it returns empty (note the way in reverse quotes are written):

  1. `(database.${doc.collction}.${doc._id})`

The restriction of the get function

  • The variable doc that exists in the get function parameter in the security rule needs to appear in the query condition in the form of 1 or in, and if it does, only the in value is allowed, i.e. doc.shopId in array, array.length

  • An expression can have up to 3 get functions and access up to 3 different documents.

  • The get function has a maximum nesting depth of 2, which is get (path).

Read the action trigger and quota consumption description

The execution of the get function counts the number of database requests and is also subject to database quotas. I n the absence of variables, each get produces a read operation, and when a variable is used, a get read operation occurs for each variable value. For example:

Suppose there are the following rules on a collection shop:

  1. {
  2. "read": "auth.openid == get(`database.shop.${doc._id}`).owner",
  3. "write": false
  4. }

Five reads are generated when the following query statement is executed.

  1. db.collection('shop').where(_.or([{_id:1},{_id:2},{_id:3},{_id:4},{_id:5}])).get()