Local Development Workflow
This walkthrough assumes that you're familiar with the basics of Polar, facts, and policy tests. If you aren't, check out the Introduction to Polar and Polar: Testing and Policy Iteration pages.
If you've installed the Oso Cloud developer tools, then you can set up a local development workflow for your authorization code. By eliminating dependencies on remote services, you can create tighter feedback loops that let you detect many issues more quickly. There are a lot of ways to set up a local development workflow, but the general process looks like this:
- Make a change to your policy
- Validate the policy syntax
- Add policy tests
- Run the policy tests against the Oso Dev Server
- Push the updated policy to the Oso Dev Server
- Test application code against the updated policy
We'll illustrate the process with a simple example. All code is embedded in the document, so you can download it and follow along.
Starter policy
For this illustration, we'll use VS Code and the starter policy below. If you don't use VS Code, that's fine - just skip the parts that refer to it. It's included here to point out the features of the extension.
This policy defines a single actor, User
, and a single resource, Organization
. It states that a User
has the view
permission on an Organization
if they have the member
role on that Organization
. Pretty basic, but it's all we need to get started. Let's do that.
Open the policy in an editor. If you're using VS Code, it will look something like this:
Note the "Run Test" link over the test definition in line 10. You can click that to run the test and see the result in the editor window.
The success indicator is subtle - it's those 3 dots under the name of the rule that succeeded.
Let's add another resource to the policy.
Make a change to the policy
An Organization contains one or more Projects. Add a Project resource to the policy so you can model its authorization logic.
The policy now defines a Project
resource and states that a User
can inherit the member
role on a Project
from its parent Organization
. Next, you'll validate the policy syntax.
Validate the policy syntax
From VS Code
Syntax errors are indicated in the VSCode editor. For example, if you forget a trailing semicolon, you'll see something like this:
The filename is red in the tab and the word relations
on line 13 is underlined in red. If you hover over relations
, you'll see a description of the issue.
Once you've fixed the problem, the VS Code editor window returns to normal.
Using oso-cloud validate
You can also use the oso-cloud validate
CLI command to validate the syntax from the command line.
❯ oso-cloud validate policy.polarPolicy failed validation with 1 errors:Policy failed validation due to parser error: did not expect to find the token 'relations' at line 13, column 3 of file policy.polar: 013: relations = { ^
Once you've fixed the problem, oso-cloud validate
passes.
❯ oso-cloud validate policy.polarPolicy validated successfully.
Add policy tests
Next, you'll add tests to confirm that the authorization logic for Projects is correct. The starter policy has a test that validates the Organization role logic.
test "example test" { setup { has_role(User{"alice"}, "member", Organization{"org1"}); } assert allow(User{"alice"}, "view", Organization{"org1"});}
Now that there's a Project
resource, you should add tests to confirm the Project
authorization logic. You can create new tests or extend the existing one. For simplicity, we'll extend the existing test.
test "example test" { setup { # Project "project1" belongs to Organization "org1" has_relation(Project{"project1"}, "organization", Organization{"org1"}); # User "alice" has the "member" role on Organization "org1" has_role(User{"alice"}, "member", Organization{"org1"}); # User "bob" has the "member" role on Project "project1" has_role(User{"bob"}, "member", Project{"project1"}); } # Alice can view the "org1" Organization assert allow(User{"alice"}, "view", Organization{"org1"}); # Alice can view the "project1" Project via inheritance from the "org1" Organization assert allow(User{"alice"}, "view", Project{"project1"}); # Bob can not view the "org1" Organization assert_not allow(User{"bob"}, "view", Organization{"org1"}); # Bob can view the "project1" Project via direct assignment assert allow(User{"bob"}, "view", Project{"project1"}); # Charlie can view neither the "org1" Organization nor the "project1" Project assert_not allow(User{"charlie"}, "view", Organization{"org1"}); assert_not allow(User{"charlie"}, "view", Project{"project1"});}
Now the test covers the positive and negative cases of all authorization paths.
Run the policy tests against the Oso Dev Server
Next, you'll run the policy tests against the Oso Dev Server running on your local machine.
Start the Oso Dev Server
First, make sure the Oso Dev Server is running. The filename is standalone
, and you can start it from wherever you saved it.
❯ ~/.local/bin/standaloneServer is in test mode, use token 'e_0123456789_12345_osotesttoken01xiIn'
The server runs in the foreground and listens on port 8080. On startup, it writes the API key to stdout. You can interact with it from the oso-cloud
CLI by setting the OSO_URL
and OSO_AUTH
environment variables as follows:
❯ export OSO_URL='http://localhost:3000'❯ export OSO_AUTH='e_0123456789_12345_osotesttoken01xiIn'
You can either export them to set them for the session, or prepend them to each oso-cloud
command.
Run the policy tests
Run the policy tests against the Oso Dev Server by issuing the oso-cloud test
command:
❯ oso-cloud test policy.polarLoading policy from files:policy.polarRan 1 test:example test: 6/6 PASSPASS
If your policy is split into multiple .polar
files, you must pass all of
them to oso-cloud test
.
Once the tests pass, you can push the updated policy to the Oso Dev Server and test your application code against it.
Push the updated policy to the Oso Dev Server
Push the updated policy to the local binary with the oso-cloud policy
command:
If your policy is split into multiple .polar
files, you must push all of
them to the Oso Dev Server on any update.
❯ oso-cloud policy policy.polarLoading policy from files:policy.polarPolicy successfully loaded.
Verify the policy (optional)
You can verify that the correct policy is loaded by issuing the oso-cloud policy
command without an argument.
❯ oso-cloud policy
The currently loaded policy will be written to stdout.
Click to expand policy
== Policy: policy.polar ==actor User {}resource Organization { roles = ["member"]; permissions = ["view"]; "view" if "member";}resource Project { roles = ["member"]; permissions = ["view"]; relations = { organization: Organization }; "member" if "member" on "organization"; "view" if "member";}test "example test" { setup { # Project "project1" belongs to Organization "org1" has_relation(Project{"project1"}, "organization", Organization{"org1"}); # User "alice" has the "member" role on Organization "org1" has_role(User{"alice"}, "member", Organization{"org1"}); # User "bob" has the "member" role on Project "project1" has_role(User{"bob"}, "member", Project{"project1"}); } # Alice can view the "org1" Organization assert allow(User{"alice"}, "view", Organization{"org1"}); # Alice can view the "project1" Project via inheritance from the "org1" Organization assert allow(User{"alice"}, "view", Project{"project1"}); # Bob can not view the "org1" Organization assert_not allow(User{"bob"}, "view", Organization{"org1"}); # Bob can view the "project1" Project via direct assignment assert allow(User{"bob"}, "view", Project{"project1"}); # Charlie can view neither the "org1" Organization nor the "project1" Project assert_not allow(User{"charlie"}, "view", Organization{"org1"}); assert_not allow(User{"charlie"}, "view", Project{"project1"});}
You can also start the Oso Dev Server with a list of policy files and the
--watch-for-changes
flag to instruct it to automatically reload your policy
files and rerun tests any time they change. See the
docs for more details.
Now you're ready to test your application locally against the updated policy.
Test application code against the Oso Dev Server
Local integration tests and functional tests should run against the local Oso Dev Server after you've updated the policy.
Clear old fact data
To ensure that the Oso Dev Server is in a known state before running local integration tests, you can delete all data by either:
- Issuing the
oso-cloud clear
command from the command line - Invoking the clear_data API endpoint (opens in a new tab) at the Oso Dev Server
Make sure that you run these commands against your local Oso Dev Server instance and not the live Oso Cloud environment.
Re-seed initial state
After deleting old data from the Oso Dev Server, you can re-seed it with any initial state data that isn't recreated by your integration tests. For example, if you have a role
table that contains pre-seeded roles, you should add facts for those roles to the Oso Dev Server before starting your tests. But if your tests add new roles, you should NOT pre-seed the Oso Dev Server with facts for those roles. Instead, you should let your application code do that as part of the tests.
Your integration tests should exercise all of your code paths, including the ones that add data to Oso Cloud. If you preseed facts for data that is added by your tests, then you won't be able to catch bugs in the code that adds those facts.
However, if your testing database has an initial state, you should add facts to the Oso Dev Server to make sure it's in the same initial state. Otherwise tests that rely on a consistent initial state will fail
We recommend defining the initial state in a script that issues oso-cloud tell
commands to the Oso Dev Server for all facts that need to be present before the integration test suite runs.
Initialize the SDK against the Oso Dev Server
To use the Oso Dev Server from your code, you just need to initialize the client in your application against it. Refer to the documentation for the SDK you're using for details. For example, you'd initialize the Node.js client like this:
const { Oso } = require("oso-cloud");const oso = new Oso( "http://localhost:8080", "e_0123456789_12345_osotesttoken01xiIn");
Conclusion
With the Oso Cloud developer tools, you can build a local development workflow that lets you iterate quickly on authorization code. By following these steps ...
- Make a change to your policy
- Validate the policy syntax
- Add policy tests
- Run the policy tests against the Oso Dev Server
- Push the updated policy to the Oso Dev Server
- Test application code against the updated policy
... you can make a series of small authorization changes and validate them on your local system before you commit anything to version control or introduce dependencies on remote services. This will make you and your team more effective - you because you're developing more quickly and your team because you're less likely to push broken code to a shared environment.
Once you've validated your changes locally and are ready to commit, you're ready to start moving your code to production. Read the CI/CD doc to learn how to set up a deployment pipeline that works with Oso Cloud.
Read on for detailed configuration options for the Oso Dev Server, which you can use to refine this workflow to meet your particular needs.