Identity-based authentication

User-defined roles can have a membership attribute which describes the set of documents that should have the role’s privileges. You can use the membership attribute to control which identities can perform actions on particular collections, as well as other restrictions.

A membership definition can include a predicate function which is evaluated whenever a query is made against the covered documents. The following example procedure demonstrates how to use a membership attribute to restrict data access.

This examples imagines a company database with two collections called Users and Customers. The Users collection contains documents with information about company employees, including name, role, and department. The Customers collection contains information about the company’s customers, including name, location, and year-to-date revenue generated. Different employees have different levels of access to the two collections based on their department and role.

Step 1: Create a new database

Navigate to the Fauna Dashboard and create a new database called company_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 Users and another one called Customers:

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

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[history_days:30 name:Users ref:{Users 0xc00028a240 0xc00028a240 <nil>} ts:1649797447170000] map[history_days:30 name:Customers ref:{Customers 0xc00028a360 0xc00028a360 <nil>} ts:1649797447170000]]
client.query([
  q.CreateCollection({ name: 'Users' }),
  q.CreateCollection({ name: 'Customers' }),
])
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Collection("Users"),
    ts: 1649797456750000,
    history_days: 30,
    name: 'Users'
  },
  {
    ref: Collection("Customers"),
    ts: 1649797456750000,
    history_days: 30,
    name: 'Customers'
  }
]
result = client.query([
  q.create_collection({ 'name': 'Users' }),
  q.create_collection({ 'name': 'Customers' })
])
print(result)
[{'ref': Ref(id=Users, collection=Ref(id=collections)), 'ts': 1650393167970000, 'history_days': 30, 'name': 'Users'}, {'ref': Ref(id=Customers, collection=Ref(id=collections)), 'ts': 1650393167970000, 'history_days': 30, 'name': 'Customers'}]
[
  CreateCollection({ name: 'Users' }),
  CreateCollection({ name: 'Customers' })
]
[
  {
    ref: Collection("Users"),
    ts: 1649797461320000,
    history_days: 30,
    name: 'Users'
  },
  {
    ref: Collection("Customers"),
    ts: 1649797461320000,
    history_days: 30,
    name: 'Customers'
  }
]
Query metrics:
  •    bytesIn:  105

  •   bytesOut:  282

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    2

  •  readBytes:  818

  • writeBytes:  662

  •  queryTime: 36ms

  •    retries:    0

Step 3: Create new users

The following example creates four documents in the Users collection, each with personal information and login credentials for each user.

try
{
    Value result = await client.Query(
        Arr(
            Create(
                Ref(Collection("Users"), "1"),
                Obj(
                    "data", Obj(
                        "name", "Donna Merrick",
                        "role", "manager",
                        "department", "HR"
                    ),
                    "credentials", Obj("password", "abc123")
                )
            ),
            Create(
                Ref(Collection("Users"), "2"),
                Obj(
                    "data", Obj(
                        "name", "John Morales",
                        "role", "recruiter",
                        "department", "HR"
                    ),
                    "credentials", Obj("password", "def123")
                )
            ),
            Create(
                Ref(Collection("Users"), "3"),
                Obj(
                    "data", Obj(
                        "name", "Sam Grant",
                        "role", "manager",
                        "department", "Data Analytics"
                    ),
                    "credentials", Obj("password", "ghi123")
                )
            ),
            Create(
                Ref(Collection("Users"), "4"),
                Obj(
                    "data", Obj(
                        "name", "Arlene Lee",
                        "role", "analyst",
                        "department", "Data Analytics"
                    ),
                    "credentials", Obj("password", "jkl123")
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
Arr(ObjectV(ref: RefV(id = "1", collection = RefV(id = "Users", collection = RefV(id = "collections"))),ts: LongV(1649894780890000),data: ObjectV(name: StringV(Donna Merrick),role: StringV(manager),department: StringV(HR))), ObjectV(ref: RefV(id = "2", collection = RefV(id = "Users", collection = RefV(id = "collections"))),ts: LongV(1649894780890000),data: ObjectV(name: StringV(John Morales),role: StringV(recruiter),department: StringV(HR))), ObjectV(ref: RefV(id = "3", collection = RefV(id = "Users", collection = RefV(id = "collections"))),ts: LongV(1649894780890000),data: ObjectV(name: StringV(Sam Grant),role: StringV(manager),department: StringV(Data Analytics))), ObjectV(ref: RefV(id = "4", collection = RefV(id = "Users", collection = RefV(id = "collections"))),ts: LongV(1649894780890000),data: ObjectV(name: StringV(Arlene Lee),role: StringV(analyst),department: StringV(Data Analytics))))
result, err := client.Query(
	f.Arr{
		f.Create(
			f.Ref(f.Collection("Users"), "1"),
			f.Obj{
				"data": f.Obj{
					"name": "Donna Merrick",
					"role": "manager",
					"department": "HR",
				},
				"credentials": f.Obj{ "password": "abc123" },
			},
		),
		f.Create(
			f.Ref(f.Collection("Users"), "2"),
			f.Obj{
				"data": f.Obj{
					"name": "John Morales",
					"role": "recruiter",
					"department": "HR",
				},
				"credentials": f.Obj{ "password": "def123" },
			},
		),
		f.Create(
			f.Ref(f.Collection("Users"), "3"),
			f.Obj{
				"data": f.Obj{
					"name": "Sam Grant",
					"role": "manager",
					"department": "Data Analytics",
				},
				"credentials": f.Obj{ "password": "ghi123" },
			},
		),
		f.Create(
			f.Ref(f.Collection("Users"), "4"),
			f.Obj{
				"data": f.Obj{
					"name": "Arlene Lee",
					"role": "analyst",
					"department": "Data Analytics",
				},
				"credentials": f.Obj{ "password": "def123" },
			},
		),
	},
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
[map[data:map[department:HR name:Donna Merrick role:manager] ref:{1 0xc000184570 0xc000184570 <nil>} ts:1649805254620000] map[data:map[department:HR name:John Morales role:recruiter] ref:{2 0xc000184780 0xc000184780 <nil>} ts:1649805254620000] map[data:map[department:Data Analytics name:Sam Grant role:manager] ref:{3 0xc000184990 0xc000184990 <nil>} ts:1649805254620000] map[data:map[department:Data Analytics name:Arlene Lee role:analyst] ref:{4 0xc000184ba0 0xc000184ba0 <nil>} ts:1649805254620000]]
client.query([
  q.Create(
    q.Ref(q.Collection('Users'), 1),
    {
      data: {
        name: 'Donna Merrick',
        role: 'manager',
        department: 'HR',
      },
      credentials: { password: 'abc123' },
    },
  ),
  q.Create(
    q.Ref(q.Collection('Users'), 2),
    {
      data: {
        name: 'John Morales',
        role: 'recruiter',
        department: 'HR',
      },
      credentials: { password: 'def123' },
    },
  ),
  q.Create(
    q.Ref(q.Collection('Users'), 3),
    {
      data: {
        name: 'Sam Grant',
        role: 'manager',
        department: 'Data Analytics',
      },
      credentials: { password: 'ghi123' },
    },
  ),
  q.Create(
    q.Ref(q.Collection('Users'), 4),
    {
      data: {
        name: 'Arlene Lee',
        role: 'analyst',
        department: 'Data Analytics',
      },
      credentials: { password: 'jkl123' },
    },
  ),
])
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
[
  {
    ref: Ref(Collection("Users"), "1"),
    ts: 1649808534450000,
    data: { name: 'Donna Merrick', role: 'manager', department: 'HR' }
  },
  {
    ref: Ref(Collection("Users"), "2"),
    ts: 1649808534450000,
    data: { name: 'John Morales', role: 'recruiter', department: 'HR' }
  },
  {
    ref: Ref(Collection("Users"), "3"),
    ts: 1649808534450000,
    data: {
      name: 'Sam Grant',
      role: 'manager',
      department: 'Data Analytics'
    }
  },
  {
    ref: Ref(Collection("Users"), "4"),
    ts: 1649808534450000,
    data: {
      name: 'Arlene Lee',
      role: 'analyst',
      department: 'Data Analytics'
    }
  }
]
result = client.query([
  q.create(
    q.ref(q.collection("Users"), 1),
    {
      "data": {
        "name": "Donna Merrick",
        "role": "manager",
        "department": "HR"
      },
      "credentials": { "password": "abc123" },
    }
  ),
  q.create(
    q.ref(q.collection("Users"), 2),
    {
      "data": {
        "name": "John Morales",
        "role": "recruiter",
        "department": "HR"
      },
      "credentials": { "password": "def123" },
    }
  ),
  q.create(
    q.ref(q.collection("Users"), 3),
    {
      "data": {
        "name": "Sam Grant",
        "role": "manager",
        "department": "Data Analytics"
      },
      "credentials": { "password": "ghi123" },
    }
  ),
  q.create(
    q.ref(q.collection("Users"), 4),
    {
      "data": {
        "name": "Arlene Lee",
        "role": "analyst",
        "department": "Data Analytics"
      },
      "credentials": { "password": "jkl123" },
    }
  ),
])
print(result)
[{'ref': Ref(id=1, collection=Ref(id=Users, collection=Ref(id=collections))), 'ts': 1650393168950000, 'data': {'name': 'Donna Merrick', 'role': 'manager', 'department': 'HR'}}, {'ref': Ref(id=2, collection=Ref(id=Users, collection=Ref(id=collections))), 'ts': 1650393168950000, 'data': {'name': 'John Morales', 'role': 'recruiter', 'department': 'HR'}}, {'ref': Ref(id=3, collection=Ref(id=Users, collection=Ref(id=collections))), 'ts': 1650393168950000, 'data': {'name': 'Sam Grant', 'role': 'manager', 'department': 'Data Analytics'}}, {'ref': Ref(id=4, collection=Ref(id=Users, collection=Ref(id=collections))), 'ts': 1650393168950000, 'data': {'name': 'Arlene Lee', 'role': 'analyst', 'department': 'Data Analytics'}}]
[
  Create(
    Ref(Collection("Users"), "1"),
    {
      data: {
        name: 'Donna Merrick',
        role: 'manager',
        department: 'HR'
      },
      credentials: { password: 'abc123' }
    }
  ),
  Create(
    Ref(Collection("Users"), "2"),
    {
      data: {
        name: 'John Morales',
        role: 'recruiter',
        department: 'HR'
      },
      credentials: { password: 'def123' }
    }
  ),
  Create(
    Ref(Collection("Users"), "3"),
    {
      data: {
        name: 'Sam Grant',
        role: 'manager',
        department: 'Data Analytics'
      },
      credentials: { password: 'ghi123' }
    }
  ),
  Create(
    Ref(Collection("Users"), "4"),
    {
      data: {
        name: 'Arlene Lee',
        role: 'analyst',
        department: 'Data Analytics'
      },
      credentials: { password: 'jkl123' }
    }
  )
]
[
  {
    ref: Ref(Collection("Users"), "1"),
    ts: 1649803776970000,
    data: { name: 'Donna Merrick', role: 'manager', department: 'HR' }
  },
  {
    ref: Ref(Collection("Users"), "2"),
    ts: 1649803776970000,
    data: { name: 'John Morales', role: 'recruiter', department: 'HR' }
  },
  {
    ref: Ref(Collection("Users"), "3"),
    ts: 1649803776970000,
    data: {
      name: 'Sam Grant',
      role: 'manager',
      department: 'Data Analytics'
    }
  },
  {
    ref: Ref(Collection("Users"), "4"),
    ts: 1649803776970000,
    data: {
      name: 'Arlene Lee',
      role: 'analyst',
      department: 'Data Analytics'
    }
  }
]
Query metrics:
  •    bytesIn:   803

  •   bytesOut:   820

  • computeOps:     1

  •    readOps:     0

  •   writeOps:     8

  •  readBytes:   248

  • writeBytes: 2,606

  •  queryTime:  28ms

  •    retries:     0

Step 4: Create customer data

The next example creates a document in the Customers collection.

try
{
    Value result = await client.Query(
        Create(Collection("Customers"),
            Obj(
                "data", Obj(
                    "name", "Acme Widgets, Inc.",
                    "location", "Hoboken, NJ",
                    "YTD_orders", 35
                )
            )
        )
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "328871582255546880", collection = RefV(id = "Customers", collection = RefV(id = "collections"))),ts: LongV(1649895231390000),data: ObjectV(name: StringV(Acme Widgets, Inc.),location: StringV(Hoboken, NJ),YTD_orders: LongV(35)))
result, err := client.Query(
	f.Create(f.Collection("Customers"),
		f.Obj{
			"data": f.Obj{
				"name": "Acme Widgets, Inc.",
				"location": "Hoboken, NJ",
				"YTD_orders": 35,
			},
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[data:map[YTD_orders:35 location:Hoboken, NJ name:Acme Widgets, Inc.] ref:{328778607104098816 0xc00009bb30 0xc00009bb30 <nil>} ts:1649806563380000]
client.query(
  q.Create(
    q.Collection('Customers'),
    {
      data: {
        name: 'Acme Widgets, Inc.',
        location: 'Hoboken, NJ',
        YTD_orders: 35,
      },
    }
  ),
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Ref(Collection("Customers"), "328780674072838656"),
  ts: 1649808534590000,
  data: {
    name: 'Acme Widgets, Inc.',
    location: 'Hoboken, NJ',
    YTD_orders: 35
  }
}
result = client.query(
  q.create(
    q.collection("Customers"),
    {
      "data": {
        "name": "Acme Widgets, Inc.",
        "location": "Hoboken, NJ",
        "YTD_orders": 35
      }
    }
  )
)
print(result)
{'ref': Ref(id=329393708633948672, collection=Ref(id=Customers, collection=Ref(id=collections))), 'ts': 1650393169910000, 'data': {'name': 'Acme Widgets, Inc.', 'location': 'Hoboken, NJ', 'YTD_orders': 35}}
Create(Collection("Customers"),
  {
    data: {
      name: 'Acme Widgets, Inc.',
      location: 'Hoboken, NJ',
      YTD_orders: 35 
    }
  }
)
{
  ref: Ref(Collection("Customers"), "328775685877268992"),
  ts: 1649803777480000,
  data: {
    name: 'Acme Widgets, Inc.',
    location: 'Hoboken, NJ',
    YTD_orders: 35
  }
}
Query metrics:
  •    bytesIn:  148

  •   bytesOut:  241

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:    0

  • writeBytes:  251

  •  queryTime: 18ms

  •    retries:    0

Step 5: Create roles

This is where we set up our data access rules. First, set up two roles which govern access to the Customers collection:

  • Any user who is a member of the Data Analytics department can perform read operations.

  • Users who are managers in the Data Analytics department can perform read, write, create, and delete operations.

The first role is called DA-reader. It covers documents in the Customers collection and contains a predicate function which checks to see if the caller belongs to the Data Analytics department.

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "DA-reader",
            "membership", Arr(
                Obj(
                    "resource", Collection("Customers"),
                    "predicate", Query(
                        Lambda(
                            Arr("ref"),
                            Equals(
                                "Data Analytics",
                                Select(
                                    Arr("data", "department"),
                                    Get(Var("ref"))
                                )
                            )
                        )
                    )
                )
            ),
            "privileges", Arr(
                Obj(
                    "resource", Collection("Customers"),
                    "actions", Obj(
                        "read", true
                    )
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "DA-reader", collection = RefV(id = "roles")),ts: LongV(1649895545340000),name: StringV(DA-reader),membership: Arr(ObjectV(resource: RefV(id = "Customers", collection = RefV(id = "collections")),predicate: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))),privileges: Arr(ObjectV(resource: RefV(id = "Customers", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole(
		f.Obj{
			"name": "DA-reader",
			"membership": f.Obj{
				"resource": f.Collection("Customers"),
				"predicate": f.Query(
					f.Lambda(
						f.Arr{"ref"},
						f.Equals(
							"Data Analytics",
							f.Select(
								f.Arr{"data", "department"},
								f.Get(f.Var("ref")),
							),
						),
					),
				),
			},
			"privileges": f.Obj{
				"resource": f.Collection("Customers"),
				"actions": f.Obj{"read": true},
			},
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[membership:map[predicate:{[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 114 101 102 34 93 44 34 101 120 112 114 34 58 123 34 101 113 117 97 108 115 34 58 91 34 68 97 116 97 32 65 110 97 108 121 116 105 99 115 34 44 123 34 115 101 108 101 99 116 34 58 91 34 100 97 116 97 34 44 34 100 101 112 97 114 116 109 101 110 116 34 93 44 34 102 114 111 109 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 102 34 125 125 125 93 125 125]} resource:{Customers 0xc00019c2d0 0xc00019c2d0 <nil>}] name:DA-reader privileges:map[actions:map[read:true] resource:{Customers 0xc00019c420 0xc00019c420 <nil>}] ref:{DA-reader 0xc00019c1b0 0xc00019c1b0 <nil>} ts:1649806708100000]
client.query(
  q.CreateRole({
    name: 'DA-reader',
    membership: [
      {
        resource: q.Collection('Customers'),
        predicate: q.Query(
          q.Lambda(
            ['ref'],
            q.Equals(
              'Data Analytics',
              q.Select(
                ['data', 'department'],
                q.Get(q.Var('ref'))
              )
            )
          )
        ),
      },
    ],
    privileges: [
      {
        resource: q.Collection('Customers'),
        actions: {
          read: true,
        },
      },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("DA-reader"),
  ts: 1659657764790000,
  name: 'DA-reader',
  membership: [
    {
      resource: Collection("Customers"),
      predicate: Query(Lambda(["ref"], Equals("Data Analytics", Select(["data", "department"], Get(Var("ref"))))))
    }
  ],
  privileges: [ { resource: Collection("Customers"), actions: { read: true } } ]
}
result = client.query(
  q.create_role({
    "name": "DA-reader",
    "membership": [
      {
        "resource": q.collection("Customers"),
        "predicate": q.query(
          q.lambda_(
            ["ref"],
            q.equals(
              "Data Analytics",
              q.select(
                ["data", "department"],
                q.get(q.var("ref"))
              )
            )
          )
        ),
      },
    ],
    "privileges": [
      {
        "resource": q.collection("Customers"),
        "actions": {
          "read": True
        },
      },
    ],
  })
)
print(result)
{'ref': Ref(id=DA-reader, collection=Ref(id=roles)), 'ts': 1650393170870000, 'name': 'DA-reader', 'membership': [{'resource': Ref(id=Customers, collection=Ref(id=collections)), 'predicate': Query({'api_version': '4', 'lambda': ['ref'], 'expr': {'equals': ['Data Analytics', {'select': ['data', 'department'], 'from': {'get': {'var': 'ref'}}}]}})}], 'privileges': [{'resource': Ref(id=Customers, collection=Ref(id=collections)), 'actions': {'read': True}}]}
CreateRole({
  name: "DA-reader",
  membership: [
    {
      resource: Collection("Customers"),
      predicate: 
        Query(
          Lambda(
            ["ref"],
            Equals("Data Analytics", Select(["data", "department"], Get(Var("ref"))))
          )
        )
    }
  ],
  privileges: [
    {
      resource: Collection("Customers"),
      actions: {
        read: true,
      }
    }
  ]
})
{
  ref: Role("DA-reader"),
  ts: 1649803777990000,
  name: 'DA-reader',
  membership: [
    {
      resource: Collection("Customers"),
      predicate: Query(Lambda(["ref"], Equals("Data Analytics", Select(["data", "department"], Get(Var("ref"))))))
    }
  ],
  privileges: [ { resource: Collection("Customers"), actions: { read: true } } ]
}
Query metrics:
  •    bytesIn:  354

  •   bytesOut:  507

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  119

  • writeBytes:  642

  •  queryTime: 15ms

  •    retries:    0

The second role is called DA-manager. It also covers documents in the Customers collection, and its predicate function checks to see if the caller is a manager in the Data Analytics department.

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "DA-manager",
            "membership", Arr(
                Obj(
                    "resource", Collection("Customers"),
                    "predicate", Query(
                        Lambda(
                            Arr("ref"),
                            And(
                                Equals(
                                    "Data Analytics",
                                    Select(
                                        Arr("data", "department"),
                                        Get(Var("ref"))
                                    )
                                ),
                                Equals(
                                    "manager",
                                    Select(
                                        Arr("data", "role"),
                                        Get(Var("ref"))
                                    )
                                )
                            )
                        )
                    )
                )
            ),
            "privileges", Arr(
                Obj(
                    "resource", Collection("Customers"),
                    "actions", Obj(
                        "read", true
                    )
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "DA-manager", collection = RefV(id = "roles")),ts: LongV(1649896118560000),name: StringV(DA-manager),membership: Arr(ObjectV(resource: RefV(id = "Customers", collection = RefV(id = "collections")),predicate: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))),privileges: Arr(ObjectV(resource: RefV(id = "Customers", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole(
		f.Obj{
			"name": "DA-manager",
			"membership": f.Obj{
				"resource": f.Collection("Customers"),
				"predicate": f.Query(
					f.Lambda(
						f.Arr{"ref"},
						f.And(
							f.Equals(
								"Data Analytics",
								f.Select(
									f.Arr{"data", "department"},
									f.Get(f.Var("ref")),
								),
							),
							f.Equals(
								"manager",
								f.Select(
									f.Arr{"data", "role"},
									f.Get(f.Var("ref")),
								),
							),
						),
					),
				),
			},
			"privileges": f.Obj{
				"resource": f.Collection("Customers"),
				"actions": f.Obj{
					"read": true,
					"write": true,
					"create": true,
					"delete": true,
				},
			},
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[membership:map[predicate:{[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 114 101 102 34 93 44 34 101 120 112 114 34 58 123 34 97 110 100 34 58 91 123 34 101 113 117 97 108 115 34 58 91 34 68 97 116 97 32 65 110 97 108 121 116 105 99 115 34 44 123 34 115 101 108 101 99 116 34 58 91 34 100 97 116 97 34 44 34 100 101 112 97 114 116 109 101 110 116 34 93 44 34 102 114 111 109 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 102 34 125 125 125 93 125 44 123 34 101 113 117 97 108 115 34 58 91 34 109 97 110 97 103 101 114 34 44 123 34 115 101 108 101 99 116 34 58 91 34 100 97 116 97 34 44 34 114 111 108 101 34 93 44 34 102 114 111 109 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 102 34 125 125 125 93 125 93 125 125]} resource:{Customers 0xc00019e3c0 0xc00019e3c0 <nil>}] name:DA-manager privileges:map[actions:map[create:true delete:true read:true write:true] resource:{Customers 0xc00019e510 0xc00019e510 <nil>}] ref:{DA-manager 0xc00019e2a0 0xc00019e2a0 <nil>} ts:1649807242590000]
client.query(
  q.CreateRole({
    name: 'DA-manager',
    membership: [
      {
        resource: q.Collection('Customers'),
        predicate: q.Query(
          q.Lambda(
            ['ref'],
            q.And(
              q.Equals(
                'manager',
                q.Select(
                  ['data', 'role'],
                  q.Get(q.Var('ref'))
                )
              ),
              q.Equals(
                'Data analytics',
                q.Select(
                  ['data', 'department'],
                  q.Get(q.Var('ref'))
                )
              )
            )
          )
        ),
      },
    ],
    privileges: [
      {
        resource: q.Collection('Customers'),
        actions: {
          read: true,
          write: true,
          create: true,
          delete: true,
        },
      },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("DA-manager"),
  ts: 1659657612120000,
  name: 'DA-manager',
  membership: [
    {
      resource: Collection("Customers"),
      predicate: Query(Lambda(["ref"], And(Equals("manager", Select(["data", "role"], Get(Var("ref")))), Equals("Data analytics", Select(["data", "department"], Get(Var("ref")))))))
    }
  ],
  privileges: [
    {
      resource: Collection("Customers"),
      actions: { read: true, write: true, create: true, delete: true }
    }
  ]
}
result = client.query(
  q.create_role({
    "name": "DA-manager",
    "membership": [
      {
        "resource": q.collection("Customers"),
        "predicate": q.query(
          q.lambda_(
            ["ref"],
            q.and_(
              q.equals(
                "Data Analytics",
                q.select(
                  ["data", "department"],
                  q.get(q.var("ref"))
                )
              ),
              q.equals(
                "manager",
                q.select(
                  ["data", "role"],
                  q.get(q.var("ref"))
                )
              )
            )
          )
        ),
      },
    ],
    "privileges": [
      {
        "resource": q.collection("Customers"),
        "actions": {
          "read": True,
          "write": True,
          "create": True,
          "delete": True
        },
      },
    ],
  })
)
print(result)
{'ref': Ref(id=DA-manager, collection=Ref(id=roles)), 'ts': 1650393171820000, 'name': 'DA-manager', 'membership': [{'resource': Ref(id=Customers, collection=Ref(id=collections)), 'predicate': Query({'api_version': '4', 'lambda': ['ref'], 'expr': {'and': [{'equals': ['Data Analytics', {'select': ['data', 'department'], 'from': {'get': {'var': 'ref'}}}]}, {'equals': ['manager', {'select': ['data', 'role'], 'from': {'get': {'var': 'ref'}}}]}]}})}], 'privileges': [{'resource': Ref(id=Customers, collection=Ref(id=collections)), 'actions': {'read': True, 'write': True, 'create': True, 'delete': True}}]}
CreateRole({
  name: "DA-manager",
  membership: [
    {
      resource: Collection("Customers"),
      predicate: 
        Query(
          Lambda(
            ["ref"],
            And(
              Equals("manager", Select(["data", "role"], Get(Var("ref")))),
              Equals("Data analytics", Select(["data", "department"], Get(Var("ref"))))
            )
          )
        )
    }
  ],
  privileges: [
    {
      resource: Collection("Customers"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: true,
      }
    }
  ]
})
{
  ref: Role("DA-manager"),
  ts: 1649803778490000,
  name: 'DA-manager',
  membership: [
    {
      resource: Collection("Customers"),
      predicate: Query(Lambda(["ref"], And(Equals("manager", Select(["data", "role"], Get(Var("ref")))), Equals("Data analytics", Select(["data", "department"], Get(Var("ref")))))))
    }
  ],
  privileges: [
    {
      resource: Collection("Customers"),
      actions: { read: true, write: true, create: true, delete: true }
    }
  ]
}
Query metrics:
  •    bytesIn:  485

  •   bytesOut:  639

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  162

  • writeBytes:  729

  •  queryTime: 15ms

  •    retries:    0

Next, set up a role to govern access to the Users collection. This one is simpler: only managers in the HR department can access the Users collection.

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "HR-manager",
            "membership", Arr(
                Obj(
                    "resource", Collection("Users"),
                    "predicate", Query(
                        Lambda(
                            Arr("ref"),
                            And(
                                Equals(
                                    "HR",
                                    Select(
                                        Arr("data", "department"),
                                        Get(Var("ref"))
                                    )
                                ),
                                Equals(
                                    "manager",
                                    Select(
                                        Arr("data", "role"),
                                        Get(Var("ref"))
                                    )
                                )
                            )
                        )
                    )
                )
            ),
            "privileges", Arr(
                Obj(
                    "resource", Collection("Users"),
                    "actions", Obj(
                        "read", true,
                        "write", true,
                        "create", true,
                        "delete", true
                    )
                )
            )
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "HR-manager", collection = RefV(id = "roles")),ts: LongV(1649896122590000),name: StringV(HR-manager),membership: Arr(ObjectV(resource: RefV(id = "Users", collection = RefV(id = "collections")),predicate: QueryV(System.Collections.Generic.Dictionary`2[System.String,FaunaDB.Query.Expr]))),privileges: Arr(ObjectV(resource: RefV(id = "Users", collection = RefV(id = "collections")),actions: ObjectV(read: BooleanV(True),write: BooleanV(True),create: BooleanV(True),delete: BooleanV(True)))))
result, err := client.Query(
	f.CreateRole(
		f.Obj{
			"name": "HR-manager",
			"membership": f.Obj{
				"resource": f.Collection("Users"),
				"predicate": f.Query(
					f.Lambda(
						f.Arr{"ref"},
						f.And(
							f.Equals(
								"HR",
								f.Select(
									f.Arr{"data", "department"},
									f.Get(f.Var("ref")),
								),
							),
							f.Equals(
								"manager",
								f.Select(
									f.Arr{"data", "role"},
									f.Get(f.Var("ref")),
								),
							),
						),
					),
				),
			},
			"privileges": f.Obj{
				"resource": f.Collection("Users"),
				"actions": f.Obj{
					"read": true,
					"write": true,
					"create": true,
					"delete": true,
				},
			},
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[membership:map[predicate:{[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 114 101 102 34 93 44 34 101 120 112 114 34 58 123 34 97 110 100 34 58 91 123 34 101 113 117 97 108 115 34 58 91 34 72 82 34 44 123 34 115 101 108 101 99 116 34 58 91 34 100 97 116 97 34 44 34 100 101 112 97 114 116 109 101 110 116 34 93 44 34 102 114 111 109 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 102 34 125 125 125 93 125 44 123 34 101 113 117 97 108 115 34 58 91 34 109 97 110 97 103 101 114 34 44 123 34 115 101 108 101 99 116 34 58 91 34 100 97 116 97 34 44 34 114 111 108 101 34 93 44 34 102 114 111 109 34 58 123 34 103 101 116 34 58 123 34 118 97 114 34 58 34 114 101 102 34 125 125 125 93 125 93 125 125]} resource:{Users 0xc0001963c0 0xc0001963c0 <nil>}] name:HR-manager privileges:map[actions:map[create:true delete:true read:true write:true] resource:{Users 0xc000196510 0xc000196510 <nil>}] ref:{HR-manager 0xc0001962a0 0xc0001962a0 <nil>} ts:1649807589810000]
client.query(
  q.CreateRole({
    name: 'HR-manager',
    membership: [
      {
        resource: q.Collection('Users'),
        predicate: q.Query(
          q.Lambda(
            ['ref'],
            q.And(
              q.Equals(
                'manager',
                q.Select(
                  ['data', 'role'],
                  q.Get(q.Var('ref'))
                )
              ),
              q.Equals(
                'HR',
                q.Select(
                  ['data', 'department'],
                  q.Get(q.Var('ref'))
                )
              )
            )
          )
        ),
      },
    ],
    privileges: [
      {
        resource: q.Collection('Users'),
        actions: {
          read: true,
          write: true,
          create: true,
          delete: true,
        },
      },
    ],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("HR-manager"),
  ts: 1659657612200000,
  name: 'HR-manager',
  membership: [
    {
      resource: Collection("Users"),
      predicate: Query(Lambda(["ref"], And(Equals("manager", Select(["data", "role"], Get(Var("ref")))), Equals("HR", Select(["data", "department"], Get(Var("ref")))))))
    }
  ],
  privileges: [
    {
      resource: Collection("Users"),
      actions: { read: true, write: true, create: true, delete: true }
    }
  ]
}
result = client.query(
  q.create_role({
    "name": "HR-manager",
    "membership": [
      {
        "resource": q.collection("Users"),
        "predicate": q.query(
          q.lambda_(
            ["ref"],
            q.and_(
              q.equals(
                "HR",
                q.select(
                  ["data", "department"],
                  q.get(q.var("ref"))
                )
              ),
              q.equals(
                "manager",
                q.select(
                  ["data", "role"],
                  q.get(q.var("ref"))
                )
              )
            )
          )
        ),
      },
    ],
    "privileges": [
      {
        "resource": q.collection("Users"),
        "actions": {
          "read": True,
          "write": True,
          "create": True,
          "delete": True
        },
      },
    ],
  })
)
print(result)
{'ref': Ref(id=HR-manager, collection=Ref(id=roles)), 'ts': 1650393172790000, 'name': 'HR-manager', 'membership': [{'resource': Ref(id=Users, collection=Ref(id=collections)), 'predicate': Query({'api_version': '4', 'lambda': ['ref'], 'expr': {'and': [{'equals': ['HR', {'select': ['data', 'department'], 'from': {'get': {'var': 'ref'}}}]}, {'equals': ['manager', {'select': ['data', 'role'], 'from': {'get': {'var': 'ref'}}}]}]}})}], 'privileges': [{'resource': Ref(id=Users, collection=Ref(id=collections)), 'actions': {'read': True, 'write': True, 'create': True, 'delete': True}}]}
CreateRole({
  name: "HR-manager",
  membership: [
    {
      resource: Collection("Users"),
      predicate: 
        Query(
          Lambda(
            ["ref"],
            And(
              Equals("manager", Select(["data", "role"], Get(Var("ref")))),
              Equals("HR", Select(["data", "department"], Get(Var("ref"))))
            )
          )
        )
    }
  ],
  privileges: [
    {
      resource: Collection("Users"),
      actions: {
        read: true,
        write: true,
        create: true,
        delete: true,
      }
    }
  ]
})
{
  ref: Role("HR-manager"),
  ts: 1649803778980000,
  name: 'HR-manager',
  membership: [
    {
      resource: Collection("Users"),
      predicate: Query(Lambda(["ref"], And(Equals("manager", Select(["data", "role"], Get(Var("ref")))), Equals("HR", Select(["data", "department"], Get(Var("ref")))))))
    }
  ],
  privileges: [
    {
      resource: Collection("Users"),
      actions: { read: true, write: true, create: true, delete: true }
    }
  ]
}
Query metrics:
  •    bytesIn:  465

  •   bytesOut:  619

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  116

  • writeBytes:  717

  •  queryTime: 13ms

  •    retries:    0

There is, however, a special property of Fauna’s ABAC implementation: a document can always read and modify itself, unless prevented by a higher-priority role definition. Under our current rules, a user such as Sam Grant, who is not a manager in the HR department, would be able to read and modify his own document in the Users collection (but no one else’s). To prevent this, we can create a role with no permissions on the Users collection and no predicate function, so it applies to all callers:

try
{
    Value result = await client.Query(
        CreateRole(Obj(
            "name", "HR-default",
            "membership", Arr(
                Obj("resource", Collection("Users"))
            ),
            "privileges", Arr()
        ))
    );
    Console.WriteLine(result);
}
catch (Exception e)
{
    Console.WriteLine($"ERROR: {e.Message}");
}
ObjectV(ref: RefV(id = "HR-default", collection = RefV(id = "roles")),ts: LongV(1649896126620000),name: StringV(HR-default),membership: Arr(ObjectV(resource: RefV(id = "Users", collection = RefV(id = "collections")))),privileges: Arr())
result, err := client.Query(
	f.CreateRole(
		f.Obj{
			"name": "HR-default",
			"membership": f.Obj{"resource": f.Collection("Users")},
			"privileges": f.Arr{},
		},
	),
)

if err != nil {
	fmt.Fprintln(os.Stderr, err)
} else {
	fmt.Println(result)
}
map[membership:map[resource:{Users 0xc0001845d0 0xc0001845d0 <nil>}] name:HR-default privileges:[] ref:{HR-default 0xc0001844b0 0xc0001844b0 <nil>} ts:1649807812980000]
client.query(
  q.CreateRole({
    name: 'HR-default',
    membership: [ { resource: q.Collection('Users') } ],
    privileges: [],
  })
)
.then((ret) => console.log(ret))
.catch((err) => console.error(
  'Error: [%s] %s: %s',
  err.name,
  err.message,
  err.errors()[0].description,
))
{
  ref: Role("HR-default"),
  ts: 1649885114250000,
  name: 'HR-default',
  membership: [ { resource: Collection("Users") } ],
  privileges: []
}
result = client.query(
  q.create_role({
    "name": "HR-default",
    "membership": [
      { "resource": q.collection("Customers") },
    ],
    "privileges": []
  })
)
print(result)
{'ref': Ref(id=HR-default, collection=Ref(id=roles)), 'ts': 1650393173730000, 'name': 'HR-default', 'membership': [{'resource': Ref(id=Customers, collection=Ref(id=collections))}], 'privileges': []}
CreateRole({
  name: "HR-default",
  membership: [
    {
      resource: Collection("Users"),
    },
  ],
  privileges: []
})
{
  ref: Role("HR-default"),
  ts: 1649803779480000,
  name: 'HR-default',
  membership: [ { resource: Collection("Users") } ],
  privileges: []
}
Query metrics:
  •    bytesIn:  126

  •   bytesOut:  239

  • computeOps:    1

  •    readOps:    0

  •   writeOps:    1

  •  readBytes:  158

  • writeBytes:  509

  •  queryTime: 16ms

  •    retries:    0

This role prevents all users from accessing the Users collection by default. Users who meet the criteria in the predicate function of the HR-manager role can still perform actions according to the privileges attribute of that role. For more information about ABAC behavior when multiple roles apply to the same collection, see overlapping roles.

Step 6: Test the roles

To test the roles, you can use the Login function to log in as any one of the existing users, then check to see that the user can only access the data in the collections as the roles allow.

The following example logs in with the password for John Morales:

ObjectV(ref: RefV(id = "328872525113065984", collection = RefV(id = "tokens")),ts: LongV(1649896130580000),instance: RefV(id = "2", collection = RefV(id = "Users", collection = RefV(id = "collections"))),secret: StringV(fnEEkGPRs8ACAASQY7pGUAYAQAbs9IAG99zraRTqLOwM2ZoGxjs))
map[instance:{2 0xc00007a660 0xc00007a660 <nil>} ref:{328780082127569408 0xc00007a4b0 0xc00007a4b0 <nil>} secret:fnEEkA--JHACAASQD7oWgAYAHWBLh3AgoNES_iRvgrRJWPC_yv0 ts:1649807970080000]
{
  ref: Ref(Tokens(), "328860973817397760"),
  ts: 1649885114400000,
  instance: Ref(Collection("Users"), "2"),
  secret: 'fnEEkFlQNOACAASQWU7fgAYAnd2xJuc12gScwCVR80zXrcw4EqE'
}
{'ref': Ref(id=329393713593713152, collection=Ref(id=tokens)), 'ts': 1650393174650000, 'instance': Ref(id=2, collection=Ref(id=Users, collection=Ref(id=collections))), 'secret': 'fnEEkj3WWgACAASSPdDv8AoAS0PjXY27oLkqMpzF5NjOqbdezrU'}
{
  ref: Ref(Tokens(), "328775688500806144"),
  ts: 1649803779990000,
  instance: Ref(Collection("Users"), "2"),
  secret: 'fnEEkAu_K-ACAASQC7uuwAYABpPNOFZ1S-MxQKGlFZcl_9QcRBg'
}
Query metrics:
  •    bytesIn:   91

  •   bytesOut:  291

  • computeOps:    1

  •    readOps:    1

  •   writeOps:    1

  •  readBytes:  214

  • writeBytes:  433

  •  queryTime: 20ms

  •    retries:    0

Once you have the secret returned by the Login function, you can use it to perform actions as the identity associated with it.

Summary

Creating custom roles with narrowly-defined privileges is the best way to maintain access control in your databases and client applications.

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!