import { UserWithRoles } from "../authn";
import { Repository } from "@prisma/client";
import { 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);
// A user can read a repo if they have any role on the repo or its parent organization.
export async function canReadRepo(
user: UserWithRoles,
repo: Repository,
): Promise<boolean> {
const orgRole = user.orgRoles.some((orgRole) => orgRole.orgId == repo.orgId);
const repoRole = user.repoRoles.some(
(repoRole) => repoRole.repoId == repo.id,
);
const authorizedInline = orgRole || repoRole;
// entities for Oso
const osoUser = { type: "User", id: user.id.toString() };
const osoRepo = { type: "Repository", id: repo.id.toString() };
const osoOrg = { type: "Organization", id: repo.orgId.toString() };
// get roles for context facts
const osoOrgRole = user.orgRoles
.filter((orgRole) => orgRole.orgId == repo.orgId)
.pop();
const osoRepoRole = user.repoRoles
.filter((repoRole) => repoRole.repoId == repo.id)
.pop();
// define context facts
let contextFacts: [
string,
{ type: string; id: string },
string,
{ type: string; id: string },
][] = [
// fact format: has_relation(Repository: repoId, "parent", Organization: orgId)
["has_relation", osoRepo, "parent", osoOrg],
];
if (osoOrgRole) {
// has_role(User: userId, role, Organization: orgId)
contextFacts.push(["has_role", osoUser, osoOrgRole.role, osoOrg]);
}
if (osoRepoRole) {
// has_role(User: userId, role, Repository: repoId)
contextFacts.push(["has_role", osoUser, osoRepoRole.role, osoRepo]);
}
// authorize using oso.authorize
const authorizedOso = await osoClient.authorize(
osoUser,
"read",
osoRepo,
contextFacts,
);
console.log(
`User:${user.id} read Repository:${repo.id}: inline: ${authorizedInline}; Oso: ${authorizedOso}`,
);
return authorizedInline;
}