Role-based authentication

The following procedure demonstrates setting up User-defined roles and assigning them to UDFs for better data security.

For purposes of illustration, this tutorial imagines an online service called Weather Data, in which users can read about and report on weather information around the world. The service has three kinds of users:

  • Readers, who can only read the available information.

  • Reporters, who can read from and write to the database.

  • Managers, who can create new users and also have read/write access to the database.

The service has three UDFs:

  • Get_weather_reports, which anyone can call.

  • Create_weather_report, which only Reporters can call.

  • Create_weather_user, which only Managers can call.

To follow along with this procedure, you need a Fauna account. For help with setting up a Fauna account, see the Dashboard quick start.

Step 1: Create a new database

Navigate to the Fauna Dashboard and create a new database called weather_data.

Once your database is created, you can interact with it either with a driver, with the Dashboard Shell, or with the fauna-shell command-line tool. All of the subsequent steps in this example provide FQL queries in each supported language, well as Fauna Query Language for use in the Dashboard Shell or fauna-shell reference.

Step 2: Create two new collections

Create a new collection called weather_reports and another one called weather_users:

try
{
    Value result = await client.Query(
        Arr(
            CreateCollection(Obj("name", "weather_reports")),
            CreateCollection(Obj("name", "weather_users"))
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "weather_reports", collection = RefV(id = "collections")),ts: LongV(1648443114870000),history_days: LongV(30),name: StringV(weather_reports)), ObjectV(ref: RefV(id = "weather_users", collection = RefV(id = "collections")),ts: LongV(1648443114870000),history_days: LongV(30),name: StringV(weather_users)))
result, err := client.Query(
	f.Arr{
		f.CreateCollection(f.Obj{"name": "weather_reports"}),
		f.CreateCollection(f.Obj{"name": "weather_users"}),
	})

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[history_days:30 name:weather_reports ref:{weather_reports 0x140002844b0 0x140002844b0 <nil>} ts:1648443222280000] map[history_days:30 name:weather_users ref:{weather_users 0x140002845d0 0x140002845d0 <nil>} ts:1648443222280000]]
client.query([
  q.CreateCollection({ name: 'weather_reports' }),
  q.CreateCollection({ name: 'weather_users' }),
])
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Collection("weather_reports"),
    ts: 1648443407640000,
    history_days: 30,
    name: 'weather_reports'
  },
  {
    ref: Collection("weather_users"),
    ts: 1648443407640000,
    history_days: 30,
    name: 'weather_users'
  }
]
result = client.query([
  q.create_collection({ 'name': 'weather_reports' }),
  q.create_collection({ 'name': 'weather_users' })
])
print(result)
[{'ref': Ref(id=weather_reports, collection=Ref(id=collections)), 'ts': 1650393153190000, 'history_days': 30, 'name': 'weather_reports'}, {'ref': Ref(id=weather_users, collection=Ref(id=collections)), 'ts': 1650393153190000, 'history_days': 30, 'name': 'weather_users'}]
[
  CreateCollection({ name: 'weather_reports' }),
  CreateCollection({ name: 'weather_users' })
]
[
  {
    ref: Collection("weather_reports"),
    ts: 1648443426630000,
    history_days: 30,
    name: 'weather_reports'
  },
  {
    ref: Collection("weather_users"),
    ts: 1648443426630000,
    history_days: 30,
    name: 'weather_users'
  }
]
Query metrics:
  •    bytesIn: 119

  •   bytesOut: 310

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   2

  •  readBytes: 880

  • writeBytes: 704

  •  queryTime: 9ms

  •    retries:   0

Step 3: Create UDFs

We need three UDFs to perform the functions of this application. The first one is called Get_weather_reports, and it uses the Documents function to retrieve all the records in the weather_reports collection.

We haven’t created the required roles yet, so these UDFs are created without the role definitions. Once the roles have been created, we can update them to add the role definitions.
try
{
    Value result = await client.Query(
        CreateFunction(
            Obj(
                "name", "Get_weather_reports",
                "body", Query(
                    Lambda(
                        Arr(),
                        Map(
                            Paginate(Documents(Collection("weather_reports"))),
                            Lambda("report", Get(Var("report")))
                        )
                    )
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Get_weather_reports", collection = RefV(id = "functions")),ts: LongV(1648443122440000),name: StringV(Get_weather_reports),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))
result, err := client.Query(
	f.CreateFunction(f.Obj{
		"name": "Get_weather_reports",
		"body": f.Query(
			f.Lambda(
				f.Arr{},
				f.Map(f.Paginate(f.Documents(f.Collection("weather_reports"))),
				f.Lambda("report", f.Get(f.Var("report"))))))}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 93 44 34 101 120 112 114 34 58 123 34 109 97 112 34 58 123 34 108 97 109 98 100 97 34 58 34 114 101 112 111 114 116 34 44 34 101 120 112 114 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 112 111 114 116 34 125 125 125 44 34 99 111 108 108 101 99 116 105 111 110 34 58 123 34 112 97 103 105 110 97 116 101 34 58 123 34 100 111 99 117 109 101 110 116 115 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 114 101 112 111 114 116 115 34 125 125 125 125 125]} name:Get_weather_reports ref:{Get_weather_reports 0x140002044b0 0x140002044b0 <nil>} ts:1648443223020000]
client.query(
  q.CreateFunction({
    name: 'Get_weather_reports',
    body: q.Query(
      q.Lambda(
        [],
        q.Map(
          q.Paginate(q.Documents(q.Collection('weather_reports'))),
          q.Lambda('report', q.Get(q.Var('report')))
        )
      )
    ),
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Function("Get_weather_reports"),
  ts: 1648443407730000,
  name: 'Get_weather_reports',
  body: Query(Lambda([], Map(Paginate(Documents(Collection("weather_reports"))), Lambda("report", Get(Var("report"))))))
}
result = client.query(
  q.create_function({
    'name': 'Get_weather_reports',
    'body': q.query(
      q.lambda_(
        [],
        q.map_(
          q.lambda_("report", q.get(q.var("report"))),
          q.paginate(q.documents(q.collection("weather_reports")))
        )
      )
    ),
  }) 
)
print(result)
{'ref': Ref(id=Get_weather_reports, collection=Ref(id=functions)), 'ts': 1650393154100000, 'name': 'Get_weather_reports', 'body': Query({'api_version': '4', 'lambda': [], 'expr': {'map': {'lambda': 'report', 'expr': {'get': {'var': 'report'}}}, 'collection': {'paginate': {'documents': {'collection': 'weather_reports'}}}}})}
CreateFunction({
  name: 'Get_weather_reports',
  body: Query(
    Lambda(
      [],
      Map(
        Paginate(Documents(Collection("weather_reports"))),
        Lambda("report", Get(Var("report")))
      )
    )
  ),
})
{
  ref: Function("Get_weather_reports"),
  ts: 1648443426910000,
  name: 'Get_weather_reports',
  body: Query(Lambda([], Map(Paginate(Documents(Collection("weather_reports"))), Lambda("report", Get(Var("report"))))))
}
Query metrics:
  •    bytesIn: 232

  •   bytesOut: 340

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   1

  •  readBytes:  32

  • writeBytes: 482

  •  queryTime: 7ms

  •    retries:   0

The next function, Create_weather_report, allows Reporters to create new documents in the weather_reports collection.

try
{
    Value result = await client.Query(
        CreateFunction(
            Obj(
                "name", "Create_weather_report",
                "body", Query(
                    Lambda(
                        Arr("date", "city", "country", "temperature"),
                        Create(Collection("weather_reports"), Obj(
                            "data", Obj(
                                "date", Var("date"),
                                "city", Var("city"),
                                "country", Var("country"),
                                "temperature", Var("temperature")
                            )
                        ))
                    )
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Create_weather_report", collection = RefV(id = "functions")),ts: LongV(1648443129880000),name: StringV(Create_weather_report),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))
result, err := client.Query(
	f.CreateFunction(f.Obj{
		"name": "Create_weather_report",
		"body": f.Query(
			f.Lambda(
				f.Arr{"date", "city", "country", "temperature"},
				f.Create(
					f.Collection("weather_reports"),
					f.Obj{
						"data": f.Obj{
							"date": f.Var("date"),
							"city": f.Var("city"),
							"country": f.Var("country"),
							"temperature": f.Var("temperature"),
						},
					},
				),
			),
		),
	}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 34 100 97 116 101 34 44 34 99 105 116 121 34 44 34 99 111 117 110 116 114 121 34 44 34 116 101 109 112 101 114 97 116 117 114 101 34 93 44 34 101 120 112 114 34 58 123 34 99 114 101 97 116 101 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 114 101 112 111 114 116 115 34 125 44 34 112 97 114 97 109 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 100 97 116 97 34 58 123 34 111 98 106 101 99 116 34 58 123 34 99 105 116 121 34 58 123 34 118 97 114 34 58 34 99 105 116 121 34 125 44 34 99 111 117 110 116 114 121 34 58 123 34 118 97 114 34 58 34 99 111 117 110 116 114 121 34 125 44 34 100 97 116 101 34 58 123 34 118 97 114 34 58 34 100 97 116 101 34 125 44 34 116 101 109 112 101 114 97 116 117 114 101 34 58 123 34 118 97 114 34 58 34 116 101 109 112 101 114 97 116 117 114 101 34 125 125 125 125 125 125 125]} name:Create_weather_report ref:{Create_weather_report 0x14000194240 0x14000194240 <nil>} ts:1648443223760000]
client.query(
  q.CreateFunction({
    name: 'Create_weather_report',
    body: q.Query(
      q.Lambda(
        ['date', 'city', 'country', 'temperature'],
        q.Create(
          q.Collection('weather_reports'),
          {
            data: {
              date: q.Var('date'),
              city: q.Var('city'),
              country: q.Var('country'),
              temperature: q.Var('temperature'),
            },
          },
        )
      )
    ),
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Function("Create_weather_report"),
  ts: 1648443407820000,
  name: 'Create_weather_report',
  body: Query(Lambda(["date", "city", "country", "temperature"], Create(Collection("weather_reports"), {"data": {"date": Var("date"), "city": Var("city"), "country": Var("country"), "temperature": Var("temperature")}})))
}
result = client.query(
  q.create_function({
    'name': 'Create_weather_report',
    'body': q.query(
      q.lambda_(
        ["date", "city", "country", "temperature"],
        q.create(
          q.collection("weather_reports"),
          {
            'data': {
              'date': q.var("date"),
              'city': q.var("city"),
              'countr': q.var("country"),
              'temperature': q.var("temperature")
            }
          }
        )
      )
    )
  })
)

print(result)
{'ref': Ref(id=Create_weather_report, collection=Ref(id=functions)), 'ts': 1650393155020000, 'name': 'Create_weather_report', 'body': Query({'api_version': '4', 'lambda': ['date', 'city', 'country', 'temperature'], 'expr': {'create': {'collection': 'weather_reports'}, 'params': {'object': {'data': {'object': {'date': {'var': 'date'}, 'city': {'var': 'city'}, 'countr': {'var': 'country'}, 'temperature': {'var': 'temperature'}}}}}}})}
CreateFunction({
  name: 'Create_weather_report',
  body: Query(
    Lambda(
      ["date", "city", "country", "temperature"],
      Create(Collection("weather_reports"), {
        data: {
          date: Var("date"),
          city: Var("city"),
          country: Var("country"),
          temperature: Var("temperature")
        }
      })
    )
  ),
})
{
  ref: Function("Create_weather_report"),
  ts: 1648443427200000,
  name: 'Create_weather_report',
  body: Query(Lambda(["date", "city", "country", "temperature"], Create(Collection("weather_reports"), {"data": {"date": Var("date"), "city": Var("city"), "country": Var("country"), "temperature": Var("temperature")}})))
}
Query metrics:
  •    bytesIn: 332

  •   bytesOut: 442

  • computeOps:   1

  •    readOps:   0

  •   writeOps:   1

  •  readBytes:  34

  • writeBytes: 562

  •  queryTime: 9ms

  •    retries:   0

The last UDF, Create_weather_user, allows Managers to create new documents in the weather_users collection. Note that the password field is passed via the credentials parameter, which causes the password string to be stored as a one-way cryptographic hash that is subsequently used to authenticate the user. For more information, see Credentials.

try
{
    Value result = await client.Query(
        CreateFunction(
            Obj(
                "name", "Create_weather_user",
                "body", Query(
                    Lambda(
                        Arr("name", "email", "password"),
                        Create(Collection("weather_users"), Obj(
                            "credentials", Obj("password", Var("password")),
                            "data", Obj(
                                "name", Var("name"),
                                "email", Var("email")
                            )
                        ))
                    )
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Create_weather_user", collection = RefV(id = "functions")),ts: LongV(1648443137530000),name: StringV(Create_weather_user),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))
result, err := client.Query(
	f.CreateFunction(f.Obj{
		"name": "Create_weather_user",
		"body": f.Query(
			f.Lambda(
				f.Arr{"name", "email", "password"},
				f.Create(
					f.Collection("weather_users"),
					f.Obj{
						"credentials": f.Obj{
							"password": f.Var("password"),
						},
						"data": f.Obj{
							"email": f.Var("email"),
							"name": f.Var("name"),
						},
					},
				),
			),
		),
	}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 34 110 97 109 101 34 44 34 101 109 97 105 108 34 44 34 112 97 115 115 119 111 114 100 34 93 44 34 101 120 112 114 34 58 123 34 99 114 101 97 116 101 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 117 115 101 114 115 34 125 44 34 112 97 114 97 109 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 99 114 101 100 101 110 116 105 97 108 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 112 97 115 115 119 111 114 100 34 58 123 34 118 97 114 34 58 34 112 97 115 115 119 111 114 100 34 125 125 125 44 34 100 97 116 97 34 58 123 34 111 98 106 101 99 116 34 58 123 34 101 109 97 105 108 34 58 123 34 118 97 114 34 58 34 101 109 97 105 108 34 125 44 34 110 97 109 101 34 58 123 34 118 97 114 34 58 34 110 97 109 101 34 125 125 125 125 125 125 125]} name:Create_weather_user ref:{Create_weather_user 0x1400007bec0 0x1400007bec0 <nil>} ts:1648443224500000]
client.query(
  q.CreateFunction({
    name: 'Create_weather_user',
    body: q.Query(
      q.Lambda(
        ['name', 'email', 'password'],
        q.Create(
          q.Collection('weather_users'),
          {
            credentials: { password: q.Var('password') },
            data: {
              name: q.Var('name'),
              email: q.Var('email'),
            },
          },
        )
      )
    ),
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Function("Create_weather_user"),
  ts: 1648443407900000,
  name: 'Create_weather_user',
  body: Query(Lambda(["name", "email", "password"], Create(Collection("weather_users"), {"credentials": {"password": Var("password")}, "data": {"name": Var("name"), "email": Var("email")}})))
}
result = client.query(
  q.create_function({
    'name': 'Create_weather_user',
    'body': q.query(
      q.lambda_(
        ["name", "email", "password"],
        q.create(q.collection("weather_users"), {
          'credentials': { 'password': q.var('password') },
          'data': {
            'name': q.var("name"),
            'email': q.var("email")
          }
        })
      )
    )
  })
)

print(result)
{'ref': Ref(id=Create_weather_user, collection=Ref(id=functions)), 'ts': 1650393155940000, 'name': 'Create_weather_user', 'body': Query({'api_version': '4', 'lambda': ['name', 'email', 'password'], 'expr': {'create': {'collection': 'weather_users'}, 'params': {'object': {'credentials': {'object': {'password': {'var': 'password'}}}, 'data': {'object': {'name': {'var': 'name'}, 'email': {'var': 'email'}}}}}}})}
CreateFunction({
  name: 'Create_weather_user',
  body: Query(
    Lambda(
      ["name", "email", "password"],
      Create(Collection("weather_users"), {
        credentials: { password: Var("password") },
        data: { email: Var("email"), name: Var("name") }
      })
    )
  ),
})
{
  ref: Function("Create_weather_user"),
  ts: 1648443427490000,
  name: 'Create_weather_user',
  body: Query(Lambda(["name", "email", "password"], Create(Collection("weather_users"), {"credentials": {"password": Var("password")}, "data": {"email": Var("email"), "name": Var("name")}})))
}
Query metrics:
  •    bytesIn:  311

  •   bytesOut:  419

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:   32

  • writeBytes:  540

  •  queryTime: 16ms

  •    retries:    0

Step 4: Create new user-defined roles

The next step is to create roles with narrowly-defined privileges to execute the UDFs.

First, create a new role called Reader with the read privilege on the weather_reports collection and the call privilege on the Get_weather_reports function:

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "Reader",
            "privileges", Arr(
                Obj(
                    "resource", Collection("weather_reports"),
                    "actions", Obj("read", true)
                ),
                Obj(
                    "resource", Function("Get_weather_reports"),
                    "actions", Obj("call", true)
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Reader", collection = RefV(id = "roles")),ts: LongV(1648443144890000),name: StringV(Reader),privileges: Arr(ObjectV(resource: RefV(id = "weather_reports", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True))), ObjectV(resource: RefV(id = "Get_weather_reports", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole( f.Obj{
		"name": "Reader",
		"privileges": f.Arr{
			f.Obj{
				"resource": f.Collection("weather_reports"),
				"actions": f.Obj{ "read": true },
			},
			f.Obj{
				"resource": f.Function("Get_weather_reports"),
				"actions": f.Obj{ "call": true },
			},
		},
	}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[name:Reader privileges:[map[actions:map[read:true] resource:{weather_reports 0x140001a6090 0x140001a6090 <nil>}] map[actions:map[call:true] resource:{Get_weather_reports 0x140001a61e0 0x140001a61e0 <nil>}]] ref:{Reader 0x14000115f20 0x14000115f20 <nil>} ts:1648443225240000]
client.query(
  q.CreateRole({
    name: 'Reader',
    privileges: [
      {
        resource: q.Collection('weather_reports'),
        actions: { read: true },
      },
      {
        resource: q.Function('Get_weather_reports'),
        actions: { call: true },
      },
    ],
  }),
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("Reader"),
  ts: 1648443407990000,
  name: 'Reader',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    }
  ]
}
result = client.query(
  q.create_role({
    "name": "Reader",
    "privileges": [
      {
        "resource": q.collection("weather_reports"),
        "actions": { "read": True }
      },
      {
        "resource": q.function("Get_weather_reports"),
        "actions": { "call": True }
      }
    ]
  })
)

print(result)
{'ref': Ref(id=Reader, collection=Ref(id=roles)), 'ts': 1650393156910000, 'name': 'Reader', 'privileges': [{'resource': Ref(id=weather_reports, collection=Ref(id=collections)), 'actions': {'read': True}}, {'resource': Ref(id=Get_weather_reports, collection=Ref(id=functions)), 'actions': {'call': True}}]}
CreateRole({
  name: "Reader",
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    }
  ]
})
{
  ref: Role("Reader"),
  ts: 1648443427780000,
  name: 'Reader',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    }
  ]
}
Query metrics:
  •    bytesIn:  245

  •   bytesOut:  365

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  296

  • writeBytes:  405

  •  queryTime: 43ms

  •    retries:    0

Next, create a role named Reporter. This role can create new documents in weather_reports, read from weather_reports, and call the Create_weather_report and Get_weather_reports functions.

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "Reporter",
            "privileges", Arr(
                Obj(
                    "resource", Collection("weather_reports"),
                    "actions", Obj("read", true, "create", true)
                ),
                Obj(
                    "resource", Function("Get_weather_reports"),
                    "actions", Obj("call", true)
                ),
                Obj(
                    "resource", Function("Create_weather_report"),
                    "actions", Obj("call", true)
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Reporter", collection = RefV(id = "roles")),ts: LongV(1648443152520000),name: StringV(Reporter),privileges: Arr(ObjectV(resource: RefV(id = "weather_reports", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True),create: BooleanV(True))), ObjectV(resource: RefV(id = "Get_weather_reports", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True))), ObjectV(resource: RefV(id = "Create_weather_report", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole( f.Obj{
		"name": "Reporter",
		"privileges": f.Arr{
			f.Obj{
				"resource": f.Collection("weather_reports"),
				"actions": f.Obj{ "read": true, "create": true },
			},
			f.Obj{
				"resource": f.Function("Get_weather_reports"),
				"actions": f.Obj{ "call": true },
			},
			f.Obj{
				"resource": f.Function("Create_weather_report"),
				"actions": f.Obj{ "call": true },
			},
		},
	}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[name:Reporter privileges:[map[actions:map[create:true read:true] resource:{weather_reports 0x14000194390 0x14000194390 <nil>}] map[actions:map[call:true] resource:{Get_weather_reports 0x140001944e0 0x140001944e0 <nil>}] map[actions:map[call:true] resource:{Create_weather_report 0x14000194630 0x14000194630 <nil>}]] ref:{Reporter 0x14000194240 0x14000194240 <nil>} ts:1648443226010000]
client.query(
  q.CreateRole({
    name: 'Reporter',
    privileges: [
      {
        resource: q.Collection('weather_reports'),
        actions: { read: true, create: true },
      },
      {
        resource: q.Function('Get_weather_reports'),
        actions: { call: true },
      },
      {
        resource: q.Function('Create_weather_report'),
        actions: { call: true },
      },
    ],
  }),
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("Reporter"),
  ts: 1648443408080000,
  name: 'Reporter',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_report"),
      actions: { call: true }
    }
  ]
}
result = client.query(
  q.create_role({
    "name": "Reporter",
    "privileges": [
      {
        "resource": q.collection("weather_reports"),
        "actions": { "read": True, "create": True }
      },
      {
        "resource": q.function("Get_weather_reports"),
        "actions": { "call": True }
      },
      {
        "resource": q.function("Create_weather_report"),
        "actions": { "call": True }
      }
    ]
  })
)

print(result)
{'ref': Ref(id=Reporter, collection=Ref(id=roles)), 'ts': 1650393157870000, 'name': 'Reporter', 'privileges': [{'resource': Ref(id=weather_reports, collection=Ref(id=collections)), 'actions': {'read': True, 'create': True}}, {'resource': Ref(id=Get_weather_reports, collection=Ref(id=functions)), 'actions': {'call': True}}, {'resource': Ref(id=Create_weather_report, collection=Ref(id=functions)), 'actions': {'call': True}}]}
CreateRole({
  name: "Reporter",
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_report"),
      actions: { call: true }
    }
  ]
})
{
  ref: Role("Reporter"),
  ts: 1648443428070000,
  name: 'Reporter',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_report"),
      actions: { call: true }
    }
  ]
}
Query metrics:
  •    bytesIn:  357

  •   bytesOut:  501

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  572

  • writeBytes:  456

  •  queryTime: 20ms

  •    retries:    0

Lastly, create a role named Manager. This role has read and create privileges on the weather_users collection, read privileges on weather_reports, and can call Create_weather_user and Get_weather_reports.

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "Manager",
            "privileges", Arr(
                Obj(
                    "resource", Collection("weather_reports"),
                    "actions", Obj("read", true)
                ),
                Obj(
                    "resource", Collection("weather_users"),
                    "actions", Obj("read", true, "create", true)
                ),
                Obj(
                    "resource", Function("Get_weather_reports"),
                    "actions", Obj("call", true)
                ),
                Obj(
                    "resource", Function("Create_weather_report"),
                    "actions", Obj("call", true)
                ),
                Obj(
                    "resource", Function("Create_weather_user"),
                    "actions", Obj("call", true)
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "Manager", collection = RefV(id = "roles")),ts: LongV(1648443160230000),name: StringV(Manager),privileges: Arr(ObjectV(resource: RefV(id = "weather_reports", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True))), ObjectV(resource: RefV(id = "weather_users", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True),create: BooleanV(True))), ObjectV(resource: RefV(id = "Get_weather_reports", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True))), ObjectV(resource: RefV(id = "Create_weather_report", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True))), ObjectV(resource: RefV(id = "Create_weather_user", collection = RefV(id = "functions")),actions: ObjectV(call: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole( f.Obj{
		"name": "Manager",
		"privileges": f.Arr{
			f.Obj{
				"resource": f.Collection("weather_reports"),
				"actions": f.Obj{ "read": true },
			},
			f.Obj{
				"resource": f.Collection("weather_users"),
				"actions": f.Obj{"read": true, "create": true},
			},
			f.Obj{
				"resource": f.Function("Get_weather_reports"),
				"actions": f.Obj{ "read": true },
			},
			f.Obj{
				"resource": f.Function("Create_weather_report"),
				"actions": f.Obj{ "read": true },
			},
			f.Obj{
				"resource": f.Function("Create_weather_user"),
				"actions": f.Obj{ "call": true },
			},
		},
	}))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
map[name:Manager privileges:[map[actions:map[read:true] resource:{weather_reports 0x140001703c0 0x140001703c0 <nil>}] map[actions:map[create:true read:true] resource:{weather_users 0x14000170510 0x14000170510 <nil>}] map[actions:map[read:true] resource:{Get_weather_reports 0x14000170660 0x14000170660 <nil>}] map[actions:map[read:true] resource:{Create_weather_report 0x140001707b0 0x140001707b0 <nil>}] map[actions:map[call:true] resource:{Create_weather_user 0x14000170900 0x14000170900 <nil>}]] ref:{Manager 0x14000170270 0x14000170270 <nil>} ts:1648443226760000]
client.query(
  q.CreateRole({
    name: 'Manager',
    privileges: [
      {
        resource: q.Collection('weather_reports'),
        actions: { read: true },
      },
      {
        resource: q.Collection('weather_users'),
        actions: { read: true, create: true },
      },
      {
        resource: q.Function('Get_weather_reports'),
        actions: { call: true },
      },
      {
        resource: q.Function('Create_weather_report'),
        actions: { call: true },
      },
      {
        resource: q.Function('Create_weather_user'),
        actions: { call: true },
      },
    ],
  }),
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("Manager"),
  ts: 1648443408170000,
  name: 'Manager',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Collection("weather_users"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_report"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_user"),
      actions: { call: true }
    }
  ]
}
result = client.query(
  q.create_role({
    "name": "Manager",
    "privileges": [
      {
        "resource": q.collection("weather_reports"),
        "actions": { "read": True }
      },
      {
        "resource": q.collection("weather_users"),
        "actions": { "read": True, "create": True }
      },
      {
        "resource": q.function("Get_weather_reports"),
        "actions": { "call": True }
      },
      {
        "resource": q.function("Create_weather_report"),
        "actions": { "call": True }
      },
      {
        "resource": q.function("Create_weather_user"),
        "actions": { "call": True }
      }
    ]
  })
)

print(result)
{'ref': Ref(id=Manager, collection=Ref(id=roles)), 'ts': 1650393158840000, 'name': 'Manager', 'privileges': [{'resource': Ref(id=weather_reports, collection=Ref(id=collections)), 'actions': {'read': True}}, {'resource': Ref(id=weather_users, collection=Ref(id=collections)), 'actions': {'read': True, 'create': True}}, {'resource': Ref(id=Get_weather_reports, collection=Ref(id=functions)), 'actions': {'call': True}}, {'resource': Ref(id=Create_weather_report, collection=Ref(id=functions)), 'actions': {'call': True}}, {'resource': Ref(id=Create_weather_user, collection=Ref(id=functions)), 'actions': {'call': True}}]}
CreateRole({
  name: "Manager",
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Collection("weather_users"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_user"),
      actions: { call: true }
    }
  ]
})
{
  ref: Role("Manager"),
  ts: 1648443428350000,
  name: 'Manager',
  privileges: [
    {
      resource: Collection("weather_reports"),
      actions: { read: true }
    },
    {
      resource: Collection("weather_users"),
      actions: { read: true, create: true }
    },
    {
      resource: Function("Get_weather_reports"),
      actions: { call: true }
    },
    {
      resource: Function("Create_weather_user"),
      actions: { call: true }
    }
  ]
}
Query metrics:
  •    bytesIn:  540

  •   bytesOut:  727

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  904

  • writeBytes:  527

  •  queryTime: 18ms

  •    retries:    0

Now your roles are all established, and you can assign them to the UDFs you created earlier.

Step 5: Assign roles to UDFs

Use the Update function to update your UDFs to have associated roles.

The following query:

  • Updates the Get_weather_reports function to have the Reader role.

  • Updates the Create_weather_report function to have the Reporter role.

  • Updates the Create_weather_user function to have the Manager role.

try
{
    Value result = await client.Query(
        Arr(
            Update(
                Function("Get_weather_reports"),
                Obj("role", Role("Reader"))
            ),
            Update(
                Function("Create_weather_report"),
                Obj("role", Role("Reporter"))
            ),
            Update(
                Function("Create_weather_user"),
                Obj("role", Role("Manager"))
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "Get_weather_reports", collection = RefV(id = "functions")),ts: LongV(1648443167610000),name: StringV(Get_weather_reports),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]),role: RefV(id = "Reader", collection = RefV(id = "roles"))), ObjectV(ref: RefV(id = "Create_weather_report", collection = RefV(id = "functions")),ts: LongV(1648443167610000),name: StringV(Create_weather_report),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]),role: RefV(id = "Reporter", collection = RefV(id = "roles"))), ObjectV(ref: RefV(id = "Create_weather_user", collection = RefV(id = "functions")),ts: LongV(1648443167610000),name: StringV(Create_weather_user),body: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]),role: RefV(id = "Manager", collection = RefV(id = "roles"))))
result, err := client.Query(
	f.Arr{
		f.Update(
			f.Function("Get_weather_reports"),
			f.Obj{"role": f.Role("Reader")},
		),
		f.Update(
			f.Function("Create_weather_report"),
			f.Obj{"role": f.Role("Reporter")},
		),
		f.Update(
			f.Function("Create_weather_user"),
			f.Obj{"role": f.Role("Manager")},
		),
	},
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 93 44 34 101 120 112 114 34 58 123 34 109 97 112 34 58 123 34 108 97 109 98 100 97 34 58 34 114 101 112 111 114 116 34 44 34 101 120 112 114 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 112 111 114 116 34 125 125 125 44 34 99 111 108 108 101 99 116 105 111 110 34 58 123 34 112 97 103 105 110 97 116 101 34 58 123 34 100 111 99 117 109 101 110 116 115 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 114 101 112 111 114 116 115 34 125 125 125 125 125]} name:Get_weather_reports ref:{Get_weather_reports 0x14000182240 0x14000182240 <nil>} role:{Reader 0x14000182330 0x14000182330 <nil>} ts:1648443227560000] map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 34 100 97 116 101 34 44 34 99 105 116 121 34 44 34 99 111 117 110 116 114 121 34 44 34 116 101 109 112 101 114 97 116 117 114 101 34 93 44 34 101 120 112 114 34 58 123 34 99 114 101 97 116 101 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 114 101 112 111 114 116 115 34 125 44 34 112 97 114 97 109 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 100 97 116 97 34 58 123 34 111 98 106 101 99 116 34 58 123 34 99 105 116 121 34 58 123 34 118 97 114 34 58 34 99 105 116 121 34 125 44 34 99 111 117 110 116 114 121 34 58 123 34 118 97 114 34 58 34 99 111 117 110 116 114 121 34 125 44 34 100 97 116 101 34 58 123 34 118 97 114 34 58 34 100 97 116 101 34 125 44 34 116 101 109 112 101 114 97 116 117 114 101 34 58 123 34 118 97 114 34 58 34 116 101 109 112 101 114 97 116 117 114 101 34 125 125 125 125 125 125 125]} name:Create_weather_report ref:{Create_weather_report 0x14000182450 0x14000182450 <nil>} role:{Reporter 0x14000182540 0x14000182540 <nil>} ts:1648443227560000] map[body:{[123 34 97 112 105 95 118 101 114 115 105 111 110 34 58 34 52 34 44 34 108 97 109 98 100 97 34 58 91 34 110 97 109 101 34 44 34 101 109 97 105 108 34 44 34 112 97 115 115 119 111 114 100 34 93 44 34 101 120 112 114 34 58 123 34 99 114 101 97 116 101 34 58 123 34 99 111 108 108 101 99 116 105 111 110 34 58 34 119 101 97 116 104 101 114 95 117 115 101 114 115 34 125 44 34 112 97 114 97 109 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 99 114 101 100 101 110 116 105 97 108 115 34 58 123 34 111 98 106 101 99 116 34 58 123 34 112 97 115 115 119 111 114 100 34 58 123 34 118 97 114 34 58 34 112 97 115 115 119 111 114 100 34 125 125 125 44 34 100 97 116 97 34 58 123 34 111 98 106 101 99 116 34 58 123 34 101 109 97 105 108 34 58 123 34 118 97 114 34 58 34 101 109 97 105 108 34 125 44 34 110 97 109 101 34 58 123 34 118 97 114 34 58 34 110 97 109 101 34 125 125 125 125 125 125 125]} name:Create_weather_user ref:{Create_weather_user 0x14000182660 0x14000182660 <nil>} role:{Manager 0x14000182750 0x14000182750 <nil>} ts:1648443227560000]]
client.query([
  q.Update(
    q.Function('Get_weather_reports'),
    { role: q.Role('Reader') },
  ),
  q.Update(
    q.Function('Create_weather_report'),
    { role: q.Role('Reporter') },
  ),
  q.Update(
    q.Function('Create_weather_user'),
    { role: q.Role('Manager') },
  ),
])
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Function("Get_weather_reports"),
    ts: 1648443408270000,
    name: 'Get_weather_reports',
    body: Query(Lambda([], Map(Paginate(Documents(Collection("weather_reports"))), Lambda("report", Get(Var("report")))))),
    role: Role("Reader")
  },
  {
    ref: Function("Create_weather_report"),
    ts: 1648443408270000,
    name: 'Create_weather_report',
    body: Query(Lambda(["date", "city", "country", "temperature"], Create(Collection("weather_reports"), {"data": {"date": Var("date"), "city": Var("city"), "country": Var("country"), "temperature": Var("temperature")}}))),
    role: Role("Reporter")
  },
  {
    ref: Function("Create_weather_user"),
    ts: 1648443408270000,
    name: 'Create_weather_user',
    body: Query(Lambda(["name", "email", "password"], Create(Collection("weather_users"), {"credentials": {"password": Var("password")}, "data": {"name": Var("name"), "email": Var("email")}}))),
    role: Role("Manager")
  }
]
result = client.query([
  q.update(
    q.function('Get_weather_reports'),
    { 'role': q.role('Reader') }
  ),
  q.update(
    q.function('Create_weather_report'),
    { 'role': q.role('Reporter') }
  ),
  q.update(
    q.function('Create_weather_user'),
    { 'role': q.role('Manager') }
  )
])
print(result)
[{'ref': Ref(id=Get_weather_reports, collection=Ref(id=functions)), 'ts': 1650393159790000, 'name': 'Get_weather_reports', 'body': Query({'api_version': '4', 'lambda': [], 'expr': {'map': {'lambda': 'report', 'expr': {'get': {'var': 'report'}}}, 'collection': {'paginate': {'documents': {'collection': 'weather_reports'}}}}}), 'role': Ref(id=Reader, collection=Ref(id=roles))}, {'ref': Ref(id=Create_weather_report, collection=Ref(id=functions)), 'ts': 1650393159790000, 'name': 'Create_weather_report', 'body': Query({'api_version': '4', 'lambda': ['date', 'city', 'country', 'temperature'], 'expr': {'create': {'collection': 'weather_reports'}, 'params': {'object': {'data': {'object': {'date': {'var': 'date'}, 'city': {'var': 'city'}, 'countr': {'var': 'country'}, 'temperature': {'var': 'temperature'}}}}}}}), 'role': Ref(id=Reporter, collection=Ref(id=roles))}, {'ref': Ref(id=Create_weather_user, collection=Ref(id=functions)), 'ts': 1650393159790000, 'name': 'Create_weather_user', 'body': Query({'api_version': '4', 'lambda': ['name', 'email', 'password'], 'expr': {'create': {'collection': 'weather_users'}, 'params': {'object': {'credentials': {'object': {'password': {'var': 'password'}}}, 'data': {'object': {'name': {'var': 'name'}, 'email': {'var': 'email'}}}}}}}), 'role': Ref(id=Manager, collection=Ref(id=roles))}]
[
  Update(
    Function("Get_weather_reports"),
    { role: Role("Reader") }
  ),
  Update(
    Function("Create_weather_report"),
    { role: Role("Reporter") }
  ),
  Update(
    Function("Create_weather_user"),
    { role: Role("Manager") }
  )
]
[
  {
    ref: Function("Get_weather_reports"),
    ts: 1648443428630000,
    name: 'Get_weather_reports',
    body: Query(Lambda([], Map(Paginate(Documents(Collection("weather_reports"))), Lambda("report", Get(Var("report")))))),
    role: Role("Reader")
  },
  {
    ref: Function("Create_weather_report"),
    ts: 1648443428630000,
    name: 'Create_weather_report',
    body: Query(Lambda(["date", "city", "country", "temperature"], Create(Collection("weather_reports"), {"data": {"date": Var("date"), "city": Var("city"), "country": Var("country"), "temperature": Var("temperature")}}))),
    role: Role("Reporter")
  },
  {
    ref: Function("Create_weather_user"),
    ts: 1648443428630000,
    name: 'Create_weather_user',
    body: Query(Lambda(["name", "email", "password"], Create(Collection("weather_users"), {"credentials": {"password": Var("password")}, "data": {"email": Var("email"), "name": Var("name")}}))),
    role: Role("Manager")
  }
]
Query metrics:
  •    bytesIn:   285

  •   bytesOut: 1,389

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     3

  •  readBytes: 1,336

  • writeBytes:   833

  •  queryTime:   8ms

  •    retries:     0

Step 6: Create API keys

If you are following along using the Dashboard Shell, you can skip ahead to Step 7: Create documents with the UDFs.

Client applications which access your Fauna database with the secret from a key or a token. A secret is a password-equivalent that connects the application to a specific database using role-based privileges.

Each key that you create should be assigned a role with sufficient privileges to do its intended job, and no more. Tokens have no implicit privileges, and require creation of custom roles to grant privileges.

The following query creates three keys, having the roles we need for this example:

try
{
    Value result = await client.Query(
        Arr(
            CreateKey(Obj("role", Role("Reader"))),
            CreateKey(Obj("role", Role("Reporter"))),
            CreateKey(Obj("role", Role("Manager")))
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "327348990760714752", collection = RefV(id = "keys")),ts: LongV(1648443175000000),role: RefV(id = "Reader", collection = RefV(id = "roles")),secret: StringV(fnAEivosN-ACANgL7PSGqx3mT7z-DUB9tfd3SzkS),hashed_secret: StringV($2a$05$iajWOHPtAPkgtM4O2V6nA.w.ldSvcSN2nSapQPfkjYzwG.YLE9Rwu)), ObjectV(ref: RefV(id = "327348990760715776", collection = RefV(id = "keys")),ts: LongV(1648443175000000),role: RefV(id = "Reporter", collection = RefV(id = "roles")),secret: StringV(fnAEivosN-AGAKKvjNw4zvdtyaM-5nL_qY-BCvXl),hashed_secret: StringV($2a$05$/hPakXnawaCDarKjV84hyeLU1RWJvQsmcrvyBNr3U.e2GPJVP6J6O)), ObjectV(ref: RefV(id = "327348990760716800", collection = RefV(id = "keys")),ts: LongV(1648443175000000),role: RefV(id = "Manager", collection = RefV(id = "roles")),secret: StringV(fnAEivosN-AKAD5rbRkfykhcHqFXVCa3BiU4Ok9X),hashed_secret: StringV($2a$05$NArb7c0ZhAwOTb3p6DNPx.bTWbwVTrFLluy7NXh9g4Gz9aj8.uARq)))
result, err := client.Query(
	f.Arr{
		f.CreateKey(f.Obj{"role": f.Role("Reader")}),
		f.CreateKey(f.Obj{"role": f.Role("Reporter")}),
		f.CreateKey(f.Obj{"role": f.Role("Manager")}),
	})

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[hashed_secret:$2a$05$NrfAh.7sKdyzg.xyUZzOuuf7aw5jkpsHsDcxCGtmszs2tJkLYBfj6 ref:{327349046662398464 0x140000964b0 0x140000964b0 <nil>} role:{Reader 0x140000965a0 0x140000965a0 <nil>} secret:fnAEivo5O-ACAKn8PuQjO6wKXT6NQDfyjQ_iw8uh ts:1648443228310000] map[hashed_secret:$2a$05$LMztJ7WTEGFy.PUKBOxeF.YkmgSqSV.N8XgjfHImCxdOZV39BgiFG ref:{327349046662399488 0x140000966c0 0x140000966c0 <nil>} role:{Reporter 0x140000967b0 0x140000967b0 <nil>} secret:fnAEivo5O-AGACLnWko4jtVFNzWt_k3DPgZyFG81 ts:1648443228310000] map[hashed_secret:$2a$05$o61DDIbNMbG5rY6qwO34aOQnzhTKzO3ut9QKlJzNaxFIvXBZdoba. ref:{327349046662400512 0x140000968d0 0x140000968d0 <nil>} role:{Manager 0x140000969c0 0x140000969c0 <nil>} secret:fnAEivo5O-AKAM9QAOXF84X8AEGA2K6qCtzsNlxT ts:1648443228310000]]
client.query([
  q.CreateKey({ role: q.Role('Reader') }),
  q.CreateKey({ role: q.Role('Reporter') }),
  q.CreateKey({ role: q.Role('Manager') }),
])
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Ref(Keys(), "327349235456410112"),
    ts: 1648443408360000,
    role: Role("Reader"),
    secret: 'fnAEivplMOACAKHNeV3sp9pUdhP9nmfdPTZXWH1h',
    hashed_secret: '$2a$05$AohJh7Ua5clOhKZovdB.duGilrGxbp/aoObITcyZ5kWtJeEpe4K72'
  },
  {
    ref: Ref(Keys(), "327349235456411136"),
    ts: 1648443408360000,
    role: Role("Reporter"),
    secret: 'fnAEivplMOAGAJlL_c2sU2tYOAjLDtwvjlOV7GM8',
    hashed_secret: '$2a$05$yXeH6zRO9b.k/dEduiYK2eBHLPZ0y5e/41fTPT5JTOy8wVKIZCstm'
  },
  {
    ref: Ref(Keys(), "327349235456412160"),
    ts: 1648443408360000,
    role: Role("Manager"),
    secret: 'fnAEivplMOAKAPYFCqxcsC58_ebtJ_KminGgC9f1',
    hashed_secret: '$2a$05$1BJbh7l1IqjgFcwhd4dqdu7YP6tiw4ujVmjv7skGrDNAk8Z/vyFN2'
  }
]
result = client.query([
  q.create_key({ 'role': q.role('Reader') }),
  q.create_key({ 'role': q.role('Reader') }),
  q.create_key({ 'role': q.role('Manager') })
])
print(result)
[{'ref': Ref(id=329393699011166720, collection=Ref(id=keys)), 'ts': 1650393160750000, 'role': Ref(id=Reader, collection=Ref(id=roles)), 'secret': 'fnAEkj3S9NACAHbH-9RtYhorGj2Hz7x8fDjEVkF1', 'hashed_secret': '$2a$05$T7JjOxbVvWQY.ztf2dIphuAdDy.6.C4nogXJM3ehlqD41WButk8HK'}, {'ref': Ref(id=329393699011167744, collection=Ref(id=keys)), 'ts': 1650393160750000, 'role': Ref(id=Reader, collection=Ref(id=roles)), 'secret': 'fnAEkj3S9NAGAM8LmmYzIVtrTtqe9-f0ggIc-8RB', 'hashed_secret': '$2a$05$1QN0lYoF36asyMJt.LTgbeqb4Ih4pOw0UpnACLfHwt3zyFraWe/1W'}, {'ref': Ref(id=329393699011168768, collection=Ref(id=keys)), 'ts': 1650393160750000, 'role': Ref(id=Manager, collection=Ref(id=roles)), 'secret': 'fnAEkj3S9NAKAEqtdkUMHU1QFAUhQmTGGSsm2ZiQ', 'hashed_secret': '$2a$05$xKYiSMbWo0nRVyaFdsY2Tum7jYeVDe9Q.peFFHOXBeakGwGNuG5x2'}]
[
  CreateKey({ role: Role("Reader") }),
  CreateKey({ role: Role("Reporter") }),
  CreateKey({ role: Role("Manager") })
]
[
  {
    ref: Ref(Keys(), "327349257024569856"),
    ts: 1648443428920000,
    role: Role("Reader"),
    secret: 'fnAEivpqNnACAEYLR13MyfbKJCtIE89XzF-uVouT',
    hashed_secret: '$2a$05$92XljLnZx617a.1OtwGOM.qiDvCqlX9rBPCsvKCchDgs2SUblJXhO'
  },
  {
    ref: Ref(Keys(), "327349257024570880"),
    ts: 1648443428920000,
    role: Role("Reporter"),
    secret: 'fnAEivpqNnAGANdBiR5wMXWfDA7srJboP56W9BJ3',
    hashed_secret: '$2a$05$7h8izslky8S2R7J5k4GkwO2nQy60YjABf/ZZQGV.xd9mPx3AtEyDG'
  },
  {
    ref: Ref(Keys(), "327349257024571904"),
    ts: 1648443428920000,
    role: Role("Manager"),
    secret: 'fnAEivpqNnAKAKvpBgWBqozEZ-MUmtysS2ds8Qzi',
    hashed_secret: '$2a$05$IC5MLu9d.shgWt0Mzfe/5uJ5u1myYtj4qUhMI5mFaf.BRtiMqE1I.'
  }
]
Query metrics:
  •    bytesIn:   163

  •   bytesOut:   926

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     3

  •  readBytes:   668

  • writeBytes: 1,017

  •  queryTime:  17ms

  •    retries:     0

A key’s secret is only displayed once, so be sure to save it in a safe place.

If you’d like to do the same in the Fauna Dashboard:

  1. Click the SECURITY link in the left-side navigation.

  2. Click the NEW KEY link.

  3. Select an appropriate role from the ROLE dropdown menu.

  4. Give the key a name which describes its intended function.

  5. Click SAVE.

Step 7: Create documents with the UDFs

So far, neither of the two collections have any documents. Now you can use your UDFs to add some documents.

Create a weather user document

In the Dashboard

  1. Click SHELL in the left-side navigation.

  2. Click the RUN AS button near the bottom of the screen. Select Manager from the dropdown menu.

  3. Enter the following query and click the RUN QUERY AS button:

    Call(
      Function("Create_weather_user"),
      "Alice Smith",
      "alice@example.com",
      "mypass123"
    )
    {
      ref: Ref(Collection("weather_users"), "327349258408690176"),
      ts: 1648443430240000,
      data: { email: 'alice@example.com', name: 'Alice Smith' }
    }
    Query metrics:
    •    bytesIn:   103

    •   bytesOut:   225

    • computeOps:     1

    •    readOps:     0

    •   writeOps:     2

    •  readBytes:    48

    • writeBytes:   650

    •  queryTime: 658ms

    •    retries:     0

Note that if you change the RUN AS dropdown menu to Reader and run the query again it fails, because the Reader role does not have the necessary privilege to run the Create_weather_user UDF.

Using a driver

try
{
    Value result = await client.Query(
        Call(
            Function("Create_weather_user"),
            "Alice Smith",
            "alice@example.com",
            "mypass123"
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "327349007072363008", collection = RefV(id = "weather_users", collection = RefV(id = "collections"))),ts: LongV(1648443190550000),data: ObjectV(name: StringV(Alice Smith),email: StringV(alice@example.com)))
result, err := client.Query(
	f.Call(
		f.Function("Create_weather_user"),
		"Alice Smith",
		"alice@example.com",
		"mypass123",
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:map[email:alice@example.com name:Alice Smith] ref:{327349048923128320 0x1400009c300 0x1400009c300 <nil>} ts:1648443230460000]
client.query(
  q.Call(
    q.Function('Create_weather_user'),
    'Alice Smith',
    'alice@example.com',
    'mypass123'
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s\n%o',
  err.name,
  err.message,
  err.errors()[0].description,
  err,
))
{
  ref: Ref(Collection("weather_users"), "327349236387545600"),
  ts: 1648443409240000,
  data: { name: 'Alice Smith', email: 'alice@example.com' }
}
result = client.query(
  q.call(
    q.function("Create_weather_user"),
    "Alice Smith",
    "alice@example.com",
    "mypass123"
  )
)
print(result)
{'ref': Ref(id=329393701651481088, collection=Ref(id=weather_users, collection=Ref(id=collections))), 'ts': 1650393163260000, 'data': {'name': 'Alice Smith', 'email': 'alice@example.com'}}
Call(
  Function("Create_weather_user"),
  "Alice Smith",
  "alice@example.com",
  "mypass123"
)
{
  ref: Ref(Collection("weather_users"), "327349258408690176"),
  ts: 1648443430240000,
  data: { email: 'alice@example.com', name: 'Alice Smith' }
}
Query metrics:
  •    bytesIn:   103

  •   bytesOut:   225

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     2

  •  readBytes:    48

  • writeBytes:   650

  •  queryTime: 658ms

  •    retries:     0

Create a weather report

In the Dashboard

  1. If the Shell screen is not in view, click SHELL in the left-side navigation.

  2. Change the RUN AS dropdown menu to Reporter and run the following query:

    Call(
      Function("Create_weather_report"),
      "2022-02-24",
      "Paris",
      "France",
      "42 degrees F"
    )
    {
      ref: Ref(Collection("weather_reports"), "327349259058807296"),
      ts: 1648443430860000,
      data: {
        date: '2022-02-24',
        city: 'Paris',
        country: 'France',
        temperature: '42 degrees F'
      }
    }
    Query metrics:
    •    bytesIn:  104

    •   bytesOut:  261

    • computeOps:    1

    •    readOps:    0

    •   writeOps:    1

    •  readBytes:    0

    • writeBytes:  260

    •  queryTime: 28ms

    •    retries:    0

Using a driver

try
{
    Value result = await client.Query(
        Call(
            Function("Create_weather_report"),
            "2022-02-24",
            "Paris",
            "France",
            "42 degrees F"
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "327349022696145408", collection = RefV(id = "weather_reports", collection = RefV(id = "collections"))),ts: LongV(1648443205450000),data: ObjectV(date: StringV(2022-02-24),city: StringV(Paris),country: StringV(France),temperature: StringV(42 degrees F)))
result, err := client.Query(
	f.Call(
		f.Function("Create_weather_report"),
		"2022-02-24",
		"Paris",
		"France",
		"42 degrees F",
	))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:map[city:Paris country:France date:2022-02-24 temperature:42 degrees F] ref:{327349050505429504 0x14000115b60 0x14000115b60 <nil>} ts:1648443231970000]
client.query(
  q.Call(
    q.Function('Create_weather_report'),
    '2022-02-24',
    'Paris',
    'France',
    '42 degrees F',
  )
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Ref(Collection("weather_reports"), "327349236618232320"),
  ts: 1648443409460000,
  data: {
    date: '2022-02-24',
    city: 'Paris',
    country: 'France',
    temperature: '42 degrees F'
  }
}
result = client.query(
  q.call(
    q.function("Create_weather_report"),
    "2022-02-24",
    "Paris",
    "France",
    "42 degrees F"
  )
)
print(result)
{'ref': Ref(id=329393703663698432, collection=Ref(id=weather_reports, collection=Ref(id=collections))), 'ts': 1650393165170000, 'data': {'date': '2022-02-24', 'city': 'Paris', 'countr': 'France', 'temperature': '42 degrees F'}}
Call(
  Function("Create_weather_report"),
  "2022-02-24",
  "Paris",
  "France",
  "42 degrees F"
)
{
  ref: Ref(Collection("weather_reports"), "327349259058807296"),
  ts: 1648443430860000,
  data: {
    date: '2022-02-24',
    city: 'Paris',
    country: 'France',
    temperature: '42 degrees F'
  }
}
Query metrics:
  •    bytesIn:  104

  •   bytesOut:  261

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:    0

  • writeBytes:  260

  •  queryTime: 28ms

  •    retries:    0

Step 8: Fetch existing weather reports

In the Dashboard

  1. If the Shell screen is not in view, click SHELL in the left-side navigation.

  2. Change the RUN AS dropdown menu to Reader and run the following query:

    try
    {
        Value result = await client.Query(
            Call("Get_weather_reports")
        );
        Console.WriteLine(result);
    }
    catch (Exception e)
    {
        Console.WriteLine($"ERROR: {e.Message}");
    }
    ObjectV(data: Arr(ObjectV(ref: RefV(id = "327349022696145408", collection = RefV(id = "weather_reports", collection = RefV(id = "collections"))),ts: LongV(1648443205450000),data: ObjectV(date: StringV(2022-02-24),city: StringV(Paris),country: StringV(France),temperature: StringV(42 degrees F)))))
    result, err := client.Query(
    	f.Call("Get_weather_reports"))
    
    if err != nil {
    	fmt.Fprintln(os.Stderr, err)
    } else {
    	fmt.Println(result)
    }
    map[data:[map[data:map[city:Paris country:France date:2022-02-24 temperature:42 degrees F] ref:{327349050505429504 0x1400007b800 0x1400007b800 <nil>} ts:1648443231970000]]]
    client.query(
      q.Call('Get_weather_reports')
    )
    .then((ret) => console.log(ret))
    .catch((err) => console.error(
      'Error: [%s] %s: %s',
      err.name,
      err.message,
      err.errors()[0].description,
    ))
    {
      data: [
        {
          ref: Ref(Collection("weather_reports"), "339108339609764352"),
          ts: 1659657763970000,
          data: {
            date: '2022-02-24',
            city: 'Paris',
            country: 'France',
            temperature: '42 degrees F'
          }
        }
      ]
    }
    result = client.query(
      q.call("Get_weather_reports")
    )
    print(result)
    {'data': [{'ref': Ref(id=329393703663698432, collection=Ref(id=weather_reports, collection=Ref(id=collections))), 'ts': 1650393165170000, 'data': {'date': '2022-02-24', 'city': 'Paris', 'countr': 'France', 'temperature': '42 degrees F'}}]}
    Call("Get_weather_reports")
    {
      data: [
        {
          ref: Ref(Collection("weather_reports"), "327349259058807296"),
          ts: 1648443430860000,
          data: {
            date: '2022-02-24',
            city: 'Paris',
            country: 'France',
            temperature: '42 degrees F'
          }
        }
      ]
    }
    Query metrics:
    •    bytesIn:   45

    •   bytesOut:  272

    • computeOps:    1

    •    readOps:    9

    •   writeOps:    0

    •  readBytes:  374

    • writeBytes:    0

    •  queryTime: 22ms

    •    retries:    0

You can also run this query as either of the other roles, because they all have the Call privilege for it.

Using a driver

try
{
    Value result = await client.Query(
        Call("Get_weather_reports")
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(data: Arr(ObjectV(ref: RefV(id = "327349022696145408", collection = RefV(id = "weather_reports", collection = RefV(id = "collections"))),ts: LongV(1648443205450000),data: ObjectV(date: StringV(2022-02-24),city: StringV(Paris),country: StringV(France),temperature: StringV(42 degrees F)))))
result, err := client.Query(
	f.Call("Get_weather_reports"))

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:[map[data:map[city:Paris country:France date:2022-02-24 temperature:42 degrees F] ref:{327349050505429504 0x1400007b800 0x1400007b800 <nil>} ts:1648443231970000]]]
client.query(
  q.Call('Get_weather_reports')
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  data: [
    {
      ref: Ref(Collection("weather_reports"), "339108339609764352"),
      ts: 1659657763970000,
      data: {
        date: '2022-02-24',
        city: 'Paris',
        country: 'France',
        temperature: '42 degrees F'
      }
    }
  ]
}
result = client.query(
  q.call("Get_weather_reports")
)
print(result)
{'data': [{'ref': Ref(id=329393703663698432, collection=Ref(id=weather_reports, collection=Ref(id=collections))), 'ts': 1650393165170000, 'data': {'date': '2022-02-24', 'city': 'Paris', 'countr': 'France', 'temperature': '42 degrees F'}}]}
Call("Get_weather_reports")
{
  data: [
    {
      ref: Ref(Collection("weather_reports"), "327349259058807296"),
      ts: 1648443430860000,
      data: {
        date: '2022-02-24',
        city: 'Paris',
        country: 'France',
        temperature: '42 degrees F'
      }
    }
  ]
}
Query metrics:
  •    bytesIn:   45

  •   bytesOut:  272

  • computeOps:    1

  •    readOps:    9

  •   writeOps:    0

  •  readBytes:  374

  • writeBytes:    0

  •  queryTime: 22ms

  •    retries:    0

Is this article helpful? 

Tell Fauna how the article can be improved:
Visit Fauna's forums or email docs@fauna.com

Thank you for your feedback!