Pick a language:

Quickstart

This guide will walk you through your first change to an Oso policy file. There are three steps:

  1. Download a minimal Go starter project that’s already integrated with Oso.
  2. Run the server and visit the app in your browser.
  3. Make a small change to the policy to allow a new type of access.

1. Clone the repo and install dependencies

First, clone the Go quickstart repo, and install the dependencies:

git clone https://github.com/osohq/oso-go-quickstart.git
cd oso-go-quickstart
go mod download

2. Run the server

With the dependencies installed, you should be ready to start the server:

go run .

If all is well, the server should be listening on port 5000.

Visit http://localhost:5000/repo/gmail in your browser. You should see a successful response, indicating that you have access to the gmail repo.

To see an unsuccessful response, visit http://localhost:5000/repo/react. You’ll see an error: Repo named react was not found. There actually is a repo named react, but you don’t have access to it. Let’s fix that now.

3. Update the policy

In main.polar, add the following two lines to define a new “rule.” This rule will allow any “actor” (or user) to perform the "read" action on a repository if that repository is marked as IsPublic.

main.polar
actor User {}

resource Repository {
  permissions = ["read", "push", "delete"];
  roles = ["contributor", "maintainer", "admin"];

  "read" if "contributor";
  "push" if "maintainer";
  "delete" if "admin";

  "maintainer" if "admin";
  "contributor" if "maintainer";
}

# This rule tells Oso how to fetch roles for a repository
has_role(user: User, roleName: String, repository: Repository) if
  role in user.Roles and
  role.Role = roleName and
  role.RepoId = repository.Id;

has_permission(_actor: User, "read", repository: Repository) if
  repository.IsPublic;

allow(actor, action, resource) if
  has_permission(actor, action, resource);

Restart the server, and again visit http://localhost:5000/repo/react. Now, you’ll see a successful response:

A
200 response from /repo/react

What just happened?

The quickstart server uses an Oso policy to make sure users are allowed to view repos. The call to oso.Authorize() in server.go performs this check in the /repo/:repoName route. If the user does not have access to a repository, an error response is returned to them.

In this case, the repo with the name react is public because of its definition in the models.go file, so it should be accessible to everyone. By making the change to main.polar, you told Oso to allow users to "read" repositories that have the IsPublic field set to true.

That way, when you visited the react repo in your browser, Oso determined that the action was permitted!

Check out the full code for the example below:

server.go
package main

import (
	"fmt"
	"reflect"

	"github.com/gofiber/fiber/v2"
	"github.com/osohq/go-oso"
)

func main() {
	app := fiber.New()
	oso, err := oso.NewOso()
	if err != nil {
		fmt.Printf("Failed to set up Oso: %v", err)
		return
	}

	oso.RegisterClass(reflect.TypeOf(Repository{}), nil)
	oso.RegisterClass(reflect.TypeOf(User{}), nil)
	if err := oso.LoadFiles([]string{"main.polar"}); err != nil {
		fmt.Printf("Failed to start: %s", err)
		return
	}

	app.Get("/repo/:repoName", func(c *fiber.Ctx) error {
		c.Set(fiber.HeaderContentType, fiber.MIMETextHTML)
		repoName := c.Params("repoName")
		repository := GetRepositoryByName(repoName)
		err := oso.Authorize(GetCurrentUser(), "read", repository)
		if err == nil {
			return c.Status(200).SendString(fmt.Sprintf("<h1>A Repo</h1><p>Welcome to repo %s</p>", repository.Name))
		} else {
			return c.Status(404).SendString(fmt.Sprintf("<h1>Whoops!</h1><p>Repo named %s was not found</p>", repoName))
		}
	})
	if err := app.Listen(":5000"); err != nil {
		fmt.Printf("Failed to start: %s", err)
	}
}
models.go
package main

type Repository struct {
	Id       int
	Name     string
	IsPublic bool
}

var reposDb = map[string]Repository{
	"gmail": {Id: 0, Name: "gmail"},
	"react": {Id: 1, Name: "react", IsPublic: true},
	"oso":   {Id: 2, Name: "oso"},
}

func GetRepositoryByName(name string) Repository {
	return reposDb[name]
}

type RepositoryRole struct {
	Role   string
	RepoId int
}

type User struct {
	Roles []RepositoryRole
}

var usersDb = map[string]User{
	"larry":  {Roles: []RepositoryRole{{Role: "admin", RepoId: 0}}},
	"anne":   {Roles: []RepositoryRole{{Role: "maintainer", RepoId: 1}}},
	"graham": {Roles: []RepositoryRole{{Role: "contributor", RepoId: 2}}},
}

func GetCurrentUser() User {
	return usersDb["larry"]
}

Want to talk it through?

If you have any questions, are getting stuck, or just want to talk something through, jump into Slack and an engineer from the core team (or one of the hundreds of developers in the growing community) will help you out.


Was this page useful?