Skip to content

Commit

Permalink
Fixed security RLS helper functions
Browse files Browse the repository at this point in the history
  • Loading branch information
riccardocucia committed Nov 5, 2024
1 parent babbcea commit 852fc07
Show file tree
Hide file tree
Showing 25 changed files with 463 additions and 353 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@

* Added Rooms list pagination and searchable

### Fixed

* Security fix on RLS helper functions

## [1.2.0] - 2024-10-31
#### [@rickypid](https://github.com/rickypid)

Expand Down
28 changes: 16 additions & 12 deletions doc/docs/guides/supabase-security-rls.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ Security rules make use of some helper functions:
RETURNS boolean
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
VOLATILE NOT LEAKPROOF SECURITY INVOKER
SET search_path = ''
AS $BODY$
BEGIN
return auth.uid() IS NOT NULL;
return auth.uid() IS NOT NULL;
end;
$BODY$;

Expand All @@ -27,10 +28,11 @@ Security rules make use of some helper functions:
RETURNS boolean
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
VOLATILE NOT LEAKPROOF SECURITY INVOKER
SET search_path = ''
AS $BODY$
BEGIN
return auth.uid() = user_id;
return auth.uid() = user_id;
end;
$BODY$;

Expand All @@ -39,10 +41,11 @@ Security rules make use of some helper functions:
RETURNS boolean
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
VOLATILE NOT LEAKPROOF SECURITY INVOKER
SET search_path = ''
AS $BODY$
BEGIN
return auth.uid() = ANY(members);
return auth.uid() = ANY(members);
end;
$BODY$;

Expand All @@ -51,15 +54,16 @@ Security rules make use of some helper functions:
RETURNS boolean
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
VOLATILE NOT LEAKPROOF SECURITY INVOKER
SET search_path = ''
AS $BODY$
DECLARE
members uuid[];
members uuid[];
BEGIN
SELECT "userIds" INTO members
FROM chats.rooms
WHERE id = room_id;
return chats.is_member(members);
SELECT "userIds" INTO members
FROM chats.rooms
WHERE id = room_id;
return chats.is_member(members);
end;
$BODY$;
```
Expand Down
11 changes: 11 additions & 0 deletions doc/docs/guides/supabse-indexes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
id: supabase-indexes
title: Database Indexes
---

These indexes are added to improve the performance of foreign keys in database tables:

```sql
CREATE INDEX ON "chats"."messages" USING btree ("authorId");
CREATE INDEX ON "chats"."messages" USING btree ("roomId");
```
69 changes: 52 additions & 17 deletions doc/docs/guides/supabse-trigges.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,73 @@ title: Database Triggers
This is an example of a triggers that sets room's `lastMessages` to the most recent message sent once recieved in Supabase.

```sql
CREATE OR REPLACE FUNCTION chats.update_last_messages()
RETURNS TRIGGER AS $$
DECLARE
CREATE OR REPLACE FUNCTION chats.update_last_messages()
RETURNS TRIGGER
SET search_path = ''
AS $$
DECLARE
ts_in_milliseconds bigint;
BEGIN
BEGIN
SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds;
UPDATE chats.rooms
SET "updatedAt" = ts_in_milliseconds,
"lastMessages" = jsonb_build_array(NEW)
WHERE id = NEW."roomId";
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER update_last_messages_trigger
AFTER INSERT ON chats.messages
FOR EACH ROW
EXECUTE FUNCTION chats.update_last_messages();
END;
$$ LANGUAGE plpgsql;

drop trigger if exists update_last_messages_trigger on chats.messages;
CREATE TRIGGER update_last_messages_trigger
AFTER INSERT OR UPDATE ON chats.messages
FOR EACH ROW
EXECUTE FUNCTION chats.update_last_messages();
```

"This trigger, on the other hand, is responsible for setting the message status to `sent` when it is added to the `messages` table:

```sql
CREATE OR REPLACE FUNCTION set_message_status_to_sent()
RETURNS TRIGGER AS $$
CREATE OR REPLACE FUNCTION chats.set_message_status_to_sent()
RETURNS TRIGGER
SET search_path = ''
AS $$
BEGIN
NEW.status := 'sent';
RETURN NEW;
NEW.status := 'sent';
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

drop trigger if exists update_status_before_insert on chats.messages;
CREATE TRIGGER update_status_before_insert
BEFORE INSERT ON chats.messages
FOR EACH ROW EXECUTE FUNCTION set_message_status_to_sent();
BEFORE INSERT ON chats.messages
FOR EACH ROW EXECUTE FUNCTION chats.set_message_status_to_sent();
```

"This trigger, is responsible for replicate `auth.users` table rows in `chats.users` table, this is to avoid exposing user data :

```sql

CREATE OR REPLACE FUNCTION chats.handle_new_user()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100
VOLATILE NOT LEAKPROOF SECURITY DEFINER
SET search_path=public
SET search_path = ''
AS $BODY$
DECLARE
ts_in_milliseconds bigint;
BEGIN
SELECT EXTRACT(epoch FROM NOW()) * 1000 INTO ts_in_milliseconds;
insert into chats.users (id, "createdAt", "updatedAt", "lastSeen")
values (new.id, ts_in_milliseconds, ts_in_milliseconds, ts_in_milliseconds);
return new;
end;
$BODY$;

drop trigger if exists on_auth_user_created on auth.users;
create trigger on_auth_user_created
after insert on auth.users
for each row execute procedure chats.handle_new_user();

```
Binary file added doc/static/img/flutter_chat_ui.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion example/lib/constants.dart

This file was deleted.

5 changes: 2 additions & 3 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import 'package:flutter/material.dart';
import 'package:flutter_supabase_chat_core/flutter_supabase_chat_core.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import 'color_schemes.g.dart';
import 'home.dart';
import 'rooms.dart';
import 'src/pages/home.dart';
import 'src/theme/color_schemes.dart';
import 'supabase_options.dart';

void main() async {
Expand Down
160 changes: 0 additions & 160 deletions example/lib/rooms.dart

This file was deleted.

19 changes: 19 additions & 0 deletions example/lib/src/class/message_status_ex.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;

extension MessageStatusEx on types.Status {
IconData get icon {
switch (this) {
case types.Status.delivered:
return Icons.done_all;
case types.Status.error:
return Icons.error_outline;
case types.Status.seen:
return Icons.done_all;
case types.Status.sending:
return Icons.timelapse;
case types.Status.sent:
return Icons.done;
}
}
}
7 changes: 7 additions & 0 deletions example/lib/src/class/user_ex.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import 'package:flutter_chat_types/flutter_chat_types.dart' as types;

extension UserEx on types.User {
String getUserName() => firstName != null || lastName != null
? '${firstName ?? ''} ${lastName ?? ''}'.trim()
: id;
}
File renamed without changes.
6 changes: 4 additions & 2 deletions example/lib/chat.dart → example/lib/src/pages/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,10 @@ class _ChatPageState extends State<ChatPage> {
void _handleMessageTap(BuildContext _, types.Message message) async {
if (message is types.FileMessage) {
final client = http.Client();
final request = await client.get(Uri.parse(message.uri),
headers: SupabaseChatCore.instance.httpSupabaseHeaders);
final request = await client.get(
Uri.parse(message.uri),
headers: SupabaseChatCore.instance.httpSupabaseHeaders,
);
final result = await FileSaver.instance.saveFile(
name: message.uri.split('/').last,
bytes: request.bodyBytes,
Expand Down
Loading

0 comments on commit 852fc07

Please sign in to comment.