Skip to content

Commit 1a5fe5e

Browse files
authored
[3/6] View level permissions - Rows (baserow#4072)
1 parent 3f79a2d commit 1a5fe5e

29 files changed

Lines changed: 1645 additions & 150 deletions

File tree

backend/src/baserow/contrib/database/api/rows/serializers.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,10 +508,32 @@ class MoveRowQueryParamsSerializer(serializers.Serializer):
508508

509509
class CreateRowQueryParamsSerializer(serializers.Serializer):
510510
before = serializers.IntegerField(required=False)
511+
view = serializers.IntegerField(required=False)
511512

512513

513514
class BatchCreateRowsQueryParamsSerializer(serializers.Serializer):
514515
before = serializers.IntegerField(required=False)
516+
view = serializers.IntegerField(required=False)
517+
518+
519+
class GetRowQueryParamsSerializer(serializers.Serializer):
520+
view = serializers.IntegerField(required=False)
521+
522+
523+
class UpdateRowQueryParamsSerializer(serializers.Serializer):
524+
view = serializers.IntegerField(required=False)
525+
526+
527+
class BatchUpdateRowsQueryParamsSerializer(serializers.Serializer):
528+
view = serializers.IntegerField(required=False)
529+
530+
531+
class DeleteRowQueryParamsSerializer(serializers.Serializer):
532+
view = serializers.IntegerField(required=False)
533+
534+
535+
class BatchDeleteRowsQueryParamsSerializer(serializers.Serializer):
536+
view = serializers.IntegerField(required=False)
515537

516538

517539
class ListRowsQueryParamsSerializer(

backend/src/baserow/contrib/database/api/rows/views.py

Lines changed: 105 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@
5656
ERROR_ROW_IDS_NOT_UNIQUE,
5757
)
5858
from baserow.contrib.database.api.rows.exceptions import InvalidJoinParameterException
59-
from baserow.contrib.database.api.rows.serializers import GetRowAdjacentSerializer
59+
from baserow.contrib.database.api.rows.serializers import (
60+
GetRowAdjacentSerializer,
61+
GetRowQueryParamsSerializer,
62+
)
6063
from baserow.contrib.database.api.tables.errors import ERROR_TABLE_DOES_NOT_EXIST
6164
from baserow.contrib.database.api.tokens.authentications import TokenAuthentication
6265
from baserow.contrib.database.api.tokens.errors import (
@@ -110,7 +113,6 @@
110113
from baserow.contrib.database.table.handler import TableHandler
111114
from baserow.contrib.database.table.models import Table
112115
from baserow.contrib.database.table.operations import (
113-
CreateRowDatabaseTableOperationType,
114116
ListRowNamesDatabaseTableOperationType,
115117
ListRowsDatabaseTableOperationType,
116118
)
@@ -137,12 +139,16 @@
137139
from .schemas import row_names_response_schema
138140
from .serializers import (
139141
BatchCreateRowsQueryParamsSerializer,
142+
BatchDeleteRowsQueryParamsSerializer,
140143
BatchDeleteRowsSerializer,
144+
BatchUpdateRowsQueryParamsSerializer,
141145
CreateRowQueryParamsSerializer,
146+
DeleteRowQueryParamsSerializer,
142147
ListRowsQueryParamsSerializer,
143148
MoveRowQueryParamsSerializer,
144149
RowHistorySerializer,
145150
RowSerializer,
151+
UpdateRowQueryParamsSerializer,
146152
get_batch_row_serializer_class,
147153
get_example_batch_rows_serializer_class,
148154
get_example_row_serializer_class,
@@ -481,6 +487,13 @@ def get(self, request, table_id, query_params):
481487
description="If provided then the newly created row will be "
482488
"positioned before the row with the provided id.",
483489
),
490+
OpenApiParameter(
491+
name="view",
492+
location=OpenApiParameter.QUERY,
493+
type=OpenApiTypes.INT,
494+
description="Provide if the row is created in a view. This can result "
495+
"in different permission checking and default values.",
496+
),
484497
OpenApiParameter(
485498
name="user_field_names",
486499
location=OpenApiParameter.QUERY,
@@ -547,6 +560,7 @@ def get(self, request, table_id, query_params):
547560
{
548561
UserNotInWorkspace: ERROR_USER_NOT_IN_GROUP,
549562
TableDoesNotExist: ERROR_TABLE_DOES_NOT_EXIST,
563+
ViewDoesNotExist: ERROR_VIEW_DOES_NOT_EXIST,
550564
NoPermissionToTable: ERROR_NO_PERMISSION_TO_TABLE,
551565
RowDoesNotExist: ERROR_ROW_DOES_NOT_EXIST,
552566
CannotCreateRowsInTable: ERROR_CANNOT_CREATE_ROWS_IN_TABLE,
@@ -567,13 +581,6 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
567581

568582
TokenHandler().check_table_permissions(request, "create", table, False)
569583

570-
CoreHandler().check_permissions(
571-
request.user,
572-
CreateRowDatabaseTableOperationType.type,
573-
workspace=table.database.workspace,
574-
context=table,
575-
)
576-
577584
user_field_names = extract_user_field_names_from_params(request.GET)
578585
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
579586

@@ -591,13 +598,17 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
591598
else None
592599
)
593600

601+
view_id = query_params.get("view")
602+
view = ViewHandler().get_view(view_id) if view_id else None
603+
594604
try:
595605
row = action_type_registry.get_by_type(CreateRowActionType).do(
596606
request.user,
597607
table,
598608
data,
599609
model=model,
600610
before_row=before_row,
611+
view=view,
601612
user_field_names=user_field_names,
602613
send_webhook_events=send_webhook_events,
603614
)
@@ -740,6 +751,13 @@ class RowView(APIView):
740751
type=OpenApiTypes.INT,
741752
description="Returns the row related the provided value.",
742753
),
754+
OpenApiParameter(
755+
name="view",
756+
location=OpenApiParameter.QUERY,
757+
type=OpenApiTypes.INT,
758+
description="Provide if the row if fetched in a view. This can result "
759+
"in different permission checking and default values.",
760+
),
743761
OpenApiParameter(
744762
name="user_field_names",
745763
location=OpenApiParameter.QUERY,
@@ -800,7 +818,8 @@ class RowView(APIView):
800818
}
801819
)
802820
@allowed_includes("metadata")
803-
def get(self, request, table_id, row_id, metadata):
821+
@validate_query_parameters(GetRowQueryParamsSerializer)
822+
def get(self, request, table_id, row_id, metadata, query_params: dict):
804823
"""
805824
Responds with a serializer version of the row related to the provided row_id
806825
and table_id.
@@ -815,9 +834,12 @@ def get(self, request, table_id, row_id, metadata):
815834
raise TokenCannotIncludeRowMetadata()
816835
token_handler.check_table_permissions(db_token, "read", table)
817836

837+
view_id = query_params.get("view")
838+
view = ViewHandler().get_view(view_id) if view_id else None
839+
818840
user_field_names = extract_user_field_names_from_params(request.GET)
819841
model = table.get_model()
820-
row = RowHandler().get_row(request.user, table, row_id, model)
842+
row = RowHandler().get_row(request.user, table, row_id, model, view=view)
821843
serializer_class = get_row_serializer_class(
822844
model, RowSerializer, is_response=True, user_field_names=user_field_names
823845
)
@@ -846,6 +868,13 @@ def get(self, request, table_id, row_id, metadata):
846868
type=OpenApiTypes.INT,
847869
description="Updates the row related to the value.",
848870
),
871+
OpenApiParameter(
872+
name="view",
873+
location=OpenApiParameter.QUERY,
874+
type=OpenApiTypes.INT,
875+
description="Provide if the row is updated in a view. This can result "
876+
"in different permission checking and default values.",
877+
),
849878
OpenApiParameter(
850879
name="user_field_names",
851880
location=OpenApiParameter.QUERY,
@@ -917,7 +946,10 @@ def get(self, request, table_id, row_id, metadata):
917946
)
918947
@atomic_with_retry_on_deadlock()
919948
@require_request_data_type(dict)
920-
def patch(self, request: Request, table_id: int, row_id: int) -> Response:
949+
@validate_query_parameters(UpdateRowQueryParamsSerializer)
950+
def patch(
951+
self, request: Request, table_id: int, row_id: int, query_params
952+
) -> Response:
921953
"""
922954
Updates the row with the given row_id for the table with the given
923955
table_id. Also the post data is validated according to the tables field types.
@@ -935,6 +967,10 @@ def patch(self, request: Request, table_id: int, row_id: int) -> Response:
935967

936968
user_field_names = extract_user_field_names_from_params(request.GET)
937969
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
970+
971+
view_id = query_params.get("view")
972+
view = ViewHandler().get_view(view_id) if view_id else None
973+
938974
field_ids, field_names = None, None
939975

940976
if user_field_names:
@@ -959,6 +995,7 @@ def patch(self, request: Request, table_id: int, row_id: int) -> Response:
959995
table,
960996
[data],
961997
model=model,
998+
view=view,
962999
send_webhook_events=send_webhook_events,
9631000
)
9641001
.updated_rows[0]
@@ -980,6 +1017,13 @@ def patch(self, request: Request, table_id: int, row_id: int) -> Response:
9801017
type=OpenApiTypes.INT,
9811018
description="Deletes the row in the table related to the value.",
9821019
),
1020+
OpenApiParameter(
1021+
name="view",
1022+
location=OpenApiParameter.QUERY,
1023+
type=OpenApiTypes.INT,
1024+
description="Provide if the row is deleted in a view. This can result "
1025+
"in different permission checking and default values.",
1026+
),
9831027
OpenApiParameter(
9841028
name="row_id",
9851029
location=OpenApiParameter.PATH,
@@ -1028,7 +1072,8 @@ def patch(self, request: Request, table_id: int, row_id: int) -> Response:
10281072
}
10291073
)
10301074
@atomic_with_retry_on_deadlock()
1031-
def delete(self, request, table_id, row_id):
1075+
@validate_query_parameters(DeleteRowQueryParamsSerializer)
1076+
def delete(self, request, table_id, row_id, query_params):
10321077
"""
10331078
Deletes an existing row with the given row_id for table with the given
10341079
table_id.
@@ -1039,8 +1084,15 @@ def delete(self, request, table_id, row_id):
10391084
table = TableHandler().get_table(table_id)
10401085
TokenHandler().check_table_permissions(request, "delete", table, False)
10411086

1087+
view_id = query_params.get("view")
1088+
view = ViewHandler().get_view(view_id) if view_id else None
1089+
10421090
action_type_registry.get_by_type(DeleteRowActionType).do(
1043-
request.user, table, row_id, send_webhook_events=send_webhook_events
1091+
request.user,
1092+
table,
1093+
row_id,
1094+
view=view,
1095+
send_webhook_events=send_webhook_events,
10441096
)
10451097

10461098
return Response(status=204)
@@ -1184,6 +1236,13 @@ class BatchRowsView(APIView):
11841236
description="If provided then the newly created rows will be "
11851237
"positioned before the row with the provided id.",
11861238
),
1239+
OpenApiParameter(
1240+
name="view",
1241+
location=OpenApiParameter.QUERY,
1242+
type=OpenApiTypes.INT,
1243+
description="Provide if the rows are created in a view. This can result "
1244+
"in different permission checking and default values.",
1245+
),
11871246
OpenApiParameter(
11881247
name="user_field_names",
11891248
location=OpenApiParameter.QUERY,
@@ -1283,6 +1342,9 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
12831342
else None
12841343
)
12851344

1345+
view_id = query_params.get("view")
1346+
view = ViewHandler().get_view(view_id) if view_id else None
1347+
12861348
row_validation_serializer = get_row_serializer_class(
12871349
model, user_field_names=user_field_names
12881350
)
@@ -1299,6 +1361,7 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
12991361
table,
13001362
data["items"],
13011363
before_row,
1364+
view=view,
13021365
model=model,
13031366
send_webhook_events=send_webhook_events,
13041367
)
@@ -1327,6 +1390,13 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
13271390
type=OpenApiTypes.INT,
13281391
description="Updates the rows in the table.",
13291392
),
1393+
OpenApiParameter(
1394+
name="view",
1395+
location=OpenApiParameter.QUERY,
1396+
type=OpenApiTypes.INT,
1397+
description="Provide if the rows are updated in a view. This can "
1398+
"result in different permission checking and default values.",
1399+
),
13301400
OpenApiParameter(
13311401
name="user_field_names",
13321402
location=OpenApiParameter.QUERY,
@@ -1407,7 +1477,8 @@ def post(self, request: Request, table_id: int, query_params) -> Response:
14071477
}
14081478
)
14091479
@atomic_with_retry_on_deadlock()
1410-
def patch(self, request, table_id):
1480+
@validate_query_parameters(BatchUpdateRowsQueryParamsSerializer)
1481+
def patch(self, request, table_id, query_params):
14111482
"""
14121483
Updates all provided rows at once for the table with
14131484
the given table_id.
@@ -1421,6 +1492,9 @@ def patch(self, request, table_id):
14211492
user_field_names = extract_user_field_names_from_params(request.GET)
14221493
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
14231494

1495+
view_id = query_params.get("view")
1496+
view = ViewHandler().get_view(view_id) if view_id else None
1497+
14241498
row_validation_serializer = get_row_serializer_class(
14251499
model,
14261500
user_field_names=user_field_names,
@@ -1440,6 +1514,7 @@ def patch(self, request, table_id):
14401514
table,
14411515
data["items"],
14421516
model=model,
1517+
view=view,
14431518
send_webhook_events=send_webhook_events,
14441519
)
14451520
rows = updated_data.updated_rows
@@ -1474,6 +1549,13 @@ class BatchDeleteRowsView(APIView):
14741549
type=OpenApiTypes.INT,
14751550
description="Deletes the rows in the table related to the value.",
14761551
),
1552+
OpenApiParameter(
1553+
name="view",
1554+
location=OpenApiParameter.QUERY,
1555+
type=OpenApiTypes.INT,
1556+
description="Provide if the rows are deleted in a view. This can "
1557+
"result in different permission checking and default values.",
1558+
),
14771559
OpenApiParameter(
14781560
name="send_webhook_events",
14791561
location=OpenApiParameter.QUERY,
@@ -1524,7 +1606,10 @@ class BatchDeleteRowsView(APIView):
15241606
}
15251607
)
15261608
@atomic_with_retry_on_deadlock()
1527-
def post(self, request: Request, table_id: int, data: Dict[str, Any]) -> Response:
1609+
@validate_query_parameters(BatchDeleteRowsQueryParamsSerializer)
1610+
def post(
1611+
self, request: Request, table_id: int, data: Dict[str, Any], query_params
1612+
) -> Response:
15281613
"""
15291614
Batch deletes existing rows based on provided row ids for the table with
15301615
the given table_id.
@@ -1535,10 +1620,14 @@ def post(self, request: Request, table_id: int, data: Dict[str, Any]) -> Respons
15351620

15361621
send_webhook_events = extract_send_webhook_events_from_params(request.GET)
15371622

1623+
view_id = query_params.get("view")
1624+
view = ViewHandler().get_view(view_id) if view_id else None
1625+
15381626
action_type_registry.get_by_type(DeleteRowsActionType).do(
15391627
request.user,
15401628
table,
15411629
row_ids=data["items"],
1630+
view=view,
15421631
send_webhook_events=send_webhook_events,
15431632
)
15441633

0 commit comments

Comments
 (0)