= yaml = title: Permissions Authorizer layout: tutorial wherein: you meet a built-in authorizer that that lets Alice grant fine-grained access to Bob and Carol with simple lists of names. prerequisites: {completer: permissions-authorizer, scenario: c} sort: 22 toc: true = yaml =

Permissions policy

In addition to the default authorization policy, Vanadium offers another built-in authorization policy based on the commonly used idea of a permissions map. Such an authorizer allows a wide range of policies controllable via editing lists that are given to a server as flags - no need to modify code.

{{# helpers.info }}

Vanadium also supports the use of a completely custom policy - the procedure for building one is covered in the custom authorizer tutorial.

{{/ helpers.info }}

Permission maps define a list of blessings that should be given (or denied) access to an object. All methods on objects can have tags on them and the access list used for the method is selected based on that tag. This is inspired by Role Based Access Control.

To see how this works, assume Alice wants to let her family read and write to her service but confine her friends to read operations only, which don't change state.

To do so Alice defines two new tags, Reader and Writer, and adds them to the service VDL:

 cat - <<EOF >$V_TUT/src/fortune/ifc/fortune.vdl
package ifc

type MyTag string
const (
  Reader = MyTag("R")
  Writer = MyTag("W")
)

type Fortune interface {
  // Returns a random fortune.
  Get() (Fortune string | error) {Reader}
  // Adds a fortune to the set used by Get().
  Add(Fortune string) error {Writer}
}
EOF

VDLROOT=$V23_RELEASE/src/v.io/v23/vdlroot \
    VDLPATH=$V_TUT/src \
    $V_BIN/vdl generate --lang go $V_TUT/src/fortune/ifc
go build fortune/ifc

To exploit this a new authorizer is needed - one that honors permissions provided at the server command line:

 cat - <<EOF >$V_TUT/src/fortune/server/util/authorizer.go
package util

import (
  "bytes"
  "flag"
  "fortune/ifc"
  "v.io/v23/security"
  "v.io/v23/security/access"
  "v.io/v23/vdl"
)

var (
	perms = flag.String("perms", "",
      "JSON-encoded access.Permissions.")
)

func MakeAuthorizer() (authorizer security.Authorizer) {
  aMap, _ := access.ReadPermissions(
      bytes.NewBufferString(*perms))
  typ := vdl.TypeOf(ifc.Reader)
  authorizer, _ = access.PermissionsAuthorizer(aMap, typ)
  return
}
EOF

go install fortune/server

A specific permissions map

Now restart the server with a permissions map that allows family to read and write and friends to read only:

kill_tut_process TUT_PID_SERVER
$V_TUT/bin/server \
    --v23.credentials $V_TUT/cred/alice \
    --endpoint-file-name $V_TUT/server.txt \
    --perms '{"R": {"In": ["alice:family",
                           "alice:friend"]},
              "W": {"In": ["alice:family"]}}' &
TUT_PID_SERVER=$!

This takes the form of a map. The keys are the service method labels (R is an abbreviation for Reader, a label on Get), and the values are lists of blessing patterns. Principals with blessings matching a pattern in the ‘In’ list can make the call. An optional list called NotIn specifies exclusions from the In list (e.g., you might let in family, but exclude family:uncle).

Patterns are slash-separated strings that may optionally end in a $. The pattern alice:houseguest will be matched by the names alice:houseguest and its delegates alice:houseguest:bob, alice:houseguest:bob:spouse etc., but not by the name bob or alice:colleague or prefixes of the pattern like alice. On the other hand, the pattern alice:houseguest:$ would be matched exactly by the name alice:houseguest.

Impact on Bob and Carol

You can quickly confirm that Bob (already blessed as a friend) can read:

$V_TUT/bin/client \
    --v23.credentials $V_TUT/cred/bob \
    --server `cat $V_TUT/server.txt`

But Bob cannot write (he's not family):

$V_TUT/bin/client \
    --v23.credentials $V_TUT/cred/bob \
    --server `cat $V_TUT/server.txt` \
    --add 'Bob is awesome.'

Introduce Carol:

$V_BIN/principal create --overwrite $V_TUT/cred/carol carol

At this point no request from Carol will succeed because Carol is unknown to Alice.

Suppose Carol is Alice's sister and Alice gives Carol the blessing alice:family:sister:

$V_BIN/principal bless \
    --v23.credentials $V_TUT/cred/alice \
    --for=24h $V_TUT/cred/carol family:sister | \
        $V_BIN/principal set \
            --v23.credentials $V_TUT/cred/carol \
            forpeer - alice

Now that Carol is seen as part of the family she'll be able to invoke both Get and Add:

$V_TUT/bin/client \
    --v23.credentials $V_TUT/cred/carol \
    --server `cat $V_TUT/server.txt`

$V_TUT/bin/client \
    --v23.credentials $V_TUT/cred/carol \
    --server `cat $V_TUT/server.txt` \
    --add 'Eat kale.'

Alice's permissions policy allows Carol to Get and Add data to the server, but Bob can only Get

We're done with the server now.

kill_tut_process TUT_PID_SERVER

Summary

  • Vanadium provides a pre-built Permissions authorizer.

  • Service methods defined in a VDL file can be tagged with roles, e.g. Reader and Writer.

  • A server built with the Permissions authorizer accepts a flag mapping roles to lists of blessings.

  • A client blessed to a role is able to call the corresponding methods.