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.
First, select a piece of authorization logic to move to Oso Cloud. For your section of logic should be:
- Well understood
- Low-impact
- Straightforward to extract
In this guide, the process of migration to local authorization is illustrated with code samples from a hypothetical version control application. You can follow along with those or you use examples from your code.
One permission this version control application requires is the ability to read a repository. That permission affects two operations:
read a single repository
- list all the repositories that the user can
read
The logic resides in the corresponding get handlers:
// Return the requested repository if the user has permission to read it
accountsRouter.get(
"/orgs/:orgId/repos/:repoId",
withAuthn,
async (req, res) => {
const user = await currentUser(req);
const orgRole = user.orgRoles.find(
(orgRole) => orgRole.orgId == parseInt(req.params.orgId)
);
const repoRole = user.repoRoles.find(
(repoRole) => repoRole.repoId == parseInt(req.params.repoId)
);
const repo: WithPermissions<Repository> | null =
await req.prisma.repository.findUnique({
where: {
orgId: parseInt(req.params.orgId),
id: parseInt(req.params.repoId),
},
});
if (repo && (orgRole || repoRole)) {
repo.permissions = ["read"];
...
}
}
);
// List all repos that the user can read in the specified org
accountsRouter.get("/orgs/:orgId/repos", withAuthn, async (req, res) => {
let repos = await req.prisma.repository.findMany({
where: { orgId: parseInt(req.params.orgId) },
});
const user = await currentUser(req);
const memberOfOrg = user.orgRoles.some(
(orgRole) => orgRole.orgId == parseInt(req.params.orgId),
);
repos = repos.filter((repo) => {
return (
memberOfOrg ||
user.repoRoles.some((repoRole) => repoRole.repoId == repo.id)
);
});
res.send(repos);
});
This is typical of authorization code:
- The same logic is implemented in multiple places
- The implementation slightly differs in each place
- It is not obvious it is critical authorization logic
This is a good candidate for early refactoring:
- The logic is straightforward - a user can read a repository if:
- They have any role on the repository
- They have any role on the repository’s parent organization
- It only grants read access.
- The logic is already encapsulated.
With a piece of logic to refactor identified, extract it into a dedicated function. This decouples the authorization logic from the surrounding application logic.
Create a function called canReadRepo() in a new file called authz.ts. That makes it obvious which permission it governs.
// A user can read a repo if they have any role on the repo or its parent organization.
function canReadRepo(user: UserWithRoles, repo: Repository): boolean {
const orgRole = user.orgRoles.some((orgRole) => orgRole.orgId == repo.orgId);
const repoRole = user.repoRoles.some(
(repoRole) => repoRole.repoId == repo.id,
);
return orgRole || repoRole;
}
Now you call this function when you authorize a user’s request to read a repository.
import { canReadRepo } from "../authz";
// List all repos that the user can read in the specified org
accountsRouter.get("/orgs/:orgId/repos", withAuthn, async (req, res) => {
let repos = await req.prisma.repository.findMany({
where: { orgId: parseInt(req.params.orgId) },
});
const user = await currentUser(req);
repos = repos.filter((repo) => {
return canReadRepo(user, repo);
});
res.send(repos);
});
// Return the requested repository if the user has permission to read it
accountsRouter.get(
"/orgs/:orgId/repos/:repoId",
withAuthn,
async (req, res) => {
const user = await currentUser(req);
const repo: WithPermissions<Repository> | null =
await req.prisma.repository.findUnique({
where: {
orgId: parseInt(req.params.orgId),
id: parseInt(req.params.repoId),
},
});
if (repo && canReadRepo(user, repo)) {
repo.permissions = ["read"];
...
}
}
);
Even this change provides meaningful benefits:
- A dedicated file for authorization logic (
src/authz.ts).
- The logic for the “read repository” permission is easy to find, understand, and reason about.
- The logic is defined only once.
- The application code is cleaner.
Next, implement the logic in Oso Cloud.