Filtering Related Rows Based on User Permissions
In this lesson, we’ll explore how to apply user-based access controls to related rows in your data model. Specifically, we’ll filter the TimeEntry
API to ensure that only entries for tasks the user is allowed to view are accessible. This involves reusing the apiPrefilter
logic from the Task
entity and applying it to related entities like TimeEntry
.
Step 1: Refactor apiPrefilter
into a Custom Filter
First, we’ll refactor the apiPrefilter
logic in Task
into a reusable custom filter. This allows us to apply the same filtering logic in multiple places, ensuring consistency.
Task Entity
In Task
, we define a static custom filter, allowedTasks
, which checks the user’s role:
Explanation of the Code
allowedTasks
Custom Filter: This filter usesremult.isAllowed
to check if the user has theadmin
role. Admins can access all tasks, while other authenticated users can only access their own tasks.apiPrefilter
in Task: We then useallowedTasks
withinapiPrefilter
, ensuring that only allowed tasks are accessible through the API.
Step 2: Apply the Custom Filter in TimeEntry
Now that we have the allowedTasks
filter, we can use it in the TimeEntry
entity to restrict access based on the user’s permissions for related tasks.
TimeEntry Entity
In TimeEntry
, we apply the allowedTasks
filter to only show time entries associated with tasks the user is permitted to view:
Explanation of the Code
apiPrefilter
in TimeEntry: This prefilter checksTimeEntry
rows by filtering based on the tasks the user can access. First, we fetch the IDs of tasks allowed for the user by callingTask.allowedTasks()
. We then use these IDs to filter theTimeEntry
API, ensuring that only time entries related to accessible tasks are visible.
Try It Out!
- Sign in as Alex (non-admin): Alex can only see time entries for tasks he owns.
- Sign in as Jane (admin): Jane can access all time entries, regardless of the task owner.
This setup demonstrates how to efficiently apply consistent access control across related entities using a reusable custom filter.
Improving Performance with SQL-Based Filtering
Below, we modify apiPrefilter
in TimeEntry
to use SqlDatabase.rawFilter
. This lets us directly create a SQL-based filter that leverages the related Task
entity filter without fetching task data in advance:
Explanation of the Code
-
SQL-Based Filter for Task Access: Instead of fetching allowed tasks and filtering in memory, we use
SqlDatabase.rawFilter
to create a dynamic SQL subquery that appliesTask.allowedTasks()
directly in the database. -
Roles of
dbNamesOf
,rawFilter
, andfilterToRaw
:-
dbNamesOf
: This function retrieves database-specific names for entity fields and tables, which helps build queries compatible with the database schema. In this example, we usedbNamesOf
to get the table names and field references forTask
andTimeEntry
, ensuring SQL compatibility. -
rawFilter
: TheSqlDatabase.rawFilter
function enables direct SQL manipulation for custom filters. This bypasses the usual in-memory filtering, allowing filters to execute within the database. Here, it constructs an SQLIN
query that checks if thetaskId
inTimeEntry
exists in a filtered list ofTask
IDs. -
filterToRaw
: This helper translates a standard filter (likeTask.allowedTasks()
) into a raw SQL condition. It processes the custom filter defined inallowedTasks()
and converts it into SQL, ensuring that ourTask
filtering rules are directly translated into the SQL subquery.
-
-
Efficient Filtering: By translating the
allowedTasks
filter to SQL, we ensure that all filtering happens within the database, reducing memory usage and improving query speed for better performance.
Try It Out!
To see this SQL-based filtering in action:
- Click Solve button to see the try the sql based implementation.
- Sign in as different users (e.g., Alex and Jane) to observe how access to
TimeEntry
records changes based on the user’s roles and permissions.
Using SQL-based filters provides an optimized way to manage related access control by leveraging the database’s capabilities, especially useful when dealing with large datasets or complex access rules.
- Installing dependencies
- Starting http server