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 issues
sql_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.

Get started with Oso Cloud →