This Ruby on Rails sample app demonstrates how to render a list of resources the current user has access to in a web app. It uses Oso List Filtering to generate this list without wastefully fetching all authorized resources from the authorization service, and without making multiple requests to the authorization service. To minimize the amount of data that needs to be fetched from the authorization service, some authorization data is stored in this app's own database, and other authorization data is stored in the authorization service. See here for more information on authorizing lists with local data.
The authorization policy is in oso.polar:
actor User {}
resource Project {
roles = ["owner"];
}
resource Article {
permissions = ["view"];
relations = { project: Project };
"view" if "owner" on "project";
"view" if is_public(resource);
}This policy uses three pieces of authorization data:
- Roles on projects:
has_role(User:<user id>, "owner", Project:<project id>) - Relations between articles and projects:
has_relation(Article:<article id>, "project", Project:<project id>) - Attributes on articles:
is_public(Article:<article id>
The service presented in this sample app manages Articles, which users are granted access to
if they own the Project the Article belongs to or if the Article's
owner has made the Article public.
Imagine that Articles are solely the domain of this service, whereas Projects
are a concept that may be relevant to multiple services in our system.
Authorization data about Projects (the roles that users have on Projects)
is centralized in the authorization service so that we may use that information
in other services in the future.
On the other hand, we can keep authorization data about Articles
(which Project the article belongs to and whether the article is public)
in this service's database.
The create_articles migration
creates an articles table in our PostgreSQL database which stores information
about Articles. This includes authorization data as well as
data that's not relevant to authorization, such as the article's title and body.
oso.yml specifies how to resolve authorization data from the database:
facts:
has_relation(Article:_, String:project, Project:_):
query: SELECT id, project FROM articles
is_public(Article:_):
query: SELECT id FROM articles WHERE is_public
sql_types:
Article: bigintTo enforce our authorization policy, we first set up the Oso Cloud client in the oso.rb initializer:
$oso = OsoCloud::Oso.new(
api_key: ENV['OSO_AUTH'],
data_bindings: Rails.root.join('config', 'oso.yml')
)We pass the path to oso.yml into the client so that Oso Cloud knows that some of the authorization data
is stored in our own system.
In the Articles controller
we use Oso's list_local method to have Oso Cloud partially evaluate a list query
against the data that's stored centrally (roles on Projects), and it returns to us a
filter that we can use to finish evaluating the authorization logic against the data
stored in our database.
We pass the filter returned by list_local into
Active Record's where method to filter the results from the database
down to just the ones the current user is authorized to view,
and combine it with some business logic to sort and limit the results:
class ArticlesController < ApplicationController
def index
@articles = Article.where($oso.list_local(current_user, 'view', 'Article', 'id'))
.order(:created_at)
.limit(10)
end
endThe final product is that the homepage of our app displays a list of articles the current user is authorized to view.
- Make sure you have Ruby on Rails and PostgreSQL installed, and that
you have your Oso Cloud API Key in the
OSO_AUTHenvironment variable. - Run
bin/rails db:setupto set up a PostgreSQL database and seed it with some data. - Run
bin/rails oso:seedto seed your Oso Cloud environment with some data. - Run
bin/rails serverto start the Rails server. - Open
http://localhost:3000in your browser.