[{"data":1,"prerenderedAt":764},["ShallowReactive",2],{"page-\u002Fsupabase\u002Frow-level-security":3,"pieces-supabase":721,"autour-\u002Fsupabase\u002Frow-level-security":746,"prerequis-\u002Fsupabase\u002Frow-level-security":751,"plan-supabase":753},{"id":4,"title":5,"body":6,"description":695,"extension":696,"ligne":697,"meta":698,"motsCles":699,"navigation":379,"niveau":703,"path":704,"prerequis":705,"ressources":707,"seo":715,"sources":716,"stem":719,"__hash__":720},"docs\u002Fsupabase\u002F05.row-level-security.md","Row Level Security",{"type":7,"value":8,"toc":684},"minimark",[9,14,27,31,35,43,49,55,59,62,102,113,116,120,132,181,199,203,206,244,255,303,306,310,313,510,513,572,583,589,593,610,613,617,624,627,633,637,680],[10,11,13],"h2",{"id":12},"ce-que-tu-vas-apprendre","Ce que tu vas apprendre",[15,16,17,21,24],"ul",{},[18,19,20],"li",{},"Pourquoi une table exposée sans RLS est ouverte à tout le monde.",[18,22,23],{},"Activer la RLS et écrire une politique par opération.",[18,25,26],{},"Lier chaque recette à son auteur pour que lui seul puisse la modifier.",[10,28,30],{"id":29},"le-danger","Le danger",[32,33,34],"p",{},"La clé publishable est dans ton site. N'importe qui peut la copier et appeler l'API de ton projet avec elle, sans passer par ton site.",[32,36,37,38,42],{},"Sans protection, cette personne peut lire, modifier ou vider ta table ",[39,40,41],"code",{},"recettes",". La seule barrière est la base elle-même.",[32,44,45,48],{},[46,47,5],"strong",{}," (RLS) est cette barrière. Quand elle est activée, une ligne n'est accessible que si une règle l'autorise.",[50,51,52],"warning",{},[32,53,54],{},"Active la RLS sur chaque table exposée. Une table sans RLS, avec la clé publique, est une porte ouverte.",[10,56,58],{"id":57},"activer-la-rls","Activer la RLS",[32,60,61],{},"Le Table Editor du tableau de bord l'active à la création d'une table. En SQL, c'est à toi :",[63,64,69],"pre",{"className":65,"code":66,"language":67,"meta":68,"style":68},"language-sql shiki shiki-themes github-light-default github-dark-default github-dark-default","alter table recettes enable row level security;\n","sql","",[39,70,71],{"__ignoreMap":68},[72,73,76,80,83,87,90,93,96,99],"span",{"class":74,"line":75},"line",1,[72,77,79],{"class":78},"sOnC1","alter",[72,81,82],{"class":78}," table",[72,84,86],{"class":85},"scfrF"," recettes ",[72,88,89],{"class":78},"enable",[72,91,92],{"class":78}," row",[72,94,95],{"class":78}," level",[72,97,98],{"class":78}," security",[72,100,101],{"class":85},";\n",[32,103,104,105,108,109,112],{},"Une fois activée et sans aucune politique, la table refuse tout. Un ",[39,106,107],{},"select"," renvoie une liste vide, un ",[39,110,111],{},"insert"," est rejeté.",[32,114,115],{},"C'est le bon point de départ : tu ouvres ensuite uniquement ce qui doit l'être.",[10,117,119],{"id":118},"une-politique","Une politique",[32,121,122,123,126,127,131],{},"Une ",[46,124,125],{},"politique"," (",[128,129,130],"em",{},"policy",") est une règle attachée à une table. Elle précise l'opération, le rôle concerné et la condition. Voici la lecture ouverte à tous les visiteurs :",[63,133,135],{"className":65,"code":134,"language":67,"meta":68,"style":68},"create policy \"Tout le monde peut lire les recettes\"\non recettes for select\nto anon, authenticated\nusing ( true );\n",[39,136,137,149,163,172],{"__ignoreMap":68},[72,138,139,142,145],{"class":74,"line":75},[72,140,141],{"class":78},"create",[72,143,144],{"class":78}," policy",[72,146,148],{"class":147},"shWOn"," \"Tout le monde peut lire les recettes\"\n",[72,150,152,155,157,160],{"class":74,"line":151},2,[72,153,154],{"class":78},"on",[72,156,86],{"class":85},[72,158,159],{"class":78},"for",[72,161,162],{"class":78}," select\n",[72,164,166,169],{"class":74,"line":165},3,[72,167,168],{"class":78},"to",[72,170,171],{"class":85}," anon, authenticated\n",[72,173,175,178],{"class":74,"line":174},4,[72,176,177],{"class":78},"using",[72,179,180],{"class":85}," ( true );\n",[32,182,183,184,187,188,191,192,194,195,198],{},"Les rôles sont ",[39,185,186],{},"anon"," (visiteur sans compte) et ",[39,189,190],{},"authenticated"," (connecté). La condition ",[39,193,177],{}," filtre les lignes déjà présentes. Ici ",[39,196,197],{},"true"," laisse tout passer.",[10,200,202],{"id":201},"lier-une-recette-à-son-auteur","Lier une recette à son auteur",[32,204,205],{},"Pour qu'un utilisateur ne touche qu'à ses recettes, la table doit savoir à qui appartient chaque ligne :",[63,207,209],{"className":65,"code":208,"language":67,"meta":68,"style":68},"alter table recettes\n  add column user_id uuid references auth.users (id);\n",[39,210,211,220],{"__ignoreMap":68},[72,212,213,215,217],{"class":74,"line":75},[72,214,79],{"class":78},[72,216,82],{"class":78},[72,218,219],{"class":85}," recettes\n",[72,221,222,225,228,231,235,238,241],{"class":74,"line":151},[72,223,224],{"class":78},"  add",[72,226,227],{"class":85}," column user_id uuid ",[72,229,230],{"class":78},"references",[72,232,234],{"class":233},"s5iOY"," auth",[72,236,237],{"class":85},".",[72,239,240],{"class":233},"users",[72,242,243],{"class":85}," (id);\n",[32,245,246,247,250,251,254],{},"La colonne ",[39,248,249],{},"user_id"," pointe vers la table des comptes. Le client la remplit avec l'identifiant de la personne connectée, lu dans ",[39,252,253],{},"user.sub"," (cours 04) :",[63,256,260],{"className":257,"code":258,"language":259,"meta":68,"style":68},"language-ts shiki shiki-themes github-light-default github-dark-default github-dark-default","await supabase\n  .from('recettes')\n  .insert({ titre: 'Crêpes', user_id: user.value?.sub })\n","ts",[39,261,262,270,288],{"__ignoreMap":68},[72,263,264,267],{"class":74,"line":75},[72,265,266],{"class":78},"await",[72,268,269],{"class":85}," supabase\n",[72,271,272,275,279,282,285],{"class":74,"line":151},[72,273,274],{"class":85},"  .",[72,276,278],{"class":277},"syCW7","from",[72,280,281],{"class":85},"(",[72,283,284],{"class":147},"'recettes'",[72,286,287],{"class":85},")\n",[72,289,290,292,294,297,300],{"class":74,"line":165},[72,291,274],{"class":85},[72,293,111],{"class":277},[72,295,296],{"class":85},"({ titre: ",[72,298,299],{"class":147},"'Crêpes'",[72,301,302],{"class":85},", user_id: user.value?.sub })\n",[32,304,305],{},"La politique d'insertion ci-dessous vérifie que cette valeur est bien la tienne : tricher est impossible.",[10,307,309],{"id":308},"écrire-modifier-supprimer","Écrire, modifier, supprimer",[32,311,312],{},"Une politique par opération, réservée aux connectés :",[63,314,316],{"className":65,"code":315,"language":67,"meta":68,"style":68},"create policy \"Ajouter ses recettes\"\non recettes for insert\nto authenticated\nwith check ( (select auth.uid()) = user_id );\n\ncreate policy \"Modifier ses recettes\"\non recettes for update\nto authenticated\nusing ( (select auth.uid()) = user_id )\nwith check ( (select auth.uid()) = user_id );\n\ncreate policy \"Supprimer ses recettes\"\non recettes for delete\nto authenticated\nusing ( (select auth.uid()) = user_id );\n",[39,317,318,327,338,345,374,381,391,403,410,432,455,460,470,482,489],{"__ignoreMap":68},[72,319,320,322,324],{"class":74,"line":75},[72,321,141],{"class":78},[72,323,144],{"class":78},[72,325,326],{"class":147}," \"Ajouter ses recettes\"\n",[72,328,329,331,333,335],{"class":74,"line":151},[72,330,154],{"class":78},[72,332,86],{"class":85},[72,334,159],{"class":78},[72,336,337],{"class":78}," insert\n",[72,339,340,342],{"class":74,"line":165},[72,341,168],{"class":78},[72,343,344],{"class":85}," authenticated\n",[72,346,347,350,353,356,358,360,362,365,368,371],{"class":74,"line":174},[72,348,349],{"class":78},"with",[72,351,352],{"class":78}," check",[72,354,355],{"class":85}," ( (",[72,357,107],{"class":78},[72,359,234],{"class":233},[72,361,237],{"class":85},[72,363,364],{"class":233},"uid",[72,366,367],{"class":85},"()) ",[72,369,370],{"class":78},"=",[72,372,373],{"class":85}," user_id );\n",[72,375,377],{"class":74,"line":376},5,[72,378,380],{"emptyLinePlaceholder":379},true,"\n",[72,382,384,386,388],{"class":74,"line":383},6,[72,385,141],{"class":78},[72,387,144],{"class":78},[72,389,390],{"class":147}," \"Modifier ses recettes\"\n",[72,392,394,396,398,400],{"class":74,"line":393},7,[72,395,154],{"class":78},[72,397,86],{"class":85},[72,399,159],{"class":78},[72,401,402],{"class":78}," update\n",[72,404,406,408],{"class":74,"line":405},8,[72,407,168],{"class":78},[72,409,344],{"class":85},[72,411,413,415,417,419,421,423,425,427,429],{"class":74,"line":412},9,[72,414,177],{"class":78},[72,416,355],{"class":85},[72,418,107],{"class":78},[72,420,234],{"class":233},[72,422,237],{"class":85},[72,424,364],{"class":233},[72,426,367],{"class":85},[72,428,370],{"class":78},[72,430,431],{"class":85}," user_id )\n",[72,433,435,437,439,441,443,445,447,449,451,453],{"class":74,"line":434},10,[72,436,349],{"class":78},[72,438,352],{"class":78},[72,440,355],{"class":85},[72,442,107],{"class":78},[72,444,234],{"class":233},[72,446,237],{"class":85},[72,448,364],{"class":233},[72,450,367],{"class":85},[72,452,370],{"class":78},[72,454,373],{"class":85},[72,456,458],{"class":74,"line":457},11,[72,459,380],{"emptyLinePlaceholder":379},[72,461,463,465,467],{"class":74,"line":462},12,[72,464,141],{"class":78},[72,466,144],{"class":78},[72,468,469],{"class":147}," \"Supprimer ses recettes\"\n",[72,471,473,475,477,479],{"class":74,"line":472},13,[72,474,154],{"class":78},[72,476,86],{"class":85},[72,478,159],{"class":78},[72,480,481],{"class":78}," delete\n",[72,483,485,487],{"class":74,"line":484},14,[72,486,168],{"class":78},[72,488,344],{"class":85},[72,490,492,494,496,498,500,502,504,506,508],{"class":74,"line":491},15,[72,493,177],{"class":78},[72,495,355],{"class":85},[72,497,107],{"class":78},[72,499,234],{"class":233},[72,501,237],{"class":85},[72,503,364],{"class":233},[72,505,367],{"class":85},[72,507,370],{"class":78},[72,509,373],{"class":85},[32,511,512],{},"Deux mots à distinguer :",[514,515,516,532],"table",{},[517,518,519],"thead",{},[520,521,522,526,529],"tr",{},[523,524,525],"th",{},"Clause",[523,527,528],{},"Teste",[523,530,531],{},"S'applique à",[533,534,535,556],"tbody",{},[520,536,537,542,545],{},[538,539,540],"td",{},[39,541,177],{},[538,543,544],{},"Les lignes qui existent déjà",[538,546,547,549,550,549,553],{},[39,548,107],{},", ",[39,551,552],{},"update",[39,554,555],{},"delete",[520,557,558,563,566],{},[538,559,560],{},[39,561,562],{},"with check",[538,564,565],{},"Les lignes écrites ou modifiées",[538,567,568,549,570],{},[39,569,111],{},[39,571,552],{},[32,573,574,575,578,579,582],{},"Écrire ",[39,576,577],{},"(select auth.uid())"," plutôt que ",[39,580,581],{},"auth.uid()"," seul est une recommandation de la documentation : Postgres calcule la valeur une fois par requête.",[32,584,585,586,588],{},"Un ",[39,587,552],{}," a aussi besoin d'une politique de lecture qui laisse voir la ligne. Ici la politique de lecture ouverte à tous la fournit.",[10,590,592],{"id":591},"tester","Tester",[594,595,596,602,607],"ol",{},[18,597,598,599,601],{},"Ouvre le site sans être connecté : tu vois les recettes, mais un ",[39,600,111],{}," échoue.",[18,603,604,605,237],{},"Connecte-toi avec un compte A et ajoute une recette : elle apparaît avec ton ",[39,606,249],{},[18,608,609],{},"Connecte-toi avec un compte B et tente de supprimer la recette de A : la suppression ne touche aucune ligne.",[32,611,612],{},"Le troisième test est le plus important. Il prouve que la règle tient même si quelqu'un contourne ton interface.",[10,614,616],{"id":615},"la-clé-secrète","La clé secrète",[32,618,619,620,623],{},"La clé secrète appartient au rôle ",[39,621,622],{},"service_role",", qui contourne la RLS. Aucune politique ne la retient.",[32,625,626],{},"Elle ne doit donc jamais figurer dans une page, un composant ou un fichier du dépôt. Supabase la refuse d'ailleurs si une requête vient d'un navigateur.",[628,629,630],"caution",{},[32,631,632],{},"Une clé secrète publiée sur GitHub donne un accès total à tes données. Révoque-la aussitôt dans Settings, API Keys.",[10,634,636],{"id":635},"à-retenir","À retenir",[15,638,639,642,648,651,663,671,677],{},[18,640,641],{},"La clé publishable est publique : la RLS est ce qui protège réellement les données.",[18,643,644,645,237],{},"Active la RLS sur chaque table exposée, avec ",[39,646,647],{},"alter table ... enable row level security",[18,649,650],{},"Sans politique, une table avec RLS refuse tout.",[18,652,653,654,549,656,549,658,549,660,662],{},"Écris une politique par opération (",[39,655,107],{},[39,657,111],{},[39,659,552],{},[39,661,555],{},") et limite-la au rôle utile.",[18,664,665,667,668,670],{},[39,666,177],{}," teste les lignes existantes, ",[39,669,562],{}," les lignes écrites.",[18,672,673,674,676],{},"Compare toujours à ",[39,675,581],{}," pour que chacun ne touche qu'à ses lignes.",[18,678,679],{},"La clé secrète contourne la RLS : serveur uniquement, jamais commitée.",[681,682,683],"style",{},"html pre.shiki code .sOnC1, html code.shiki .sOnC1{--shiki-light:#CF222E;--shiki-default:#FF7B72;--shiki-dark:#FF7B72}html pre.shiki code .scfrF, html code.shiki .scfrF{--shiki-light:#1F2328;--shiki-default:#E6EDF3;--shiki-dark:#E6EDF3}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .shWOn, html code.shiki .shWOn{--shiki-light:#0A3069;--shiki-default:#A5D6FF;--shiki-dark:#A5D6FF}html pre.shiki code .s5iOY, html code.shiki .s5iOY{--shiki-light:#0550AE;--shiki-default:#79C0FF;--shiki-dark:#79C0FF}html pre.shiki code .syCW7, html code.shiki .syCW7{--shiki-light:#8250DF;--shiki-default:#D2A8FF;--shiki-dark:#D2A8FF}",{"title":68,"searchDepth":151,"depth":165,"links":685},[686,687,688,689,690,691,692,693,694],{"id":12,"depth":151,"text":13},{"id":29,"depth":151,"text":30},{"id":57,"depth":151,"text":58},{"id":118,"depth":151,"text":119},{"id":201,"depth":151,"text":202},{"id":308,"depth":151,"text":309},{"id":591,"depth":151,"text":592},{"id":615,"depth":151,"text":616},{"id":635,"depth":151,"text":636},"Activer la RLS sur une table, écrire des politiques de lecture et d'écriture et comprendre pourquoi elle est indispensable avec une clé publique.","md","supabase",{},[5,700,125,701,581,177,562,702],"RLS","create policy","rôle authenticated","intermediaire","\u002Fsupabase\u002Frow-level-security",[706],"\u002Fsupabase\u002Fauthentification",[708,712],{"nom":709,"url":710,"type":711,"anglais":379},"Supabase, Row Level Security (en anglais)","https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fdatabase\u002Fpostgres\u002Frow-level-security","doc",{"nom":713,"url":714,"type":711,"anglais":379},"Supabase, clés d'API (en anglais)","https:\u002F\u002Fsupabase.com\u002Fdocs\u002Fguides\u002Fapi\u002Fapi-keys",{"title":5,"description":695},[717,718],{"nom":709,"url":710},{"nom":713,"url":714},"supabase\u002F05.row-level-security","XF3pcPeft_4S4TXD-p2b1sj7uFXYw-H2DustMebYjQk",[722,726,730,734,737,738,742],{"path":723,"title":724,"stem":725},"\u002Fsupabase\u002Fdecouvrir-supabase","Découvrir Supabase","supabase\u002F01.decouvrir-supabase",{"path":727,"title":728,"stem":729},"\u002Fsupabase\u002Fbrancher-a-nuxt","Brancher Supabase à Nuxt","supabase\u002F02.brancher-a-nuxt",{"path":731,"title":732,"stem":733},"\u002Fsupabase\u002Flire-et-ecrire","Lire et écrire des données","supabase\u002F03.lire-et-ecrire",{"path":706,"title":735,"stem":736},"Authentification","supabase\u002F04.authentification",{"path":704,"title":5,"stem":719},{"path":739,"title":740,"stem":741},"\u002Fsupabase\u002Fstockage-et-temps-reel","Stockage et temps réel","supabase\u002F06.stockage-et-temps-reel",{"path":743,"title":744,"stem":745},"\u002Fsupabase","Supabase","supabase\u002Findex",[747,749],{"title":735,"path":706,"stem":736,"description":748,"children":-1},"Inscrire et connecter des utilisateurs avec e-mail et mot de passe, lire la session et protéger une page.",{"title":740,"path":739,"stem":741,"description":750,"children":-1},"Envoyer des photos de recettes dans le stockage Supabase avec des règles d'accès, et recevoir les changements de la base en direct.",[752],{"path":706,"title":735,"ligne":697},[754,756,757,758,759,760,762],{"path":723,"title":724,"niveau":755,"stem":725},"debutant",{"path":727,"title":728,"niveau":755,"stem":729},{"path":731,"title":732,"niveau":755,"stem":733},{"path":706,"title":735,"niveau":703,"stem":736},{"path":704,"title":5,"niveau":703,"stem":719},{"path":739,"title":740,"niveau":761,"stem":741},"avance",{"path":743,"title":744,"niveau":763,"stem":745},"memo",1794583461036]