What is List Filtering?
Beyond making simple yes/no authorization decisions, apps regularly need to generate lists of authorized data. Imagine you're building a dashboard for GitCloud users. When an authenticated user accesses the page, they should see a high-level overview of the repositories they have "read" access for. This is an example of a list (all repositories) of authorized data (they have "read" access for).
How to Filter Lists
A naive way to generate that list would be to fetch all repositories from the database and then filter them in-memory by asking if the current user is allowed to view each repository in turn. If GitCloud has millions of repositories, that might get pretty darn slow, not to mention creating a lot of network requests.
When dealing with large sets of data, it's more efficient to operate in bulk. Authorization as a service products typically achieve this by centralizing your authorization data in the service. This works, but if your users have access to millions of resources, it may be inefficient to fetch all of those IDs from the authorization service.
Because of this, Oso Cloud also supports a decentralized approach to list filtering, which allows you to keep some of your application data in your application database. This reduces the amount of data that you have to fetch from Oso Cloud to filter lists. It also makes it easier to manage your data, since less of it has to be synchronized to Oso Cloud.
You can use whichever approach best fits your application and your organization. Read the following docs for detailed instructions:
List Filtering With Centralized Data
Oso Cloud's list
API can efficiently perform authorization checks over collections of data that is stored in Oso Cloud.
The list
API flattens the naive iterative approach into a single request:
"What is the set of repositories that the user is allowed to see?"
The response is a list of IDs of authorized resources (repositories, in this case)
that can then be loaded from the database in one fell swoop.
Once you have fetched the list of authorized IDs from Oso Cloud,
you can plug them into a SQL query in your application code to return the filtered data
you ultimately want to display to your user.
async function authorizedRepositories(oso: Oso, currentUser: User): Promise<Repository[]> { const repositoryIds = await oso.list( {type: "User", id: currentUser.id.toString()}, "view", "Repository" ); return getRepositoriesByIds(repositoryIds);}
For more details, consult the
API docs
for the list
endpoint.
List Filtering with Decentralized Data
Oso Cloud's list_local
API can generate lists of authorized data by combining data in Oso Cloud with data from your application database.
If your users have access to millions of resources, it may be inefficient to fetch all of those
IDs directly with the list
API. Consider building an "Issues" microservice for GitCloud,
which provides users with a paginated list of issues they have access to.
The policy says that users can access issues that belong to repositories they have access to.
If repositories have lots of issues, it could be slow to fetch the list of all authorized issue IDs from Oso Cloud, plug them into a SQL query, and get back the result.
With Oso List Filtering, you can tell Oso to look in your own database for information about which issue belongs to which repository.
This way, the size of the API response Oso Cloud sends your application is proportional to the number of repositories
the user has access to, rather than the (much larger) number of issues the user has access to.
Configuration
You'll use a YAML file to configure how Oso List Filtering looks for authorization data in your database:
facts: has_relation(Issue:_, String:parent, Repository:_): query: SELECT id, repository FROM issuessql_types: Issue: UUID Repository: UUID
This example tells Oso that has_relation
facts that associate issues with their parent repositories
are resolved by running a query (SELECT id, repository FROM issues
) against your application's
database, rather than by looking them up directly in Oso Cloud.
The sql_types
section is optional, but strongly recommended. It maps resources in Oso Cloud (e.g. Issue, Repository) to their data types in your application database. This allows Oso List Filtering to more effectively use indexes, improving query performance.
Usage
When you initialize the Oso Cloud client, provide the YAML configuration:
oso = OsoCloud::new( ... data_bindings: "path/to/data_bindings.yaml")
Then, use the list_local
method to partially evaluate authorization with the
data stored in your Oso Cloud environment (for example, has_role
facts
granting users access to repositories), and get back a filter that finishes
authorization with the data stored in your database. In addition to passing in
the user, action, and resource type as in the regular list
method, you'll pass
in a fourth argument with the name of the column that corresponds to the
resources' IDs. The list_local
method returns a SQL WHERE
clause that you
can combine with other things such as ordering and pagination:
results = Issue.where(oso.list_local(user, "read", "Issue", "id")) .order(:created_at) .limit(25) .offset(50)
For a more full-fledged example of List Filtering with Decentralized Data, check out our Ruby on Rails sample app (opens in a new tab)
Talk to an Oso Engineer
If you'd like to learm more about how to filter lists in your app or have any questions about this guide, schedule a 1x1 with an Oso engineer. We're happy to help.