Skip to content

Commit 5e3047d

Browse files
committed
Translate the README.md to English #3
1 parent 41dba68 commit 5e3047d

File tree

2 files changed

+237
-42
lines changed

2 files changed

+237
-42
lines changed

README.ja.md

+194
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# multi_tenancy
2+
3+
## 概要
4+
5+
PostgreSQL の Row Level Security (RLS) を利用したマルチテナント Rails アプリケーションのサンプルです。
6+
7+
このアプリケーションでは、各テナントが複数のユーザーを抱え、それぞれのユーザーが複数の記事(articles)を持っています。
8+
9+
あるテナントのユーザーとしてこのアプリケーションにログインした場合、別のテナントのユーザーや記事は参照できません。
10+
また、そのユーザーは同じテナントの別のユーザーの記事を参照できますが、挿入・更新・削除はできません。
11+
12+
このようなアクセス制限をアプリケーション側に委ねると、情報漏えいや情報喪失を招くバグが混入しやすくなります。
13+
しかし、データベース側で制限をすれば、その種のバグが起こりえなくなります。
14+
15+
なお、PostgreSQL ではマルチテナントシステムの構築に [Citus](https://www.citusdata.com/product/community) という拡張機能がしばしば使われますが、このサンプルでは使用していません。Citus の主目的はシステムの「スケーラビリティ」の向上です。Citus は「シャーディング」という技法により巨大なデータベースを複数の PostgreSQL インスタンスに分散させます。
16+
17+
Citusの採用にはさまざまな利点がありますが、アプリケーションの開発者は「シャーディング」の仕組みをよく理解してプログラミングをしないと、思わぬエラーやパフォーマンスの低下を引き起こします。
18+
19+
作ろうとしている Web アプリケーションの規模がシャーディングを利用するほどに巨大にならないことがわかっているならば、このサンプルのように PostgreSQL の標準機能だけを用いてマルチテナントシステムを構築できます。
20+
21+
## 動作環境
22+
23+
* Ubuntu Server 16.04
24+
* PostgreSQL 10
25+
* Ruby 2.4
26+
* Ruby on Rails 5.1.4
27+
28+
## マイグレーションヘルパーメソッド `add_policies`
29+
30+
### `users` テーブルのマイグレーションスクリプト
31+
32+
```
33+
def up
34+
create_table :users do |t|
35+
t.references :tenant, null: false
36+
t.string :name, null: false
37+
38+
t.timestamps
39+
end
40+
41+
add_foreign_key :users, :tenants
42+
43+
add_policies("users")
44+
end
45+
```
46+
47+
### `articles` テーブルのマイグレーションスクリプト
48+
49+
```
50+
create_table :articles do |t|
51+
t.references :tenant, null: false
52+
t.references :user, null: false
53+
t.string :title, null: false
54+
t.text :body
55+
t.integer :pages, null: false, default: 0
56+
57+
t.timestamps
58+
end
59+
60+
add_foreign_key :articles, :tenants
61+
add_foreign_key :articles, :users
62+
63+
add_policies(
64+
"articles",
65+
[
66+
{ table_name: "users", foreign_key: "user_id" }
67+
]
68+
)
69+
end
70+
```
71+
72+
## Row Level Security について
73+
74+
Row Level Security (以下、「RLS」と呼ぶ)は、2016 年 1 月にリリースされた PostgreSQL 9.5 で導入された機能です。
75+
日本語では「行単位セキュリティ」と訳されることもあります。
76+
77+
従来の `GRANT` 文ではテーブル単位でのアクセス制御しかできませんでしたが、RLS を利用すれば行単位でのアクセス制御が可能となります。
78+
79+
`CREATE POLICY` 文によってセキュリティポリシーを定義することにより RLS が設定されます。次の例では `alice` ユーザーに対して `articles` テーブルの一部に対する参照を許可しています。
80+
81+
```
82+
CREATE POLICY alice_policy ON articles
83+
FOR SELECT
84+
TO alice
85+
USING (user_id = (SELECT id FROM users WHERE name = 'alice'))
86+
```
87+
88+
行への参照が許可される条件は `USING` 節の中に記述されます。上の例では、副問い合わせを用いて `users` テーブルから `name` 列の値が `'alice'` である行の `id` 列の値を取得し、それと `user_id` 列の値が一致する `articles` テーブルの行への参照を許しています。
89+
90+
ただし、初期状態では RLS は無効になっています。`ALTER TABLE` 文を用いてテーブル単位で有効にする必要があります。
91+
92+
```
93+
ALTER TABLE articles ENABLE ROW LEVEL SECURITY
94+
```
95+
96+
いま `users` テーブルに次のような行が保存されているとします。
97+
98+
```
99+
id | name
100+
---+-----
101+
1 | alice
102+
2 | bob
103+
3 | charlie
104+
```
105+
106+
そして、 `articles` テーブルに次のような行が保存されているとします。
107+
108+
```
109+
id | user_id | title
110+
---+---------|------
111+
1 | 1 | W
112+
2 | 1 | X
113+
3 | 2 | Y
114+
4 | 3 | Z
115+
```
116+
117+
以上のような状態で、`alice` ユーザーが次のようなクエリを発行したとします。
118+
119+
```
120+
SELECT title FROM articles
121+
```
122+
123+
すると、RLS が無効であれば 4 個の行が検索されることになりますが、RLS が有効となっていれば 2 個しか検索されません。
124+
125+
## `current_setting` 関数
126+
127+
この Rails アプリケーションでは、マルチテナントシステムにおけるセキュリティを高めるために PostgreSQL の `current_setting` 関数を利用しています。
128+
129+
PostgreSQL では `SET` 文を用いてカスタム変数に値をセットできます。次の例では、変数 `foo.bar``'X'` という文字列をセットしています。カスタム変数名には必ずドットを含む必要があります。
130+
131+
```
132+
SET foo.bar = 'X'
133+
```
134+
135+
カスタム変数の値は `current_setting` 関数を用いて取得できます。
136+
137+
```
138+
current_setting('foo.bar')
139+
```
140+
141+
この関数は `CREATE POLICY` 文の `USING` 節の中でも使えます。次の例をご覧ください。
142+
143+
```
144+
CREATE POLICY tenant_policy ON articles
145+
FOR SELECT
146+
TO CURRENT_USER
147+
USING (tenant_id::text = current_setting('session.current_tenant_id'))
148+
```
149+
150+
ここでは `articles` テーブルに `tenant_id` という整数型の列があると仮定しています。
151+
152+
以上のような状態で、あるユーザーが次のようなクエリを発行したとします。
153+
154+
```
155+
SET session.current_tenant_id = '1';
156+
SELECT title FROM articles;
157+
```
158+
159+
すると、このユーザーには `tenant_id` の値が 1 である行だけが見えることになります。
160+
161+
## `WITH CHECK`
162+
163+
`CREATE POLICY` 文に `WITH CHECK` 節を加えると、行の挿入・更新時にレコードがある条件を満たすかどうかが確認されます。条件を満たさない `INSERT` 文や `UPDATE` 文が発行されると、エラーとなります。
164+
165+
次の例をご覧ください。
166+
167+
```
168+
CREATE POLICY tenant_policy ON articles
169+
FOR SELECT, INSERT, UPDATE, DELETE
170+
TO CURRENT_USER
171+
USING (tenant_id::text = current_setting('session.current_tenant_id'))
172+
WITH CHECK (
173+
SELECT EXISTS (
174+
SELECT id FROM users
175+
WHERE id = articles.user_id
176+
AND tenant_id::text = current_setting('session.current_tenant_id')
177+
)
178+
)
179+
```
180+
181+
上記のようにセキュリティポリシーが設定されると、`articles` テーブルに対する行の挿入・更新時に `users` テーブルで次のふたつの条件を満たす行の有無が調べられ、なければエラーとなります。
182+
183+
* `id` 列の値と *k* が等しい。
184+
* `tenant_id` 列の値を文字列に変換すると、カスタム変数 `session.current_tenant_id` の値に等しくなる。
185+
186+
ただし、*k*`articles` テーブルに挿入・更新される行の `user_id` 列の値とします。
187+
188+
## RLS の利用上の注意
189+
190+
`SUPERUSER` および `BYPASSRLS` 属性を持つロールに対して RLS は常に無効です。また、テーブルのオーナーに対して、RLS はデフォルトで無効ですが、次のように `ALTER TABLE` 文を用いて有効化できます。
191+
192+
```
193+
ALTER TABLE articles FORCE ROW LEVEL SECURITY
194+
```

README.md

+43-42
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,35 @@
11
# multi_tenancy
22

3-
## 概要
3+
This is a tentative English translation from the original [README](README.ja.md) written in Japanese.
44

5-
PostgreSQL の Row Level Security (RLS) を利用したマルチテナント Rails アプリケーションのサンプルです。
5+
## Overview
66

7-
このアプリケーションでは、各テナントが複数のユーザーを抱え、それぞれのユーザーが複数の記事(articles)を持っています。
7+
This is a sample of multi-tenant Rails application using PostgreSQL's Row Level Security (RLS).
88

9-
あるテナントのユーザーとしてこのアプリケーションにログインした場合、別のテナントのユーザーや記事は参照できません。
10-
また、そのユーザーは同じテナントの別のユーザーの記事を参照できますが、挿入・更新・削除はできません。
9+
In this application, each tenant has multiple users and each user has multiple articles.
1110

12-
このようなアクセス制限をアプリケーション側に委ねると、情報漏えいや情報喪失を招くバグが混入しやすくなります。
13-
しかし、データベース側で制限をすれば、その種のバグが起こりえなくなります。
11+
If you log in to this application as a user of a certain tenant, users and articles of another tenant can not be referenced.
12+
In addition, the user can refer to another user's articles of the same tenant, but can not insert, update, or delete them.
1413

15-
なお、PostgreSQL ではマルチテナントシステムの構築に [Citus](https://www.citusdata.com/product/community) という拡張機能がしばしば使われますが、このサンプルでは使用していません。Citus の主目的はシステムの「スケーラビリティ」の向上です。Citus は「シャーディング」という技法により巨大なデータベースを複数の PostgreSQL インスタンスに分散させます。
14+
Leaving such access restrictions to the application side makes it easier for bugs that introduce information leakage and losses to get mixed in.
15+
However, if you restrict on the database side, that kind of bug will not occur.
1616

17-
Citusの採用にはさまざまな利点がありますが、アプリケーションの開発者は「シャーディング」の仕組みをよく理解してプログラミングをしないと、思わぬエラーやパフォーマンスの低下を引き起こします。
17+
Developers often uses an extension called [Citus] (https://www.citusdata.com/product/community) to build a multi-tenant system based on PostgreSQL, but we do not use it in this sample. The main purpose of Citus is to improve the "scalability" of the system. Citus distributes the huge database to multiple PostgreSQL instances using the technique "sharding".
1818

19-
作ろうとしている Web アプリケーションの規模がシャーディングを利用するほどに巨大にならないことがわかっているならば、このサンプルのように PostgreSQL の標準機能だけを用いてマルチテナントシステムを構築できます。
19+
There are various advantages to Citus's adoption, but application developers should understand the mechanism of "sharding" well in order not to cause unexpected errors and performance degradation.
2020

21-
## 動作環境
21+
If you know that the size of the Web application you are trying to create is not huge enough to use sharding, you can build a multi-tenant system using only PostgreSQL's standard functionalities as we did in this sample.
22+
23+
## Environment
2224

2325
* Ubuntu Server 16.04
2426
* PostgreSQL 10
2527
* Ruby 2.4
2628
* Ruby on Rails 5.1.4
2729

28-
## マイグレーションヘルパーメソッド `add_policies`
30+
## Migration helper method `add_policies`
2931

30-
### `users` テーブルのマイグレーションスクリプト
32+
### Migration script for the table `users`
3133

3234
```
3335
def up
@@ -44,7 +46,7 @@ Citusの採用にはさまざまな利点がありますが、アプリケーシ
4446
end
4547
```
4648

47-
### `articles` テーブルのマイグレーションスクリプト
49+
### Migration script for the table `articles`
4850

4951
```
5052
create_table :articles do |t|
@@ -69,14 +71,13 @@ Citusの採用にはさまざまな利点がありますが、アプリケーシ
6971
end
7072
```
7173

72-
## Row Level Security について
74+
## About Row Level Security
7375

74-
Row Level Security (以下、「RLS」と呼ぶ)は、2016 年 1 月にリリースされた PostgreSQL 9.5 で導入された機能です。
75-
日本語では「行単位セキュリティ」と訳されることもあります。
76+
Row Level Security (hereinafter referred to as "RLS") was introduced in PostgreSQL 9.5 released in January 2016.
7677

77-
従来の `GRANT` 文ではテーブル単位でのアクセス制御しかできませんでしたが、RLS を利用すれば行単位でのアクセス制御が可能となります。
78+
In the conventional `GRANT` statement, only access control on a table basis was possible, but access control on a row basis is possible by using RLS.
7879

79-
`CREATE POLICY` 文によってセキュリティポリシーを定義することにより RLS が設定されます。次の例では `alice` ユーザーに対して `articles` テーブルの一部に対する参照を許可しています。
80+
RLS is set by defining a security policy with the `CREATE POLICY` statement. The following example allows the user `alice` to refer to a part of the `articles` table.
8081

8182
```
8283
CREATE POLICY alice_policy ON articles
@@ -85,15 +86,15 @@ CREATE POLICY alice_policy ON articles
8586
USING (user_id = (SELECT id FROM users WHERE name = 'alice'))
8687
```
8788

88-
行への参照が許可される条件は `USING` 節の中に記述されます。上の例では、副問い合わせを用いて `users` テーブルから `name` 列の値が `'alice'` である行の `id` 列の値を取得し、それと `user_id` 列の値が一致する `articles` テーブルの行への参照を許しています。
89+
Conditions for which reference to rows is permitted are described in the `USING` clause. In the above example, we allow references to rows of the `articles` table whose `user_id` matches the ID of the user `alice`.
8990

90-
ただし、初期状態では RLS は無効になっています。`ALTER TABLE` 文を用いてテーブル単位で有効にする必要があります。
91+
Note that RLS is disabled in the initial state. It must be enabled on a per-table basis using the `ALTER TABLE` statement.
9192

9293
```
9394
ALTER TABLE articles ENABLE ROW LEVEL SECURITY
9495
```
9596

96-
いま `users` テーブルに次のような行が保存されているとします。
97+
Suppose that we have these records in the table `users`.
9798

9899
```
99100
id | name
@@ -103,7 +104,7 @@ id | name
103104
3 | charlie
104105
```
105106

106-
そして、 `articles` テーブルに次のような行が保存されているとします。
107+
And suppose that we have these records in the table `articles`.
107108

108109
```
109110
id | user_id | title
@@ -114,31 +115,31 @@ id | user_id | title
114115
4 | 3 | Z
115116
```
116117

117-
以上のような状態で、`alice` ユーザーが次のようなクエリを発行したとします。
118+
Suppose that the user `alice` issues the following query in the above state.
118119

119120
```
120121
SELECT title FROM articles
121122
```
122123

123-
すると、RLS が無効であれば 4 個の行が検索されることになりますが、RLS が有効となっていれば 2 個しか検索されません。
124+
Then, if RLS is disabled, 4 rows will be retrieved, but if RLS is enabled, only 2 are retrieved.
124125

125-
## `current_setting` 関数
126+
## `current_setting` function
126127

127-
この Rails アプリケーションでは、マルチテナントシステムにおけるセキュリティを高めるために PostgreSQL の `current_setting` 関数を利用しています。
128+
In this Rails application, we use PostgreSQL `current_setting` function to improve security in multi-tenant system.
128129

129-
PostgreSQL では `SET` 文を用いてカスタム変数に値をセットできます。次の例では、変数 `foo.bar` `'X'` という文字列をセットしています。カスタム変数名には必ずドットを含む必要があります。
130+
PostgreSQL allows you to set values for custom variables using the `SET` statement. In the following example, the variable `foo.bar` is set to the string `'X'`. A custom variable name must include a dot.
130131

131132
```
132133
SET foo.bar = 'X'
133134
```
134135

135-
カスタム変数の値は `current_setting` 関数を用いて取得できます。
136+
We can get the values of custom variables using `current_setting` function.
136137

137138
```
138139
current_setting('foo.bar')
139140
```
140141

141-
この関数は `CREATE POLICY` 文の `USING` 節の中でも使えます。次の例をご覧ください。
142+
This function can also be used in the `USING` clause of the` CREATE POLICY` statement. See the example below.
142143

143144
```
144145
CREATE POLICY tenant_policy ON articles
@@ -147,22 +148,22 @@ CREATE POLICY tenant_policy ON articles
147148
USING (tenant_id::text = current_setting('session.current_tenant_id'))
148149
```
149150

150-
ここでは `articles` テーブルに `tenant_id` という整数型の列があると仮定しています。
151+
Here we assume that there is an integer type column called `tenant_id` in the` articles` table.
151152

152-
以上のような状態で、あるユーザーが次のようなクエリを発行したとします。
153+
Suppose that a user issues the following query in the above state.
153154

154155
```
155156
SET session.current_tenant_id = '1';
156157
SELECT title FROM articles;
157158
```
158159

159-
すると、このユーザーには `tenant_id` の値が 1 である行だけが見えることになります。
160+
Then, for this user, only lines with `tenant_id` value of 1 will be visible.
160161

161-
## `WITH CHECK`
162+
## `WITH CHECK` clause
162163

163-
`CREATE POLICY` 文に `WITH CHECK` 節を加えると、行の挿入・更新時にレコードがある条件を満たすかどうかが確認されます。条件を満たさない `INSERT` 文や `UPDATE` 文が発行されると、エラーとなります。
164+
If you add the `WITH CHECK` clause to the` CREATE POLICY` statement, it will be checked whether the record satisfies certain conditions when inserting or updating the line. If an `INSERT` statement or `UPDATE` statement that does not satisfy the condition is issued, an error occurs.
164165

165-
次の例をご覧ください。
166+
See the example below.
166167

167168
```
168169
CREATE POLICY tenant_policy ON articles
@@ -178,16 +179,16 @@ CREATE POLICY tenant_policy ON articles
178179
)
179180
```
180181

181-
上記のようにセキュリティポリシーが設定されると、`articles` テーブルに対する行の挿入・更新時に `users` テーブルで次のふたつの条件を満たす行の有無が調べられ、なければエラーとなります。
182+
When the security policy is set as described above, the presence or absence of a row satisfying the following two conditions is checked in the `users` table when inserting / updating a row in the `articles` table, otherwise an error occurs.
182183

183-
* `id` 列の値と *k* が等しい。
184-
* `tenant_id` 列の値を文字列に変換すると、カスタム変数 `session.current_tenant_id` の値に等しくなる。
184+
* The value of the `id` column is equal to *k*.
185+
* Converting the value of the `tenant_id` column to a string, it is equal to the value of the custom variable `session.current_tenant_id`.
185186

186-
ただし、*k* `articles` テーブルに挿入・更新される行の `user_id` 列の値とします。
187+
The *k* is the value of the `user_id` column of the row to be inserted or updated in the` articles` table.
187188

188-
## RLS の利用上の注意
189+
## Notes on using RLS
189190

190-
`SUPERUSER` および `BYPASSRLS` 属性を持つロールに対して RLS は常に無効です。また、テーブルのオーナーに対して、RLS はデフォルトで無効ですが、次のように `ALTER TABLE` 文を用いて有効化できます。
191+
For roles with `SUPERUSER` and `BYPASSRLS` attributes, RLS is always disabled. Also, for table owners, RLS is disabled by default, but you can enable it using the `ALTER TABLE` statement as follows.
191192

192193
```
193194
ALTER TABLE articles FORCE ROW LEVEL SECURITY

0 commit comments

Comments
 (0)