Documentation Index
Fetch the complete documentation index at: https://www.osohq.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
In this guide, you learn how to use Oso Cloud for the live authorization calls.
First, you will remove the old code. After, you can enhance the Polar code
for making your authorization even more robust.
Remove the old code
After deleting the inline authorization code from canReadRepo(), the function looks like this:
import { resolve } from "path";
import { UserWithRoles } from "../authn";
import { Repository, PrismaClient } from "@prisma/client";
import { Oso } from "oso-cloud";
import * as oso from "oso-cloud";
// Make sure the API key is defined and instantiate the client
if (!process.env.OSO_API_KEY) {
throw "Missing OSO API key from environment";
}
const oso_url = process.env.OSO_URL
? process.env.OSO_URL
: "https://cloud.osohq.com";
const osoClient = new Oso(oso_url, process.env.OSO_API_KEY, {
dataBindings: resolve("local_authorization_config.yaml"),
});
// A user can read a repo if they have any role on the repo or its parent organization.
export async function canReadRepo(
prisma: PrismaClient,
user: UserWithRoles,
repo: Repository,
): Promise<boolean> {
// entities for Oso
const osoUser = { type: "User", id: user.id.toString() };
const osoRepo = { type: "Repository", id: repo.id.toString() };
// Call authorizeLocal() to return a facts query
// derived from configuration in local_authorization_config.yaml
const query = await osoClient.authorizeLocal(osoUser, "read", osoRepo);
// Run the query
const rows = await prisma.$queryRawUnsafe<oso.AuthorizeResult[]>(query);
// Save the result to authorizedOso
const authorized = rows[0].allowed;
return authorized;
}
The authorization code does four things:
- Instantiate the Oso Cloud client with a configuration file
- Create
User and Repository resources
- Call
authorizeLocal() to receive a query that will yield the authorization
decision
- Return the query as an authorization decision
You replaced a piece of authorization logic with Oso Cloud without disrupting your existing functionality. The resulting authorization code is explicit, concise, and easy to understand.
Next steps
Add policy tests
With Oso Cloud, you can write tests to validate your policy behaves as you
expect. This delivers confidence that your existing functionality remains intact as you extend your policy. Add the following test to validate the “read repository” action:
test "a user with a role on a repository or its parent org can read the repo" {
setup {
has_role(User{"alice"}, "admin", Organization{"acme"});
has_role(User{"bob"}, "reader", Repository{"cool-app"});
has_relation(Repository{"cool-app"}, "parent", Organization{"acme"});
has_relation(Repository{"org-configs"}, "parent", Organization{"acme"});
}
assert has_permission(User{"alice"}, "read", Repository{"cool-app"});
assert has_permission(User{"alice"}, "read", Repository{"org-configs"});
assert_not has_permission(User{"alice"}, "read", Repository{"some-other-repo"});
assert has_permission(User{"bob"}, "read", Repository{"cool-app"});
assert_not has_permission(User{"bob"}, "read", Repository{"org-configs"});
assert_not has_permission(User{"bob"}, "read", Repository{"some-other-repo"});
}
Make use of Polar abstractions and shorthand rules
You do not want to leave your authorization logic as has_permission statements
for long. Polar provides some powerful features that will make your authorization logic better encapsulated and more concise. Some of these are:
You can incorporate these next. For example, you can modify the existing Polar as follows:
actor User {
}
resource Organization {
roles = ["admin", "member"];
}
resource Repository {
permissions = [
"read",
];
roles = [
"admin",
"maintainer",
"editor",
"reader"
];
relations = {
parent: Organization
};
"read" if role on "parent";
"read" if role;
}
This is functionally equivalent to the previous code, but now the available
roles and the relationship between repositories and organizations are explicit.
If you added a policy test, you can confirm that the logic
behaves identically.
Use feature flags
With this approach, you run your existing logic alongside the Oso Cloud logic
for a period of time. As a result, you can use feature flags to migrate from
one to the other. This provides a phased rollout to monitor app behavior over
time. This permits quick restoration of the original logic if something
unexpected happens.
As your application grows, you may find that database performance degrades.
With Local Authorization, the database operation is isolated to a single,
distinct line of code. This encapsulation is easy to instrument and monitor
performance. If you find that an authorization query starts to become
a bottleneck, you can centralize the data for that
query in Oso Cloud.
When you centralize data in Oso Cloud, fact queries happen on our servers.
This reduces load on your databases, but it requires an
initial
sync
of your data and then updates
when it changes. We recommend keeping your data in your application databases
so to maintain a single source of truth for authorization data.
Reach out
If you have any questions, we would love to hear from you.
Just reach out on Slack and one of our engineers will give you a hand.