ãã«ãããã³ãã¢ããªåãPostgreSQLè¡ã¬ãã«ã»ãã¥ãªãã£ã®ãã¿ãŒã³
PostgreSQLã®è¡ã¬ãã«ã»ãã¥ãªãã£ïŒRLSïŒã«ã€ããŠãããã³ãåé¢ãããŒã«ã«ãŒã«ã®å®è·µçãã¿ãŒã³ã解説ããŸããã¢ã¯ã»ã¹å¶åŸ¡ãã¢ããªã ãã§ãªãããŒã¿ããŒã¹åŽã§åŒ·å¶ããæ¹æ³ãåŠã¹ãŸãã

ãªãããŒã¿ããŒã¹åŽã§ã®ã¢ã¯ã»ã¹åŒ·å¶ãéèŠãªã®ã
æ¥åã¢ããªã§ã¯ããŠãŒã¶ãŒã¯èªç€Ÿã®ããŒã¿ããèŠãããªããããããŒãžã£ãŒã ããè¿éãæ¿èªã§ããããªã©ã®ã«ãŒã«ããããããŸããå€ãã®ããŒã ã¯ããããUIãAPIåŽã§å®è£ ããŠããã§ååã ãšèããŸãããåé¡ã¯ããŒã¿ããŒã¹ã«å°éããçµè·¯ãã²ãšã€å¢ããããšã«æ å ±æŒããã®ãã£ã³ã¹ãå¢ããããšã§ããå éšã®ç®¡çããŒã«ãããã¯ã°ã©ãŠã³ããžã§ããåæã¯ãšãªãå¿ãããããšã³ããã€ã³ãããããã¯ãã§ãã¯ãã¹ãããããŠããŸããã°ãªã©ã該åœããŸãã
ããã³ãåé¢ãšã¯ããã顧客ïŒããã³ãïŒãå¥ã®é¡§å®¢ã®ããŒã¿ã決ããŠèªã¿åã£ãã倿Žãããã§ããªãããã«ããããšã§ããããŒã«ããŒã¹ã®ã¢ã¯ã»ã¹ã¯ãåãããã³ãå ã§ããšãŒãžã§ã³ãããããŒãžã£ãŒãçµçãªã©ã§æš©éãç°ãªãããšãæããŸãããããã®ã«ãŒã«ã¯èª¬æã¯ç°¡åã§ãããã¢ããªã®è€æ°ç®æã«æ£ãã°ããšäžè²«æ§ãä¿ã€ã®ã¯é£ãããªããŸãã
PostgreSQLã®è¡ã¬ãã«ã»ãã¥ãªãã£ïŒRLSïŒã¯ãã©ã®è¡ãåç
§ã»å€æŽå¯èœããããŒã¿ããŒã¹èªèº«ã決å®ããæ©èœã§ããã¢ããªåŽã®åã¯ãšãªãæ£ããWHEREå¥ãèŠããŠããããšã«é Œã代ããã«ãããŒã¿ããŒã¹ãèªåçã«ããªã·ãŒãé©çšããŸãã
RLSãå šãŠã®åé¡ã解決ããããã§ã¯ãããŸãããã¹ããŒãèšèšã代ããã«è¡ã£ãããèªèšŒã眮ãæãããããã§ã«åŒ·åãªããŒã¿ããŒã¹æš©éãæã€ãŠãŒã¶ãŒïŒã¹ãŒããŒãŠãŒã¶ãŒãªã©ïŒããã®ä¿è·ã«ã¯ãªããªãç¹ã«æ³šæããŠãã ããããŸããããè¡ã¯æŽæ°ã§ãããéžæã§ããªãããšãã£ãè«ççãã¹ãé²ãã«ã¯ãèªã¿åããšæžã蟌ã¿äž¡æ¹ã®ããªã·ãŒãæžãå¿ èŠããããŸãã
ãã ãåŸãããå©ç¹ã¯å€§ããã§ãã
- ããŒã¿ããŒã¹ã«å°éãããã¹ãŠã®çµè·¯ã«å¯ŸããŠäžå ã®ã«ãŒã«ãé©çšã§ãã
- æ°æ©èœã远å ããããšãã®ããã£ã¡ãã£ãããªã¹ã¯ãæžã
- SQLäžã§ã¢ã¯ã»ã¹ã«ãŒã«ãèŠããã®ã§ç£æ»ãæç¢ºã«ãªã
- APIã®ãã°ãããæããå Žåã®é²åŸ¡åãåäžãã
åæèšå®ã«ã¯å°ãæéãããããŸãã誰ããªã¯ãšã¹ãããŠããããã©ã®ããã³ãããäžè²«ããŠããŒã¿ããŒã¹ã«æž¡ãä»çµã¿ãå¿ èŠã§ãã¢ããªãæé·ããã«ã€ããŠããªã·ãŒã®ã¡ã³ããã³ã¹ãå¿ èŠã§ããç¹ã«SaaSãæ©å¯ããŒã¿ãæ±ãå éšããŒã«ã§ã¯ããã®æè³ã«èŠåãã ãã®ãªã¿ãŒã³ããããŸãã
ãžã£ãŒãŽã³ç¡ãã§åããè¡ã¬ãã«ã»ãã¥ãªãã£ã®åºæ¬
è¡ã¬ãã«ã»ãã¥ãªãã£ïŒRLSïŒã¯ãã¯ãšãªãã©ã®è¡ãèŠãã倿Žãããã§ããããèªåçã«ãã£ã«ã¿ãªã³ã°ããŸããåç»é¢ãAPIãšã³ããã€ã³ããã¬ããŒããã«ãŒã«ãèŠããŠããããšã«é Œã代ããã«ãããŒã¿ããŒã¹ããããé©çšããŸãã
PostgreSQLã®RLSã§ã¯ãSELECTãINSERTãUPDATEãDELETEããšã«ãã§ãã¯ãããããªã·ãŒãæžããŸããäŸãã°ããã®ãŠãŒã¶ãŒã¯ããã³ãAã®è¡ããèŠãããªãããšããªã·ãŒã§å®çŸ©ããŠããã°ããã£ãããã管çããŒãžãæ°ããã¯ãšãªãæ¥ãã®ããããã£ãã¯ã¹ã§ãåãã¬ãŒãã¬ãŒã«ãåããŸãã
RLSã¯GRANT/REVOKEãšã¯å¥ç©ã§ããGRANTã¯ããŒãã«èªäœã«ã¢ã¯ã»ã¹ã§ãããïŒãããã¯ååäœïŒã決ããŸãããRLSã¯ãã®ããŒãã«å
ã®ã©ã®è¡ã«ã¢ã¯ã»ã¹ã§ããããæ±ºããŸããçŸå®çã«ã¯äž¡æ¹ãçµã¿åãããŸãïŒGRANTã§ããŒãã«ã¢ã¯ã»ã¹ãå¶éããRLSã§ã¢ã¯ã»ã¹ã§ããè¡ãå¶éããããšãã圢ã§ãã
ãŸãå®éçšã§ãæå¹ã§ãããã¥ãŒã¯å€ãã®å ŽåRLSã«åŸããŸãããçµåããµãã¯ãšãªã§ããã£ã«ã¿ãªã³ã°ã¯å¹ããŸããã©ã®ã¯ã©ã€ã¢ã³ãããå®è¡ãããã¯ãšãªã§ãïŒã¢ããªã³ãŒããSQLã³ã³ãœãŒã«ãããããã¬ããŒãããŒã«ãªã©ïŒããªã·ãŒãé©çšãããŸãã
RLSã¯è€æ°ã®ã¯ãšãªææ®µãå€ãã®ããŒã«ãåãããŒãã«ãå ±æããã±ãŒã¹ïŒSaaSãå éšããŒã«ã§ããããïŒã«åããŠããŸããåäžã®ä¿¡é Œã§ããããã¯ãšã³ããããªãå°ããªã¢ããªããæ©å¯æ§ãäœãäžã€ã®ãµãŒãã¹ããããã¢ã¯ã»ã¹ãããªãããŒã¿ã«ã¯éå°ãªå ŽåããããŸãã管çããŒã«ããšã¯ã¹ããŒããBIãã¹ã¯ãªãããªã©è€æ°ã®å ¥ãå£ãããç¬éã«ãRLSã®å°å ¥ã¯äŸ¡å€ãçã¿ãŸãã
ãŸãã¯ããã³ããããŒã«ãããŒã¿ææãæŽçãã
ããªã·ãŒãæžãåã«ããŸã誰ãäœãææããŠããããæç¢ºã«ããŠãã ãããPostgreSQLã®RLSã¯ãããŒã¿ã¢ãã«ãæ¢ã«ããã³ããããŒã«ãææãåæ ããŠãããšæã广çã«æ©èœããŸãã
ãŸãããã³ãã§ããå€ãã®SaaSã¢ããªã§ã¯ã顧客ããŒã¿ãå«ãå
±çšããŒãã«ã«ã¯tenant_idã«ã©ã ãèšããã®ãæãç°¡åãªã«ãŒã«ã§ããè«æ±æžã®ãããªæãããªããŒãã«ã ãã§ãªããæ·»ä»ãã¡ã€ã«ãã³ã¡ã³ããç£æ»ãã°ãããã¯ã°ã©ãŠã³ããžã§ãã®ããã«å¿ããã¡ãªããŒãã«ã«ãå¿
èŠã§ãã
次ã«å®éã«äœ¿ãããŒã«åãæ±ºããŸããå°æ°ã§ãããããããã®ã«ããŸãïŒownerãmanagerãagentãread-onlyãªã©ããããã¯åŸã§ããªã·ãŒã®ãã§ãã¯ã«å¯Ÿå¿ãããæ¥åäžã®ããŒã«ã§ãïŒããŒã¿ããŒã¹ããŒã«ãšã¯å¥ç©ã§ãïŒã
ãã®åŸãã¬ã³ãŒãã®ææåœ¢æ ãæ±ºããŸããããããŒãã«ã¯åäžãŠãŒã¶ãŒãææããïŒäŸãã°ãã©ã€ããŒããªã¡ã¢ïŒãå¥ã®ããŒãã«ã¯ããŒã ææïŒå ±æã€ã³ããã¯ã¹ïŒãšããå ·åã§ããæ··åšããããšããªã·ãŒãèªã¿ã¥ãããªããè¿åããããããªããŸãã
ã«ãŒã«ãããã¥ã¡ã³ãåããç°¡åãªæ¹æ³ã¯ãåããŒãã«ã«ã€ããŠæ¬¡ã®è³ªåã«çããããšã§ãïŒ
- ããã³ãå¢çã¯ã©ã®ã«ã©ã ã§åŒ·å¶ãããïŒã©ã®ã«ã©ã ãå¢çãïŒïŒ
- 誰ãèªãããšãã§ãããïŒããŒã«ãšææã®èгç¹ã§ïŒïŒ
- 誰ãäœæã»æŽæ°ã§ãããïŒã©ããªæ¡ä»¶ã§ïŒïŒ
- 誰ãåé€ã§ãããïŒéåžžæãå³ããã«ãŒã«ïŒïŒ
- ã©ããªäŸå€ãèš±ããïŒãµããŒãã¹ã¿ãããèªååããšã¯ã¹ããŒããªã©ïŒïŒ
äŸïŒInvoicesã¯ãããŒãžã£ãŒã¯ãã®ããã³ãã®ãã¹ãŠã®è«æ±æžãé²èЧã§ãããšãŒãžã§ã³ãã¯æ
åœé¡§å®¢ã®è«æ±æžã ããèªã¿åãå°çšãŠãŒã¶ãŒã¯é²èЧã®ã¿ã§ç·šéäžå¯ããšããããã«æ±ºããŸããã©ã®ã«ãŒã«ãå³å¯ã«å®ãã¹ããïŒããã³ãåé¢ãåé€ïŒãšã©ããæè»ã«ãããïŒãããŒãžã£ãŒã®è¿œå å¯èŠæ§ïŒãå
ã«æ±ºããŠãããšè¯ãã§ããAppMasterã®ãããªããŒã³ãŒãããŒã«ã§æ§ç¯ããå Žåã§ãããã®ãããã³ã°ã¯UIã®æåŸ
å€ãšããŒã¿ããŒã¹ã«ãŒã«ãäžèŽãããã®ã«åœ¹ç«ã¡ãŸãã
ãã«ãããã³ãããŒãã«ã®èšèšãã¿ãŒã³
ãã«ãããã³ãRLSã¯ãããŒãã«ã®åœ¢ãäºæž¬å¯èœã§ããã»ã©ç®¡çãããããªããŸããããŒãã«ããšã«ããã³ãã®ä¿ææ¹æ³ããã©ãã©ã ãšãããªã·ãŒãé£è§£ã«ãªããŸããäžè²«ããèšèšã¯RLSããªã·ãŒãèªã¿ãããããã¹ããããããæ£ããä¿ã€ã®ã«åœ¹ç«ã¡ãŸãã
ãŸãã¯äžã€ã®ããã³ãèå¥åãéžã³ãã©ãã§ãåãããã«äœ¿ããŸããããUUIDã¯æšæž¬ããã«ããå€ãã®ã·ã¹ãã ã§çæããããããäžè¬çã§ããå éšåããªãæŽæ°ã§ãæ§ããŸãããã¹ã©ãã°ïŒ"acme"ã®ãããªïŒã¯äººéã«ãšã£ãŠåãããããã§ããå€ããããšããããã衚瀺çšã«ããã³ã¢ããŒã«ã¯ããªãæ¹ãç¡é£ã§ãã
ããã³ãã¹ã³ãŒãã®ããŒã¿ã«ã¯ã該åœãããã¹ãŠã®ããŒãã«ã«tenant_idã«ã©ã ã远å ããå¯èœãªãNOT NULLã«ããŸããããã³ãç¡ãã§è¡ãååšãåŸãç¶æ
ã¯èšèšã®ã«ããã§ãã°ããŒãã«ããŒã¿ãšããã³ãããŒã¿ãæ··åšããŠããå¯èœæ§ãé«ããRLSããªã·ãŒãè€éã«ããŸãã
ã€ã³ããã¯ã¹ã¯ã·ã³ãã«ã§ããéèŠã§ããSaaSã¢ããªã®å€ãã®ã¯ãšãªã¯ãŸãããã³ãã§ãã£ã«ã¿ãããã®åŸã¹ããŒã¿ã¹ãæ¥ä»ãªã©ã§çµããŸããããã©ã«ãã§ã¯tenant_idã«ã€ã³ããã¯ã¹ã貌ããé«ãã©ãã£ãã¯ãªããŒãã«ã¯(tenant_id, created_at)ã(tenant_id, status)ã®ãããªè€åã€ã³ããã¯ã¹ãæ€èšããŠãã ããã
ã©ã®ããŒãã«ãã°ããŒãã«ã§ã©ããããã³ãã¹ã³ãŒããªã®ããæ©ãã«æ±ºããŠãã ãããäžè¬çãªã°ããŒãã«ããŒãã«ã«ã¯åœã³ãŒããé貚ã³ãŒãããã©ã³å®çŸ©ãªã©ããããŸããããã³ãã¹ã³ãŒãã®ããŒãã«ã«ã¯é¡§å®¢ãè«æ±æžããã±ãããªã©ãããã³ããææãããã®ãå«ãŸããŸãã
ä¿å®ããããã«ãŒã«ãäœãã«ã¯ãçžããçãä¿ã€ã®ãã³ãã§ãïŒ
- ããã³ãã¹ã³ãŒãã®ããŒãã«ïŒ
tenant_id NOT NULLãRLSæå¹ãããªã·ãŒã¯åžžã«tenant_idããã§ãã¯ããã - ã°ããŒãã«åç
§ããŒãã«ïŒ
tenant_idãªããããã³ãããªã·ãŒç¡ããã»ãšãã©ã®ããŒã«ã¯èªã¿åãã®ã¿ã - å ±æã ãå¶åŸ¡ãå¿ èŠãªããŒãã«ïŒæŠå¿µããšã«ããŒãã«ãåããïŒã°ããŒãã«ãšããã³ãè¡ãæ··ããªãïŒã
AppMasterã®ãããªããŒã«ã§äœãå Žåããã®äžè²«æ§ã¯ããŒã¿ã¢ãã«åŽã§ãæå©ã«åããŸããtenant_idãæšæºãã£ãŒã«ãã«ãªãã°ãåããã¿ãŒã³ãã¢ãžã¥ãŒã«éã§ç¹°ãè¿ã䜿ããŸãã
ã¹ããããã€ã¹ãããïŒæåã®ããã³ãããªã·ãŒãäœã
PostgreSQLã®RLSã§ã®æåã®åå©ã¯ãããããŒãã«ãçŸåšã®ããã³ãå
ã§ã®ã¿èªã¿åãå¯èœã«ããããšã§ããç®çã¯åçŽã§ãïŒAPIã§WHEREå¥ãå¿ããŠããããŒã¿ããŒã¹ãä»ããã³ãã®è¡ãè¿ããªãããã«ããããšã
ãŸãtenant_idã«ã©ã ãããããŒãã«ããå§ããŸãã
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
RLSãæå¹ã«ãããšãããé©ãããã®ã¯ããã©ã«ãã®æ¯ãèãã§ãïŒããããŒã«ãRLSã®å¯Ÿè±¡ã§ããã€ãããããããªã·ãŒããªãå ŽåãSELECTã¯ãŒãè¡ãè¿ãïŒæžã蟌ã¿ã¯å€±æããŸãïŒããããæåã®æ®µéã§ã¯æãŸããæåã§ãã
æ¬¡ã«æå°éã®èªã¿åãããªã·ãŒã远å ããŸãããã®äŸã§ã¯ã¢ããªããã°ã€ã³åŸã«app.tenant_idã®ãããªã»ãã·ã§ã³å€æ°ãèšå®ããæ³å®ã§ãïŒ
CREATE POLICY invoices_tenant_read
ON invoices
FOR SELECT
USING (tenant_id = current_setting('app.tenant_id')::uuid);
æ¬¡ã«æžã蟌ã¿ã«ãŒã«ã远å ããŸããRLSã§ã¯USINGãæ¢åè¡ã«ã¢ã¯ã»ã¹ã§ãããããWITH CHECKãæ¿å
¥ã»æŽæ°æã«èš±å¯ãããæ°ããå€ããå¶åŸ¡ããŸãã
CREATE POLICY invoices_tenant_insert
ON invoices
FOR INSERT
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_update
ON invoices
FOR UPDATE
USING (tenant_id = current_setting('app.tenant_id')::uuid)
WITH CHECK (tenant_id = current_setting('app.tenant_id')::uuid);
CREATE POLICY invoices_tenant_delete
ON invoices
FOR DELETE
USING (tenant_id = current_setting('app.tenant_id')::uuid);
ããªã·ãŒã¯ããã©ã«ãã§PERMISSIVEïŒããããã®ããªã·ãŒã§èš±å¯ãããã°ããïŒã§ããè€æ°ã®æ¡ä»¶ãã¹ãŠãæºããå¿
èŠãããå Žåã¯RESTRICTIVEãéžã³ãŸãïŒäŸãã°ãã¢ã«ãŠã³ããæå¹ã§ããããšããªã©ã®äºéã®ã¬ãŒããå
¥ããæã«æçšã§ãïŒã
ããªã·ãŒã¯å°ãããããŒã«ããšã«åããŠãããšèªã¿ããããªããŸããORãå€çšãã巚倧ãªã«ãŒã«ãããinvoices_tenant_read_app_userãinvoices_tenant_read_support_agentã®ããã«å¯Ÿè±¡ããšã«åããæ¹ãããã¹ããã¬ãã¥ãŒãå°æ¥çãªå€æŽã楜ã§å®å
šã§ãã
ããã³ããšãŠãŒã¶ãŒã®ã³ã³ããã¹ããå®å šã«æž¡ãæ¹æ³
RLSãæ©èœãããã«ã¯ãããŒã¿ããŒã¹ãã誰ãåŒãã§ãããããšãã©ã®ããã³ããããç¥ãå¿ èŠããããŸããRLSããªã·ãŒã¯ã¯ãšãªæã«ããŒã¿ããŒã¹ãèªããå€ãšããæ¯èŒã§ããªãããããã®ã³ã³ããã¹ããã»ãã·ã§ã³ã«æž¡ãä»çµã¿ãå¿ èŠã§ãã
äžè¬çãªãã¿ãŒã³ã¯èªèšŒåŸã«ã»ãã·ã§ã³å€æ°ãèšå®ããããªã·ãŒåŽã§current_setting()ã§èªãæ¹æ³ã§ããã¢ããªã¯ããŒã¯ã³ãæ€èšŒããŠïŒäŸãã°JWTã®çœ²åãšæå¹æéã確èªïŒãå¿
èŠãªãã£ãŒã«ãïŒtenant_idãuser_idãroleïŒã ããããŒã¿ããŒã¹æ¥ç¶ã«æžã蟌ã¿ãŸãã
-- Run once per request (or per transaction)
SELECT set_config('app.tenant_id', '3f2a0c3e-9c7b-4d3f-9c5c-3c5e9c5d1a11', true);
SELECT set_config('app.user_id', '8d9c6b1a-6b6d-4e32-9c0d-2bfe6f6c1111', true);
SELECT set_config('app.role', 'support_agent', true);
-- In a policy
-- tenant_id column is a UUID
USING (tenant_id = current_setting('app.tenant_id', true)::uuid);
第äžåŒæ°ã«trueãæž¡ããšãã®èšå®ãçŸåšã®ãã©ã³ã¶ã¯ã·ã§ã³ããŒã«ã«ã«ãªããŸããæ¥ç¶ããŒãªã³ã°ã䜿ãå ŽåãããŒã«ãããæ¥ç¶ãå¥ã®ãªã¯ãšã¹ãã«åå©çšããããããåã®ãªã¯ãšã¹ãã®ã³ã³ããã¹ããæ®ããªãããã«ããããã«éèŠã§ãã
JWTã¯ã¬ãŒã ããã³ã³ããã¹ããèšå®ããå Žå
APIãJWTã䜿ã£ãŠããå Žåãã¯ã¬ãŒã ã¯ãã®ãŸãŸä¿¡é Œããå ¥åãšããŠæ±ã£ãŠãã ããããŸãããŒã¯ã³ã®çœ²åãšæå¹æéãæ€èšŒãããã®åŸå¿ èŠãªãã£ãŒã«ãã ãïŒtenant_idãuser_idãroleïŒãã»ãã·ã§ã³èšå®ã«ã³ããŒããŸããã¯ã©ã€ã¢ã³ãããããã®å€ãçŽæ¥ããããã¯ãšãªãã©ã¡ãŒã¿ã§éããããã«ããã®ã¯é¿ããŠãã ããã
ã³ã³ããã¹ãããªãã»ç¡å¹ãªå Žåã¯ããã©ã«ãæåŠ
èšå®ãæ¬ ããŠãããšãã¯è¡ãè¿ããªãããã«ããªã·ãŒãèšèšããŠãã ããã
current_setting('app.tenant_id', true)ã䜿ãã°ãèšå®ããªããšNULLãè¿ããŸããé©åãªåã«ãã£ã¹ãïŒäŸãã°::uuidïŒããŠç¡å¹ãªãã©ãŒãããã¯æ©ãã«å€±æãããããã³ãïŒãŠãŒã¶ãŒã³ã³ããã¹ããèšå®ã§ããªãå Žåã¯æšæž¬ããã®ã§ã¯ãªããªã¯ãšã¹ãã倱æãããæ¹ãå®å
šã§ãã
ããã«ãããUIããã€ãã¹ããã¯ãšãªãæ°ãããšã³ããã€ã³ãã远å ããããšãã§ãã¢ã¯ã»ã¹å¶åŸ¡ã®äžè²«æ§ãä¿ãããŸãã
ä¿å®ããããããŒã«ãã¿ãŒã³
RLSããªã·ãŒãèªã¿ãããä¿ã€æãç°¡åãªæ¹æ³ã¯ãã¢ã€ãã³ãã£ãã£ãšæš©éãåé¢ããããšã§ããäžè¬çãªåºç€ã¯usersããŒãã«ãšããŠãŒã¶ãŒãããã³ããšããŒã«ïŒè€æ°ãå¯ïŒã«çŽã¥ããmembershipsããŒãã«ã§ãããããããšããªã·ãŒã¯ãçŸåšã®ãŠãŒã¶ãŒããã®è¡ã«å¯ŸããŠé©åãªã¡ã³ããŒã·ãããæã£ãŠãããïŒããšããåãã«éäžã§ããŸãã
ããŒã«åã¯è·çš®ãããå®éã®è¡åã«çµã³ã€ããŠãããšé·æã¡ããŸããinvoice_viewerãinvoice_approverã®ããã«ãããªã·ãŒãæç¢ºãªåäœã§æžããååã奜ãŸããã§ãã
ä¿å®ããããããŒã«ãã¿ãŒã³ã®äŸïŒ
- Owner-only: è¡ã«
created_by_user_idïŒãŸãã¯owner_user_idïŒãããããã®äžèŽããã§ãã¯ããã - Team-only: è¡ã«
team_idããããããªã·ãŒã§åãããã³ãå ã§ãã®ããŒã ã®ã¡ã³ããŒã§ããããšã確èªããã - Approved-only:
status = 'approved'ã®ãšãã ãèªã¿åããèš±å¯ããæžã蟌ã¿ã¯æ¿èªè ã«éå®ããã - Mixed rules: æåã¯å³æ Œã«ããŠãåŸããå°ããªäŸå€ïŒäŸ: "ãµããŒãã¯é²èЧå¯èœ"ïŒã远å ããã
ã¯ãã¹ããã³ãã®ç®¡çè
ïŒcross-tenant adminsïŒã¯å€ãã®ããŒã ãã€ãŸãããã€ã³ãã§ãããããæé»ã®âã¹ãŒããŒãŠãŒã¶ãŒâã§æ±ãã®ã§ã¯ãªãæç€ºçã«æ±ã£ãŠãã ãããplatform_adminã®ãããªå¥æŠå¿µãäœããããªã·ãŒã§æ
éã«ãã§ãã¯ãããå¯èœãªãã¯ãã¹ããã³ãã®ã¢ã¯ã»ã¹ã¯ããã©ã«ãã§èªã¿åãã®ã¿ãšããæžã蟌ã¿ã«ã¯ããã«é«ãããŒãã«ãèšããã®ãè¯ãã§ãã
ããã¥ã¡ã³ããéèŠã§ããåããªã·ãŒã®äžã«çãã³ã¡ã³ãã§ãæå³ããæžããŠãã ãããSQLã®èª¬æã§ã¯ãªããæ¿èªè ã¯ã¹ããŒã¿ã¹ã倿Žã§ãããé²èЧè ã¯æ¿èªæžã¿ã®è«æ±æžã®ã¿èŠãããšãã§ãããããšãã£ãäžæãããã ãã§ãæ°ã¶æåŸã®ç·šéãå®å šã«ãªããŸãã
AppMasterã®ãããªããŒã³ãŒãããŒã«ã§ããããã®ãã¿ãŒã³ã¯æå¹ã§ããUIãšAPIã¯æ©ãå€ãã£ãŠããã¡ã³ããŒã·ãããšæç¢ºãªããŒã«å®çŸ©ã«åºã¥ãããŒã¿ããŒã¹ã«ãŒã«ã¯å®å®ããŸãã
äŸïŒè«æ±æžãšãµããŒããæã€ã·ã³ãã«ãªSaaS
å°ããªSaaSãæ³åããŠãã ãããè€æ°ã®äŒç€ŸïŒããã³ãïŒã«ãµãŒãã¹ãæäŸããè«æ±æžãšãµããŒããã±ãããæ±ããŸãããŠãŒã¶ãŒã¯ãšãŒãžã§ã³ãããããŒãžã£ãŒããµããŒããªã©ã®ããŒã«ã§ãã
ããŒã¿ã¢ãã«ïŒç°¡ç¥åïŒïŒè«æ±æžãšãã±ããã®åè¡ã«tenant_idãããããã±ããã«ã¯assignee_user_idããããŸããã¢ããªã¯ãã°ã€ã³çŽåŸã«çŸåšã®ããã³ããšãŠãŒã¶ãŒãããŒã¿ããŒã¹ã»ãã·ã§ã³ã«ã»ããããŸãã
RLSãå°å ¥ãããšæ¥åžžçãªãªã¹ã¯ã¯ããå€ãããŸãã
ããã³ãAã®ãŠãŒã¶ãŒãããã³ãBã®è«æ±æžIDãæšæž¬ããŠã¢ã¯ã»ã¹ããããšããŠãïŒãããã¯UIã誀ã£ãŠéä¿¡ããŠãïŒãã¯ãšãªã¯å®è¡ãããŸããããªã·ãŒã«ãããŒãè¡ãè¿ããŸããããªã·ãŒã¯invoice.tenant_id = current_tenant_idãèŠæ±ãããããã¢ã¯ã»ã¹æåŠã®æŒãã¯çºçããŸããã
ããã³ãå ã§ã¯ããŒã«ãããã«ã¢ã¯ã»ã¹ãçµããŸãããããŒãžã£ãŒã¯ããã³ãå ã®å šè«æ±æžãšãã±ãããèŠãããŸãããšãŒãžã§ã³ãã¯èªåã«å²ãåœãŠããããã±ãããèªåã®äžæžãã®ã¿èŠãããããšãã£ãå ·åã§ãããã£ã«ã¿ãä»»æã®APIã§ã¯ç¹ã«ããã§ãã¹ãåºãããã§ãã
ãµããŒãã¯ç¹æ®ã±ãŒã¹ã§ãã顧客察å¿ã®ããã«è«æ±æžãé²èЧããå¿
èŠã¯ãããŸãããamountãbank_accountãtax_idã®ãããªæ©å¯ãã£ãŒã«ãã¯å€æŽã§ããŠã¯ãªããŸãããå®çšçãªãã¿ãŒã³ãšããŠã¯ïŒ
- ãµããŒãããŒã«ã«ã¯è«æ±æžã®
SELECTãèš±å¯ããïŒãã ãããã³ãã¹ã³ãŒãå ïŒã UPDATEã¯ãå®å šãªãçµè·¯ã ãã§èš±å¯ããïŒç·šéå¯èœãªåã ããé²åºãããã¥ãŒã䜿ãããä¿è·ããããã£ãŒã«ããžã®å€æŽãæåŠãã峿 ŒãªæŽæ°ããªã·ãŒãäœãïŒã
ãªãã¡ã¯ã¿ãªã³ã°ã§ããã³ããã£ã«ã¿ãé©çšãå¿ãããšã©ããªãããRLSããªããšã¯ãã¹ããã³ãã®è«æ±æžãæŒããŠããŸãæãããããŸãããRLSãããã°ããŒã¿ããŒã¹ãä»ããã³ãã®è¡ãè¿ããªãããããã°ã¯ç»é¢ãå£ããçšåºŠã§æžã¿ãããŒã¿æŒããã«ã¯ãªããŸããã
AppMasterã§ããããSaaSãæ§ç¯ããå Žåã§ããããŒã¿ããŒã¹åŽã®ã«ãŒã«ã¯å¿ èŠã§ããUIã®ãã§ãã¯ã¯æçã§ãããäœããæãããšãã«å¹ãã®ã¯ããŒã¿ããŒã¹ã®ã«ãŒã«ã§ãã
ãããããã¹ãšåé¿æ¹æ³
RLSã¯åŒ·åã§ãããå°ããªãã¹ã§ãå®å šãã«èŠããŠå®ã¯ãé©ãããæãããšããããŸããåé¡ã¯æ°ããããŒãã«ã®è¿œå ãããŒã«å€æŽããããã¯èª€ã£ãDBãŠãŒã¶ãŒã§ã®ãã¹ããªã©ã§è¡šããŸãã
ãããã倱æã¯æ°ããããŒãã«ã§RLSãæå¹ã«ãå¿ããããšã§ããã³ã¢ããŒãã«ã®ããªã·ãŒã¯äžå¯§ã«äœã£ãŠããŠããåŸãã远å ããnotesãattachmentsãå
šéæŸã§åºè·ããŠããŸãããšããããŸããç¿æ
£ãšããŠãæ°ããããŒãã«ïŒRLSæå¹ïŒæäœ1ã€ã®ããªã·ãŒãã培åºããŠãã ããã
å¥ã®çœ ã¯ã¢ã¯ã·ã§ã³ããšã«ããªã·ãŒãæã£ãŠããªãããšã§ããINSERTãèš±å¯ããŠããã®ã«SELECTããããã¯ããŠãããšãäœæçŽåŸã«ããŒã¿ããæ¶ãããããã«èŠããŸããéã«èªãããäœããªãããšããäžæŽåãåé¡ã§ãããäœãâèŠãããæŽæ°âåãªãŒãã³ããåé€âäžèЧããªã©ã®ãããŒã§èããŠããªã·ãŒãäœããŸãããã
SECURITY DEFINER颿°ã«ã¯æ³šæãå¿
èŠã§ãã颿°ã¯ææè
ã®æš©éã§å®è¡ããããããé©åã«æ±ããªããšRLSãåé¿ããŠããŸãããšããããŸãã䜿ãå Žåã¯å°ããä¿ã¡ãå
¥åãæ€èšŒããåçSQLã¯æ¬åœã«å¿
èŠãªå Žåã ãã«ããŠãã ããã
ãŸãã¢ããªåŽã®ãã£ã«ã¿ãªã³ã°ã«é ŒããããŒã¿ããŒã¹ã®ã¢ã¯ã»ã¹ãéãããŸãŸã«ããã®ã¯é¿ããŠãã ãããAPIã¯æé·ããŠæ°ãããšã³ããã€ã³ãããžã§ããå¢ããŸããããŒã¿ããŒã¹ããŒã«ãå šãŠèªããç¶æ ã ãšããããäœããæŒããŸãã
åé¡ãæ©æã«çºèŠããããã®å®åçãªãã§ãã¯ãªã¹ãïŒ
- æ¬çªã¢ããªã䜿ãã®ãšåãDBããŒã«ã§ãã¹ããè¡ãïŒå人ã®ç®¡çè ãŠãŒã¶ãŒã§ã¯ãªãïŒã
- åããŒãã«ã«å¯ŸããŠåŠå®ãã¹ãã1ã€å ¥ããïŒå¥ããã³ãã®ãŠãŒã¶ãŒã¯ãŒãè¡ããèŠããªãããšã確èªããã
- æåŸ
ããã¢ã¯ã·ã§ã³ïŒ
SELECTãINSERTãUPDATEãDELETEïŒãåããŒãã«ã§ãµããŒããããŠããã確èªããã SECURITY DEFINERã®äœ¿çšãèŠçŽãããªãå¿ èŠããããã¥ã¡ã³ãåããã- ãã€ã°ã¬ãŒã·ã§ã³ãšã³ãŒãã¬ãã¥ãŒã®ãã§ãã¯ãªã¹ãã«ãRLSæå¹ïŒããå«ããã
äŸïŒãµããŒããšãŒãžã§ã³ããã€ã³ãã€ã¹ã®ã¡ã¢ãäœæãããèªã¿è¿ããªãå Žåãå€ãã¯INSERTããªã·ãŒã¯ããã察å¿ããSELECTããªã·ãŒããªãããããã¯ãã®ã»ãã·ã§ã³ã§ããã³ãã³ã³ããã¹ããèšå®ãããŠããªãããšãåå ã§ãã
RLSèšå®ãæ€èšŒããããã®ç°¡åãã§ãã¯ãªã¹ã
RLSã¯ã¬ãã¥ãŒäžã¯æ£ããèŠããŠããå®éã®å©çšã§å€±æããããšããããŸããæ€èšŒã¯ããªã·ãŒãèªãããšããããçŸå®çãªã¢ã«ãŠã³ããšã¯ãšãªã§å£ããã詊ãããšãéèŠã§ããã¢ããªãäœ¿ãæ¹æ³ãã®ãŸãŸã§ãã¹ãããŠãã ããã
ãŸãå°æ°ã®ãã¹ãIDãçšæããŸããå°ãªããšã2ã€ã®ããã³ãïŒTenant A, Tenant BïŒãçšæããããããã«éåžžãŠãŒã¶ãŒãšç®¡çè ïŒãããŒãžã£ãŒããŒã«ãäœããŸãããµããŒããèªã¿åãå°çšããŒã«ããããªãããã远å ããŸãã
ãã®åŸã次ã®ãããªãã§ãã¯ãç¹°ãè¿ãè¡ããŸãïŒ
- åããŒã«ã§åºæ¬æäœãå®è¡ããïŒäžèЧååŸãåäžè¡ååŸïŒidæå®ïŒãæ¿å ¥ãæŽæ°ãåé€ãåæäœã§èš±å¯ãããã±ãŒã¹ãšæåŠãããã±ãŒã¹ã®äž¡æ¹ã詊ãã
- ããã³ãå¢çã蚌æããïŒTenant AããTenant Bã®ããŒã¿ãèªã¿æžãããããšããŠãŒãè¡ãè¿ããããããã¯æš©éãšã©ãŒã«ãªãã確èªããã
- çµåã«ããæŒæŽ©ããã¹ãããïŒä¿è·ãããããŒãã«ãä»ã®ããŒãã«ïŒåç §ããŒãã«ãå«ãïŒãšçµåããŠãå¥ããã³ãã®é¢é£è¡ãåŒã蟌ãŸããªãã確èªããã
- ã³ã³ããã¹ãããªãã»ééã£ãŠããå Žåã¯æåŠããããïŒãªã¯ãšã¹ãããšã«èšå®ããã³ã³ããã¹ããã¯ãªã¢ããŠè©Šãã"ã³ã³ããã¹ãç¡ã"ã¯ã¯ããŒãºãã«å€±æãã¹ãã§ããç¡å¹ãªããã³ãIDã詊ãã
- åºæ¬çãªããã©ãŒãã³ã¹ã確èªããïŒã¯ãšãªãã©ã³ãèŠãŠãã€ã³ããã¯ã¹ãããã³ããã£ã«ã¿ãã¿ãŒã³ïŒéåžžã¯
tenant_idïŒæ€çŽ¢ã»ãœãŒãé ç®ïŒããµããŒãããŠãããã確èªããã
ãã¹ãã§é©ããããã°ããªã·ãŒãŸãã¯ã³ã³ããã¹ãèšå®ãçŽããŠãã ãããUIãAPIã§ããããåœãŠãŠããŒã¿ããŒã¹ã«ãŒã«ã"ãªããšãç¶æ"ããããšããã®ã¯é¿ããŠãã ããã
次ã®ã¹ãããïŒå®å šã«å°å ¥ããŠäžè²«æ§ãä¿ã€
PostgreSQLã®RLSã¯å®å šã·ã¹ãã ã®ããã«æ±ã£ãŠãã ããïŒæ éã«å°å ¥ããé »ç¹ã«æ€èšŒããããŒã ãåŸããã»ã©ã·ã³ãã«ãªã«ãŒã«ã«ä¿ã€ããšã
å°ããå§ããŸããããæŒããèŽåœçãªããŒãã«ïŒæ±ºæžãè«æ±æžãå人æ å ±ã顧客ã¡ãã»ãŒãžãªã©ïŒããRLSãæå¹ã«ããããã§åå©äœéšãç©ãæ¹ããå šé¢å±éããŠèª°ãçè§£ããŠããªãç¶æ ããè¯ãã§ãã
å®çšçãªå°å ¥é ã®äŸïŒ
- ã³ã¢ãªâææãããâããŒãã«ïŒè¡ãæç¢ºã«ããã³ãã«å±ããïŒ
- å人æ å ±ãå«ãããŒãã«ïŒPIIïŒ
- ããã³ãã§ãã£ã«ã¿ãããå ±æããŒãã«ïŒã¬ããŒããåæïŒ
- çµåããŒãã«ããšããžã±ãŒã¹ïŒå€å¯Ÿå€é¢ä¿ïŒ
- åºæ¬ãå®å®ããããã®ä»ãã¹ãŠ
ãã¹ãã¯å¿ é ã«ããŠãã ãããèªåãã¹ãã¯ç°ãªãããã³ããããŒã«ã§åãã¯ãšãªãå®è¡ããŠçµæã確èªããã¹ãã§ãã"èš±å¯ããã"ãš"æåŠããã"ã®äž¡æ¹ã®ãã§ãã¯ãå«ããŠãã ãããæãé«ã³ã¹ããªã®ã¯éãã«éå°èš±å¯ããç¶ãããã°ã§ãã
ãªã¯ãšã¹ããããŒå ã§ã»ãã·ã§ã³ã³ã³ããã¹ããèšå®ããäžç®æãæç¢ºã«ããŠãã ãããtenant idãuser idãroleã¯äžåºŠãæ©ã段éã§èšå®ããåŸã§æšæž¬ããªãããšããã©ã³ã¶ã¯ã·ã§ã³ã®éäžã§ã³ã³ããã¹ããèšå®ãããšãå€ãå€ãæ¬ ããå€ã§ã¯ãšãªãèµ°ãããšã«ãªããŸãã
AppMasterã§æ§ç¯ããå Žåã¯ãçæãããããã¯ãšã³ãAPIãšPostgreSQLããªã·ãŒã®éã®äžè²«æ§ãèšç»ããŠãã ããããã¹ãŠã®ãšã³ããã€ã³ãã§åãã»ãã·ã§ã³å€æ°ã䜿ããªã©ãã³ã³ããã¹ãã®æž¡ãæ¹ãæšæºåãããšããªã·ãŒãã©ãã§ãåãããã«æ¯ãèããŸããããAppMasterã䜿ã£ãŠããŠappmaster.ioãšãã衚èšãåºãå Žåã§ããRLSã¯ããã³ãåé¢ã®æçµçãªæ ¹æ ãšããŠæ±ã£ãŠãã ããã
æåŸã«ã倱æãç£èŠããŠãã ãããèªå¯ã®å€±æã¯æçšãªã·ã°ãã«ã§ããå°å ¥çŽåŸã¯æåŠãã°ã远跡ãããããæ¬åœã®æ»æãªã®ãã¯ã©ã€ã¢ã³ãåŽã®äžå ·åãªã®ãããããã¯ããªã·ãŒãå³ããããã®ãã調æ»ããŸãããã
RLSãå¥åº·ã«ä¿ã€ããã®çãç¿æ £ãªã¹ãïŒ
- ããã©ã«ãæåŠã®èãæ¹ãæã¡ãäŸå€ã¯æå³çã«è¿œå ãã
- æç¢ºãªããªã·ãŒåïŒããŒãã« + ã¢ã¯ã·ã§ã³ + 察象ïŒ
- ããªã·ãŒå€æŽã¯ã³ãŒã倿Žãšåæ§ã«ã¬ãã¥ãŒãã
- å°å ¥åæã¯æåŠãã°ãèšé²ã»ç¢ºèªãã
- RLSãæå¹ã«ããæ°ããããŒãã«ããšã«å°ããªãã¹ãã»ããã远å ãã


