0

私はFirebaseデータベースのフラットなデータ構造をお勧めしていますが、その後はアクセスを正しく制御するための一連のルールを思いつきました。私の例では、異なる組織間でマルチテナントであるデータベースへのアクセスをロック/ロックする方法を示すことを試みています。Firebaseデータベースのルール - コレクションへのアクセスを制御する

私の最初の試みは、このようなものだった:ここでの問題は、私はできなかったことと思われたがhttps://gist.github.com/peteski22/b038d81641c1409cec734d187272eeba

{ 
    "rules" : { 
     "admins" : { 
      ".read" : "root.child('admins').hasChild(auth.uid)", 
      ".write" : "root.child('admins').hasChild(auth.uid)" 
     }, 
     "users" : { 
      "$user" : { 
       ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "root.child('admins').hasChild(auth.uid)" 
      }    
     }, 
     "organizations" : { 
      "$organization" : { 
       ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "root.child('admins').hasChild(auth.uid)" 
      }    
     }, 
     "employees" : { 
      "$employee" : { 
       ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()" 
      }    
     } 
    } 
} 

データベースの構造:https://gist.github.com/peteski22/40b0a79a6854d7bb818919a5262f4a7e

{ 
    "admins" : {   
     "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true    
    }, 
    "organizations": { 
     "-JiGh_31GA20JabpZBfc" : { 
      "name" : "Comp1 Ltd" 
     }, 
     "-JiGh_31GA20JabpZBfd" : { 
      "name" : "company2 PLC" 
     } 
    },    
    "users" : { 
     "8UnM6LIiZJYAHVdty6gzdD8oVI42": { 
      "firstName" : "Peter", 
      "lastName" : "Piper", 
      "email" : "[email protected]", 
      "organization" : "" 
     }, 
     "-JiGh_31GA20JabpZBfe" : { 
      "firstName" : "Joe", 
      "lastName" : "Blogs", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : { 
      "firstName" : "test", 
      "lastName" : "user", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfd" 
     } 
    }, 
    "employees" : { 
     "-JiGh_31GA20JabpZBeb" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Johnny", 
      "lastName" : "Baggs", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV123456"   
     }, 
     "-JiGh_31GA20JabpZBec" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Maheswari", 
      "lastName" : "Sanjal", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111"    
     }, 
     "-JiGh_31GA20JabpZBce" : { 
      "organization" : "-JiGh_31GA20JabpZBfd", 
      "firstName" : "Fedde", 
      "lastName" : "le Grande", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111" 
     } 
    } 
} 

データベースのルール次のようにしてください:

[GET] /employees 

ログインしたユーザーと同じ組織に所属する従業員のコレクションを表示します。

ぼんやりとした後、私はドキュメントの中でこれを読んでいます:https://firebase.google.com/docs/database/security/securing-data#rules_are_not_filtersこれは私のやり方でデータを取得したいと思っていると思います。

試み#2構造:https://gist.github.com/peteski22/4593733bf54815393a443dfcd0f34c04

{ 
    "admins" : {   
     "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true    
    }, 
    "organizations": { 
     "-JiGh_31GA20JabpZBfc" : { 
      "name" : "Comp1 Ltd",   
      "users" : { 
       "-JiGh_31GA20JabpZBfe" : true, 
       "-JiGh_31GA20JabpZBff" : true, 
       "-JiGh_31GA20JabpZBea" : true 
      }, 
      "employees" : { 
       "-JiGh_31GA20JabpZBeb" : true, 
       "-JiGh_31GA20JabpZBec" : true 
      } 
     }, 
     "-JiGh_31GA20JabpZBfd" : { 
      "name" : "company2 PLC",   
      "users" :{ 
       "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true 
      }, 
      "employees" :{ 
       "-JiGh_31GA20JabpZBce" : true 
      } 
     } 
    }, 
    "users" : { 
     "8UnM6LIiZJYAHVdty6gzdD8oVI42": { 
      "firstName" : "Peter", 
      "lastName" : "Piper", 
      "email" : "[email protected]", 
      "organization" : "" 
     }, 
     "-JiGh_31GA20JabpZBfe" : { 
      "firstName" : "Joe", 
      "lastName" : "Blogs", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "-JiGh_31GA20JabpZBff" : { 
      "firstName" : "Sally", 
      "lastName" : "McSwashle", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "-JiGh_31GA20JabpZBea" : { 
      "firstName" : "Eva", 
      "lastName" : "Rushtock", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : { 
      "firstName" : "test", 
      "lastName" : "user", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfd" 
     } 
    }, 
    "employees" : { 
     "-JiGh_31GA20JabpZBeb" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Johnny", 
      "lastName" : "Baggs", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV123456" 
     }, 
     "-JiGh_31GA20JabpZBec" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Maheswari", 
      "lastName" : "Sanjal", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111" 
     }, 
     "-JiGh_31GA20JabpZBce" : { 
      "organization" : "-JiGh_31GA20JabpZBfd", 
      "firstName" : "Fedde", 
      "lastName" : "le Grande", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111" 
     } 
    } 
} 

試み#戻る製図板へ

、およびhttps://www.firebase.com/docs/web/guide/structuring-data.html/https://firebase.google.com/docs/database/web/structure-data

を読んだ後、私はいくつかのDB構造の変更やルールを作りました2ルール:https://gist.github.com/peteski22/e1be434cd1ea8ec2e630bec6d8aa714f

{ 
    "rules" : { 
     "admins" : { 
      ".read" : "root.child('admins').hasChild(auth.uid)", 
      ".write" : "root.child('admins').hasChild(auth.uid)" 
     }, 
     "users" : { 
      ".indexOn": [ "organization" ], 
      "$user" : { 
       ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "root.child('admins').hasChild(auth.uid)" 
      }    
     }, 
     "organizations" : { 
      ".indexOn": [ "users", "employees" ], 
      "$organization" : { 
       ".read" : "$organization === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "root.child('admins').hasChild(auth.uid)" 
      }    
     }, 
     "employees" : { 
      ".indexOn": [ "organization" ], 
      "$employee" : { 
       ".read" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()", 
       ".write" : "data.child('organization').val() === root.child('users').child(auth.uid).child('organization').val()" 
      }    
     } 
    } 
} 

今はデータを各コレクションに細かく保つことができますが、何かを得る唯一の方法はです。組織IDを知っていて、その組織を取得してから、それぞれの従業員をIDで取得します。データを構造化するための上記の文書(セクション:結合データの結合)は、OOとSQLのバックグラウンドから来て、これを行うのがうまくいくと示唆しているようですが、 。

誰かが正しいトラックを下って行くのか、代わりに試してみるべきかについてアドバイスをいただければ、大変感謝します。

おかげ ピーター

答えて

0

ドキュメントを読み、firebaseコミュニティのたるみの人々とおしゃべりをした後、私は正しい軌道に乗っていたという結論に来ています。

"Bolt"(firebase-bolt in npm)というコンパイラを使っても、ルールを生成するのに非常に便利です。

ここに私の構造、ボルトルールとコンパイルJSONルールです:

構造

{ 
    "admins" : {   
     "8UnM6LIiZJYAHVdty6gzdD8oVI42" : true    
    }, 
    "organizations": { 
     "-JiGh_31GA20JabpZBfc" : { 
      "name" : "Comp1 Ltd",   
      "users" : { 
       "-JiGh_31GA20JabpZBfe" : true, 
       "-JiGh_31GA20JabpZBff" : true, 
       "-JiGh_31GA20JabpZBea" : true 
      }, 
      "employees" : { 
       "-JiGh_31GA20JabpZBeb" : true, 
       "-JiGh_31GA20JabpZBec" : true 
      } 
     }, 
     "-JiGh_31GA20JabpZBfd" : { 
      "name" : "company2 PLC",   
      "users" :{ 
       "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : true 
      }, 
      "employees" :{ 
       "-JiGh_31GA20JabpZBce" : true 
      } 
     } 
    }, 
    "users" : { 
     "8UnM6LIiZJYAHVdty6gzdD8oVI42": { 
      "firstName" : "Peter", 
      "lastName" : "Piper", 
      "email" : "[email protected]", 
      "organization" : "" 
     }, 
     "-JiGh_31GA20JabpZBfe" : { 
      "firstName" : "Joe", 
      "lastName" : "Blogs", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "-JiGh_31GA20JabpZBff" : { 
      "firstName" : "Sally", 
      "lastName" : "McSwashle", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "-JiGh_31GA20JabpZBea" : { 
      "firstName" : "Eva", 
      "lastName" : "Rushtock", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfc" 
     }, 
     "WgnHjk5D8xbuYeA7VDM3ngKwCYV2" : { 
      "firstName" : "test", 
      "lastName" : "user", 
      "email" : "[email protected]", 
      "organization" : "-JiGh_31GA20JabpZBfd" 
     } 
    }, 
    "employees" : { 
     "-JiGh_31GA20JabpZBeb" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Johnny", 
      "lastName" : "Baggs", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV123456" 
     }, 
     "-JiGh_31GA20JabpZBec" : { 
      "organization" : "-JiGh_31GA20JabpZBfc", 
      "firstName" : "Maheswari", 
      "lastName" : "Sanjal", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111" 
     }, 
     "-JiGh_31GA20JabpZBce" : { 
      "organization" : "-JiGh_31GA20JabpZBfd", 
      "firstName" : "Fedde", 
      "lastName" : "le Grande", 
      "email" : "[email protected]", 
      "employeeNumber" : "ASV111111" 
     } 
    } 
} 

ボルトルール

// ********** 
// FUNCTIONS 
// ********** 

function isAdmin (auth) { 
    return root.admins[auth.uid] != null 
} 

function isInSameOrganization(auth, orgUid) { 
    return root.users[auth.uid].organization === orgUid 
} 

// ********** 
// PATHS 
// ********** 

path /admins { 
    read() { isAdmin(auth) } 
    write() { isAdmin(auth) } 
} 

path /users { 
    index() { ["organization"] } 
    write() { isAdmin(auth) } 
} 

path /users/{id} is User { 
    read() { isInSameOrganization(auth, id) || isAdmin(auth) } 
} 

path /organizations { 
    write() { isAdmin(auth) } 
} 

path /organizations/{id} is Organization { 
    read() { isInSameOrganization(auth, id) } 
} 

path /employees { 
    index() { ["organization"] } 
    write() { isInSameOrganization(auth, this.organization) || isAdmin(auth) } 
} 

path /employees/{id} is Employee { 
    read() { isInSameOrganization(auth, id) || isAdmin(auth) } 
} 

// ********** 
// TYPES 
// ********** 
type OrganizationID extends String { 
    validate() { root.organizations[this] != null } 
} 

type UserID extends String { 
    validate() { root.users[this] != null } 
} 

type EmployeeID extends String { 
    // Validate that the user ID exists in the employees node (read rule access should prevent us reading a employees that isn't in our org) 
    validate() { root.employees[this] != null } 
} 

type Email extends String { 
    validate() { 
     return this.matches(/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\\.[A-Z]{2,4}$/i); 
    } 
} 

type User { 
    firstName: String, 
    lastName: String 
    email: Email, 
    organization: OrganizationID 
} 

type Employee { 
    organization: OrganizationID, 
    firstName: String, 
    lastName: String, 
    email: Email, 
    employeeNumber: String  
} 

type Organization { 
    name: String,  
    users: Map<UserID, Boolean> | Null, 
    employees: Map<EmployeeID, Boolean> | Null 
} 

(ボルトによって生成された)JSONルール

{ 
    "rules": { 
    "admins": { 
     ".read": "root.child('admins').child(auth.uid).val() != null", 
     ".write": "newData.parent().child('admins').child(auth.uid).val() != null" 
    }, 
    "users": { 
     ".write": "newData.parent().child('admins').child(auth.uid).val() != null", 
     ".indexOn": [ 
     "organization" 
     ], 
     "$id": { 
     ".validate": "newData.hasChildren(['firstName', 'lastName', 'email', 'organization'])", 
     "firstName": { 
      ".validate": "newData.isString()" 
     }, 
     "lastName": { 
      ".validate": "newData.isString()" 
     }, 
     "email": { 
      ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)" 
     }, 
     "organization": { 
      ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null" 
     }, 
     "$other": { 
      ".validate": "false" 
     }, 
     ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null" 
     } 
    }, 
    "organizations": { 
     ".write": "newData.parent().child('admins').child(auth.uid).val() != null", 
     "$id": { 
     ".validate": "newData.hasChildren(['name'])", 
     "name": { 
      ".validate": "newData.isString()" 
     }, 
     "users": { 
      "$key1": { 
      ".validate": "newData.parent().parent().parent().parent().child('users').child($key1).val() != null && newData.isBoolean()" 
      }, 
      ".validate": "newData.hasChildren()" 
     }, 
     "employees": { 
      "$key2": { 
      ".validate": "newData.parent().parent().parent().parent().child('employees').child($key2).val() != null && newData.isBoolean()" 
      }, 
      ".validate": "newData.hasChildren()" 
     }, 
     "$other": { 
      ".validate": "false" 
     }, 
     ".read": "root.child('users').child(auth.uid).child('organization').val() == $id" 
     } 
    }, 
    "employees": { 
     ".write": "newData.parent().child('users').child(auth.uid).child('organization').val() == newData.child('organization').val() || newData.parent().child('admins').child(auth.uid).val() != null", 
     ".indexOn": [ 
     "organization" 
     ], 
     "$id": { 
     ".validate": "newData.hasChildren(['organization', 'firstName', 'lastName', 'email', 'employeeNumber'])", 
     "organization": { 
      ".validate": "newData.isString() && newData.parent().parent().parent().child('organizations').child(newData.val()).val() != null" 
     }, 
     "firstName": { 
      ".validate": "newData.isString()" 
     }, 
     "lastName": { 
      ".validate": "newData.isString()" 
     }, 
     "email": { 
      ".validate": "newData.isString() && newData.val().matches(/^[A-Z0-9._%+-][email protected][A-Z0-9.-]+\\\\.[A-Z]{2,4}$/i)" 
     }, 
     "employeeNumber": { 
      ".validate": "newData.isString()" 
     }, 
     "$other": { 
      ".validate": "false" 
     }, 
     ".read": "root.child('users').child(auth.uid).child('organization').val() == $id || root.child('admins').child(auth.uid).val() != null" 
     } 
    } 
    } 
} 

私はまだこれにバグを見つけていますが、進歩を見せていると思います。ちょうどよい公式&がYoutubeの非公式のFirebaseビデオであることに注意するために、文書はかなりの部分のためにかなりまともであり、firebaseコミュニティは友好的であるようです。だから私のような新しい出会い者はどこから始めるべきか知っています。

関連する問題