An illustration of a local development workflow for Oso Cloud
This tutorial assumes that you arere familiar with the basics of Polar, facts, and policy tests.If you are not, check out the Introduction to
Polar and Testing with
Polar pages.
With the Oso Cloud developer tools installed, you can set up a local development workflow for your authorization code.By eliminating dependencies on remote services, tighter feedback loops emerge that helps detect issues more quickly.This workflow helps achieve that loop.
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 workflow with a simple example for developers who use Visual Studio Code.
To start with this workflow, you can use the policy below.
policy.polar
Copy
Ask AI
actor User {}resource Organization { roles = ["member"]; permissions = ["view"]; "view" if "member";}test "example test" { setup { has_role(User{"alice"}, "member", Organization{"org1"}); } assert allow(User{"alice"}, "view", Organization{"org1"});}
This policy defines a single actor, User, and a single resource, Organization.The policy states that a User has the view permission on an Organization if they have the member role on that Organization.Open the policy in an editor. With VS Code, it will look like this:Note the “Run Test” link over the test definition in line 10. Click “Run Test” to see the result in the editor window.The three dots under the name of the rule indicates the test passed.To continue the workflow, add another resource to the policy.
An Organization contains one or more Projects. Add a Project resource to the policy so you can model its authorization logic.
policy.polar
Copy
Ask AI
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 { has_role(User{"alice"}, "member", Organization{"org1"}); } assert allow(User{"alice"}, "view", Organization{"org1"});}
The policy now defines a Project resource. It states that a User can inherit the member role on a Project from its parent Organization. Next, you will validate the policy syntax.
Syntax errors are indicated in the VSCode editor. For example, if you omit a trailing semi-colon, 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 can see a description of the issue.Once the trailing semi-colon is added, the VS Code editor window removes the red line.
You can also use the oso-cloud validate CLI command to validate the syntax from the command line.
Copy
Ask AI
❯ 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.
Next, you will add tests to confirm that the authorization logic for Projects is correct. The initial policy has a test that validates the Organization role logic.
Now that the Project resource is added, you can 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.
Copy
Ask AI
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 for the two resources.
First, make sure the Oso Dev Server is running. The filename is standalone and you can start it from wherever you saved it.
Copy
Ask AI
❯ ~/.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, the dev server 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:
You can verify that the correct policy is loaded by issuing the oso-cloud policy command without an argument.
Copy
Ask AI
❯ oso-cloud policy
The currently loaded policy will be written to stdout.
Click to expand policy
Copy
Ask AI
== 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 are ready to test your application locally against the updated policy.
After deleting old facts from the Oso Dev Server, you can re-seed it with any initial facts that are not 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. 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 cover all of your code paths, including those that add facts to Oso Cloud. If you preseed facts that are added by your tests, the tests will not identify bugs in code adding those facts.However, if the testing database has an initial state, you should add facts to the Oso Dev Server to mirror the database’s 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.
To use the Oso Dev Server from your code, you instantiate the client in your application with the dev server’s URL. Refer to the your SDK’s documentation for details. For example, you’d initialize the Node.js client like this:
Copy
Ask AI
const { Oso } = require("oso-cloud");const oso = new Oso( "http://localhost:8080", "e_0123456789_12345_osotesttoken01xiIn",);
With the Oso Cloud developer tools, you can build a local development workflow to iterate quickly on authorization code. That workflow follows 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
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.