A bear playing hopscotch

Oso 0.20 is out with a big step up for modeling, data filtering, and enforcement

Oso 0.20 is out!

The features in this release have been in the works for a while now, and we're thrilled to finally share them with you all. If you're interested in hearing from our cofounder and CTO on our view on this problem domain and how we are solving it, read his post, Why Authorization is Hard.

0.20 includes new approaches to...

  1. Authorization modeling - we've introduced built-in policy primitives for specifying permissions, roles, and relationships to simplify common patterns like role- and relationship-based access control (RBAC and ReBAC).
  2. Data filtering - the ability to authorize collections of data (e.g., show me only the rows that Juno can see) is now available for Python, Node, and Ruby (and any data source!). Go, Java, and Rust support coming soon.
  3. APIs - Oso 0.20 ships with native APIs for enforcing authorization at different layers of the stack (e.g., request-, resource-, and field-level authorization).

Built-in Primitives for Authorization Modeling

We're introducing several features in 0.20 to improve the experience of modeling your authorization requirements, and implementing that model with Oso.

Our goals for these features were:

  1. Provide built-in ways to define common authorization models to take the guesswork out of policy writing
  2. Introduce intuitive organization to policies
  3. Provide more feedback to developers during the policy writing process

The features we developed to address these goals are resource blocks and rule types.

Role- and Relationship-Based Access Control with Resource Blocks

Resource blocks are a new way to write policies, specifically optimized for modeling roles and relationships. With resource blocks, you can:

  • Organize your policy by actor and resource
  • Explicitly define roles, permissions, and relations for each resource
  • Use the roles, permissions, and relations you defined to write shorthand rules, streamlined rules that eliminate boilerplate and let you focus on authorization logic

Here's what a policy with resource blocks looks like:

allow(actor, action, resource) if has_permission(actor, action, resource);

resource Repository {
    permissions = ["read", "push", "delete"];
    roles = ["contributor", "maintainer", "admin"];

    "read" if "contributor";
    "push" if "maintainer";
    "delete" if "admin";

    "maintainer" if "admin";
    "contributor" if "maintainer";
}

has_role(user: User, name: String, repository: Repository) if
    role in user.roles and
  role.name = name and
  role.resource = repository;

Resource blocks are available in every language library, and are compatible with any data model and data store. You can also use resource blocks alongside base Polar rules, providing you with unlimited extensibility.

Some of the things you can model with resource blocks:

  • Organization-level roles
  • Resource-level roles
  • Roles that span resource hierarchies
  • Group roles

To start modeling with resource blocks, take a look at our guides on modeling role-based access control or resource hierarchies, or read the reference to learn more about the syntax.

Policy Validation with Rule Types

We wanted to improve the policy writing process by providing developers with more feedback and validation. Rule types specify requirements for rules that are checked when policies are loaded. We've written a set of built-in rule types to ensure correct usage of rules that are evaluated by library APIs or used by built-in features.

Here's an example rule type error:

polar.exceptions.ValidationError: Invalid rule: allow(_actor, _action); Must match one of the following rule types:

allow(actor, action, resource);
        Failed to match because: Different number of parameters. Rule has 2 parameter(s) but rule type has 3.

For more information on built-in rule types, check out our docs.

Data filtering from any data source in Python, Ruby, and NodeJS

Over the last six months, we received constant requests to expand data filtering support (previously only in SQLAlchemy and Django) to more languages and frameworks.

This release includes the following updates to data filtering:

  • Data filtering is available for Python, Ruby, and NodeJS (Java, Go, and Rust coming soon!)
  • It works with any data source (you no longer need specific ORMs to use the feature)!
  • A single method to filter data: authorized_resources(actor, action, type) returns all the resources of type type that the actor is allowed to take the action on
  • New APIs for registering information about application types and fetching data, so you can customize how Oso retrieves data from your application

To get started, check out our new guide on how to filter data with Oso.

APIs for Resource, Request and Field-level Authorization

This release expands Oso's library APIs to provide specific methods for common authorization enforcement scenarios, to simplify integrating Oso with your application.

Resource-level authorization

  • authorize(actor, action, resource): ensures an actor can perform a particular action on a resource (this is most similar to is_allowed).
  • authorized_actions(actor, resource): returns all actions an actor can perform on a single resource

Request-level authorization

  • authorize_request(actor, request): ensures an actor can send a particular request to the server.

Field-level authorization

  • authorize_field(actor, action, resource, field): ensures that an actor can perform an action on a particular field of a resource.
  • authorized_fields(actor, action, resource): returns all the fields of a resource that an actor can take an action on

Read more about our new APIs in our new guide to enforcing authorization.

Other Bug Fixes and Improvements

or / and operator precedence

The or operator has had its precedence lowered to be consistent with other programming languages. Existing policies using or should be updated where necessary to group or operations using parentheses.

New policy loading API load_files. Deprecated load_file

Oso.load_file(file_name) has been deprecated and replaced with Oso.load_files([file1, file2]). Users of load_file will receive a deprecation warning.

Calling load_file, load_files or load_str more than once is now an error.

For more details on these and other changes, you can read the changelog. We've also written a migration guide that explains how to fix breaking changes if you're migrating from an earlier version of Oso.

Set up a 1x1 with an Oso engineer

Our team is happy to help you get started with Oso, or migrate to 0.20 if you on an earlier version. If you'd like to try out any of these new features, or if you're interested in learning how to use Oso in your app, schedule a 1x1 with an Oso engineers.

Our team is also available to help in Slack.

Want us to remind you?
We'll email you before the event with a friendly reminder.

Write your first policy