Oso + PlanetScale
Originally written for Oso.
PlanetScale is a database platform designed to facilitate the management and scaling of distributed databases.
With a focus on simplicity and efficiency, PlanetScale aims to streamline the challenges of handling databases that span multiple regions.
By offering tools and features that prioritize data integrity and availability, PlanetScale provides developers with a practical solution for building and maintaining databases that can seamlessly operate in various geographic locations
PlanetScale does not inherently provide support for Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC) as part of its default functionality.
RBAC establishes a framework for overseeing and implementing user permissions, contingent on the roles they hold within an organization. RBAC guarantees that users are restricted from accessing resources and executing actions pertinent to their designated roles.
ABAC operates comparably, enforcing user permissions by considering attributes such as user traits, properties of resources, and contextual circumstances. This approach permits a higher degree of precision when regulating access.
To implement RBAC or ABAC with PlanetScale in this guide, you will use Oso - a batteries-included authorization-as-a-service (AaaS) provider.
Prerequisites
- Node.js installed
- An initialized PlanetScale project with a created database
- An Oso account
Setting Up the Node.js Project
You will use Prisma - an Object Relational Mapping (ORM) library for this project.
Head over to your terminal and install the necessary dependencies:
npm i --save-dev prisma
This will install the Prisma CLI, necessary for pushing changes from schema to the database.
You will also need clients for Prisma and Oso.
npm i @prisma/client oso-cloud
Follow by initializing a Prisma project:
npx prisma init
Create a new file index.js
which will serve as the main file in the project.
Continue by importing the dependencies:
import { PrismaClient } from '@prisma/client';
import { Oso } from 'oso-cloud';
Next, add Oso’s API key:
const osoApiKey = process.env.OSO_API_KEY || '';
Note: These should be read from the environment variables. For that instance use the
dotenv
package.
Setting Up Oso Rules
Next, you'll establish Oso Rules that will define which roles have permission to perform specific actions on arbitrary resources.
Access the Oso Rules Editor in the Workbench mode - a more visually pleasing way of implementing auhtorization.
In the top section, it should say Add a resource
. Next to the dropdown, enter the Resource name Star
. Press the +
button on the right side to create a new resource.
Resource Star
will now be populated with the default roles and permissions. Since we will dynamically provide Oso with actions performed by an actor, they should match action names from Prisma.
For example, CRUD operations in Prisma can be used.
Next, change the view
permission to findMany
and edit
to create
.
view
→findMany
edit
→create
You can also create tests, automatically, by pressing Create Test
. This will create a few edge-cases for you to visually see if the tests have passed.
All of this resulted in the following Polar policy.
Polar is a declarative policy language used for defining authorization rules in applications.
actor User {}
resource Star {
roles = ["viewer", "owner"];
permissions = ["findMany", "create"];
"findMany" if "viewer";
"create" if "owner";
"viewer" if "owner";
}
test "Star roles and permissions" {
setup {
has_role(User{"alice"}, "viewer", Star{"example"});
has_role(User{"bob"}, "owner", Star{"example"});
}
assert allow(User{"alice"}, "findMany", Star{"example"});
assert allow(User{"bob"}, "findMany", Star{"example"});
assert_not allow(User{"alice"}, "create", Star{"example"});
assert allow(User{"bob"}, "create", Star{"example"});
}
Integrating Oso in Prisma
You will now create a custom Prisma Client query, that will be preformed prior to the original query, allowing for fine-grained authorization control.
Setting Up Prisma Schema
In the schema.prisma
file, located in the prisma
folder, replace the code with the following snippet, allowing for MySQL usage.
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
Create an .env
file, which will contain the DATABASE_URL
from PlanetScale. Learn how to connect a PlanetScale database.
Make sure to append
&&sslaccept=strict
at the end of your database URL in the.env
file.
Next, create a new model Star
, containing basic attributes for testing.
model Star {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
name String @db.VarChar(255)
constellation String @db.VarChar(255)
}
Complete it off by pushing the changes to the database with the following command:
npx prisma db push
Extending a Prisma Query
Back in the Node.js project, let’s create an extension of a findMany
query which returns a list of records for a given model.
const xprisma = prisma.$extends({
query: {
star: {
async findMany({ model, operation, args, query }) {
if (await oso.authorize(USER, operation, model)) return query(args);
return new Error('Not authorized');
},
},
},
});
Replace
USER
with actual data.
The method will execute the findMany
query on the star
model (typings were automatically generated by Prisma in the previous steps), while performing an authorization check with Oso Cloud.
Method will return an object, containing arguments like model we’re querying and the operation we’re performing.
In this case, the arguments in the oso.authorize()
method will be populated with the following data (USER, "findMany", "Star")
.
If the action is authorized, the query will be executed and the results returned.
Test the Integration
To test the integration of Oso and PlanetScale, we will first populate the table with some data.
await prisma.star.create({
data: {
name: 'Sirius',
constellation: 'Canis Major',
},
});
Then, we can call the findMany
query from xprisma
with the following method:
const stars = await xprisma.star.findMany();
console.log(stars);
If authorized, this should output the following:
And if not, an error will be thrown:
Next Steps and Resources
In this guide you learned how to integrate Oso and PlanetScale by using Prisma, a powerful ORM tool.
With Oso's adaptable policy language and PlanetScale's strong distributed database system, you can easily enforce strict access control and authorization rules while smoothly handling your data.
A good place to learn Oso from head to toe is the extensive documentation, blog containing tutorials, thought pieces and updates, or perhaps Oso Academy - a series of technical guides for understanding and applying authorization.