|
| 1 | +--- |
| 2 | +meta: |
| 3 | + title: How to use Row-Level Security with PostgREST for Serverless SQL Databases |
| 4 | + description: This page provides the steps to use Row-Level Security with PostGREST for Serverless SQL Databases |
| 5 | +content: |
| 6 | + h1: How to use Row-Level Security with PostgREST for Serverless SQL Databases |
| 7 | + paragraph: This page provides the steps to use Row-Level Security with PostGREST for Serverless SQL Databases |
| 8 | +tags: sql-databases serverless database row-level-security postgresql postgrest |
| 9 | +dates: |
| 10 | + validation: 2024-09-24 |
| 11 | + posted: 2024-09-24 |
| 12 | +categories: |
| 13 | + - serverless |
| 14 | +--- |
| 15 | + |
| 16 | +PostgREST's built-in Row Level Security based on users JWT relies either on [role impersonation](https://docs.postgrest.org/en/v12/references/auth.html#user-impersonation) or [transaction-scoped settings](https://docs.postgrest.org/en/v12/references/transactions.html#tx-settings). |
| 17 | + |
| 18 | +Due to connection pooling, Serverless SQL Databases currently only support transaction-scoped settings and requires using a single PostgreSQL role for all queries (the internal `role_readwrite` in PostgreSQL). |
| 19 | + |
| 20 | +<Macro id="requirements" /> |
| 21 | + |
| 22 | +<Macro id="requirements" /> |
| 23 | + |
| 24 | +- A Scaleway account logged into the [console](https://console.scaleway.com) |
| 25 | +- [Owner](/identity-and-access-management/iam/concepts/#owner) status or [IAM permissions](/identity-and-access-management/iam/concepts/#permission) allowing you to perform actions in the intended Organization |
| 26 | +- [Created a Serverless SQL Database](/serverless/sql-databases/how-to/create-a-database/) |
| 27 | + |
| 28 | +## How to add sample data and enable PostgreSQL Row Level Security |
| 29 | + |
| 30 | +1. [Connect to your Serverless SQL Database](/serverless/sql-databases/quickstart/#how-to-connect-to-a-database) with a PostgreSQL client such as `psql`: |
| 31 | + ```bash |
| 32 | + psql "postgres://[user-or-application-id]:[api-secret-key]@[database-hostname]:5432/[database-name]?sslmode=require" |
| 33 | + ``` |
| 34 | + |
| 35 | +2. Add sample data to the database using the following command: |
| 36 | + ```sql |
| 37 | + CREATE TABLE pets (name varchar, keeper varchar, id int); |
| 38 | + INSERT INTO pets VALUES ('Stuart','role_admin',1),('Nemo','role_admin',2),('Alfie','role_readwrite',3),('Peanut','role_readwrite',4); |
| 39 | + ``` |
| 40 | + |
| 41 | +3. Run the command below to enable **Row Level Security**: |
| 42 | + ```sql |
| 43 | + ALTER TABLE pets ENABLE row level security; |
| 44 | + ``` |
| 45 | + |
| 46 | +4. Run the command below to create a PostgreSQL policy so that users or applications connecting with `role_readwrite` can access a `pet` row only if its `keeper` column value is `role_readwrite`: |
| 47 | + ```sql |
| 48 | + CREATE POLICY pets_keeper ON pets TO role_readwrite USING (keeper = current_user); |
| 49 | + ``` |
| 50 | + |
| 51 | +5. (Optional) Run the command below to check that you can see all the data with your current connection: |
| 52 | + ```sql |
| 53 | + SELECT * FROM pets; |
| 54 | + ``` |
| 55 | + All the data in the database displays, as you are connected with `role_admin`. |
| 56 | + |
| 57 | + <Message type="tip"> |
| 58 | + You can verify the current role you are connected with using the following command: |
| 59 | + ```sql |
| 60 | + SELECT current_user; |
| 61 | + ``` |
| 62 | + </Message> |
| 63 | + |
| 64 | +## How to use Row Level Security with PostgREST |
| 65 | + |
| 66 | +1. Install PostgREST by following the [official documentation](https://docs.postgrest.org/en/v12/tutorials/tut0.html#step-1-install-postgresql). |
| 67 | + |
| 68 | +2. Create a `tutorial.conf` file with the following content: |
| 69 | + |
| 70 | + ```json |
| 71 | + db-uri = "postgres://[user-or-application-id]:[api-secret-key]@[database-hostname]:5432/[database-name]?sslmode=require" |
| 72 | + db-schemas = "[your database schema]" |
| 73 | + jwt-secret = "[your jwt secret]" |
| 74 | + ``` |
| 75 | + |
| 76 | + Where: |
| 77 | + - `db-uri` must use credentials with an [application](/identity-and-access-management/iam/how-to/create-application/) having **ServerlessSQLDatabaseDataReadWrite** permissions (not **ServerlessSQLDatabaseReadWrite** or **ServerlessSQLDatabaseFullAccess**) |
| 78 | + - `db-schemas` is your database schema. Use `public` as a default value. |
| 79 | + - `jwt-secret` is a token generated using the following command: |
| 80 | + ```sh |
| 81 | + openssl rand -base64 32 |
| 82 | + ``` |
| 83 | + |
| 84 | +3. In a terminal, access the folder containing the `tutorial.conf` file, and run the command below to start a local PostgREST instance: |
| 85 | + |
| 86 | + ```bash |
| 87 | + postgrest tutorial.conf |
| 88 | + ``` |
| 89 | + |
| 90 | + <Message type="tip"> |
| 91 | + You can check that you can query your database by [generating a JWT](https://docs.postgrest.org/en/v12/tutorials/tut1.html#step-3-sign-a-token) with `{"role": "role_readwrite"}` as the payload data, then running the command below, where `$TOKEN` is your generated JWT: |
| 92 | + ```bash |
| 93 | + curl http://localhost:3000/pets \ |
| 94 | + -H "Authorization: Bearer $TOKEN" |
| 95 | + ``` |
| 96 | + A list of pets displays. |
| 97 | + </Message> |
| 98 | + |
| 99 | +4. Connect to your Serverless SQL Database with **ServerlessSQLDatabaseFullAccess** permissions, and run the following command to delete the `pets_keeper` policy previously applied to the `pets` table: |
| 100 | + ```sql |
| 101 | + DROP POLICY pets_keeper ON pets; |
| 102 | + ``` |
| 103 | + |
| 104 | +5. Run the command below to create a new policy on the `pets` table: |
| 105 | + ```sql |
| 106 | + CREATE POLICY pets_keeper ON pets TO role_readwrite |
| 107 | + USING (keeper = current_setting('request.jwt.claims', true)::json->>'user_type'); |
| 108 | + ``` |
| 109 | + This policy uses `current_setting` instead of `current_user`, and thus checks for additional fields contained by the JWT, and not only the `role` field. |
| 110 | + |
| 111 | +6. [Generate a JWT](https://docs.postgrest.org/en/v12/tutorials/tut1.html#step-3-sign-a-token) with the following payload data: |
| 112 | + ```json |
| 113 | + { |
| 114 | + "role": "role_readwrite", |
| 115 | + "user_type": "role_readwrite" |
| 116 | + } |
| 117 | + ``` |
| 118 | + <Message type="tip"> |
| 119 | + Here, the `user_type` value from the JWT will be checked against the `keeper` column value in your database to authorize access. You can replace `"user_type": "role_readwrite"` with any alternative field name or value depending on your use case. However, you must keep `"role": "role_readwrite"` for any users you want to authenticate through PostgREST, because other roles (such as `role_admin`) have too many permissions and will be able to see any data. |
| 120 | + </Message> |
| 121 | + |
| 122 | +7. Run the command below to query your database using the JWT you just created through PostgREST: |
| 123 | + ```bash |
| 124 | + curl http://localhost:3000/pets \ |
| 125 | + -H "Authorization: Bearer $TOKEN" |
| 126 | + ``` |
| 127 | + A list of pets with a `role_readwrite` value for `keeper` displays. |
| 128 | + |
| 129 | + Your new application can now only access a specific subset of rows based on its permissions using transaction-scoped settings. |
| 130 | + |
| 131 | + <Message type="tip"> |
| 132 | + You can change your JWT payload data with `"user_type": "role_admin"` and see that only another set of rows will be displayed. |
| 133 | + |
| 134 | + To go further, try adding fields or values to filter, and edit your policy to filter and give your policy a more complex set of rules. Refer to the [official PostgREST](https://docs.postgrest.org/en/v12/explanations/db_authz.html) documentation for more information. |
| 135 | + </Message> |
0 commit comments