View Source Bonfire.Boundaries (Bonfire v0.9.10-classic-beta.156)

Bonfire's boundaries framework provides a flexible way to control user access to specific actions and data. It ensures that users can only see and do what they're authorized to.

You can create custom groups of contacts (circles) and grant them specific permissions to interact with you and your content. They can help you take control of your online presence and ensure that your data is shared only with the people you want.

Boundaries are limits that you set for yourself or others to define what you're comfortable with. These limits can be physical, like curtains or doors; digital, like sharing settings on social media; in writing, like codes of conduct; emotional, like feeling comfortable to take time for self-care; or mental, like choosing what you pay attention to. In Bonfire, boundaries can help limit the type of interactions that others may have with you or things you post. Boundaries are important because they help you protect yourself, maintain your autonomy, and communicate your needs and expectations clearly.

Glossary

TermDefinition
Subject or UserAn individual who interacts with the system.
CircleA categorization method for users, allowing users to group other users (e.g., colleagues, friends).
VerbAn action that a user can perform (e.g., read, reply).
PermissionA value indicating whether an action is allowed (true), denied (false), or nil.
GrantLinks a user or circle with a verb and permission.
ACLAccess Control List, a collection of grants. Also called "boundary" or "boundary preset" in the app.
ControlledLinks an object to one or more ACLs, to determine access based on the grants.
RoleA group of verbs linked to a permission.

Users and Circles

Circles are a way of categorizing users. Each user can have their own set of circles to categorize other users. Circles allow a user to group work colleagues differently from friends for example, and to allow different interactions for users in each circle or limit content visibility on a per-item basis.

Verbs

Verbs represent actions users can perform, such as reading a post or replying to a message. Each verb has a unique ID and are defined in configuration.

Permissions

A permission is a decision about whether the action may be performed or not. There are 3 possible values:

  • true: yes, the action is allowed
  • false: no, the action is explicitly denied (i.e. never permit)
  • null/nil: unknown, the action isn't explicitly allowed (defaults to not allowed)

Grants

A Grant links a subject (user or circle) with a Verb and a permission. It defines the access rights for a specific user or circle in relation to a particular action.

ACLs

An Acl is a list of Grants used to define access permissions for objects.

Because a user could be in more than one circle and each circle may have a different permission, when a user attempts an action on an object, the system combines all relevant grants to determine the final permission. This combination prioritizes permissions as follows: false > true > nil, resulting in:

inputinputresult
nilnilnil
niltruetrue
nilfalsefalse
trueniltrue
truetruetrue
truefalsefalse
falsenilfalse
falsetruefalse
falsefalsefalse

In simpler terms, a final permission is granted only if the combined result is true. Think of it as requiring an explicit "yes" for permission, while "no" always takes precedence. Notably, nil acts as a sort of "weak no," it can be overridden by a true but not granting access on its own. This approach provides flexibility for implementing features like user blocking (false is crucial here).

For efficiency, nil is the assumed default and not stored in the database.

Controlled - Applying boundaries to an object

The Controlled multimixin link an object to one or more ACLs. This allows for applying multiple boundaries to the same object. In case of overlapping permissions, the system combines them following the logic described above.

Roles

Roles are groups of verbs associated with permissions. While not stored in the database, they are defined at the configuration level to enhance readability and user experience.

Practical example: Surprise birthday party

Let's illustrate how boundaries work with a real-world example: planning a surprise party without the birthday girl finding out.

1. Setting up users

iex> import Bonfire.Me.Fake
iex> organizer = fake_user!()
iex> birthday_girl = fake_user!()
iex> friends = [fake_user!(), fake_user!()]
iex> family = [fake_user!(), fake_user!()]

2. Define your Circles

Organize users into relevant circles (friends and family).

iex> alias Bonfire.Boundaries.Circles
iex> {:ok, friends_circle} = Circles.create(organizer, %{named: %{name: "friends"}})
iex> Circles.add_to_circles(friends, friends_circle)
iex> Circles.is_encircled_by?(List.first(friends), friends_circle)
true
iex> {:ok, family_circle} = Circles.create(organizer, %{named: %{name: "family"}})
iex> Circles.add_to_circles(family, family_circle)

3. Create the ACL (boundary preset)

This boundary will control access to the surprise party plans.

iex> alias Bonfire.Boundaries.Acls
iex> {:ok, boundary} = Acls.simple_create(organizer, "Surprise party")

4. Grant permissions

Allow friends to discover, read, and respond to party plans, while family members can also edit details and send invitations.

iex> alias Bonfire.Boundaries.Grants
iex> Grants.grant(friends_circle.id, boundary.id, [:see, :read, :reply], true, current_user: organizer)
iex> Grants.grant(family_circle.id, boundary.id, [:see, :read, :reply, :edit, :invite], true, current_user: organizer)

Prevent the birthday person from accessing any party information.

iex> Grants.grant(birthday_girl.id, boundary.id, [:see, :read], false, current_user: organizer)

5. Post about the party

iex> alias Bonfire.Posts
iex> {:ok, party_plan} = Posts.publish(
        current_user: organizer,
        boundary: boundary.id,
        post_attrs: %{post_content: %{name: "Surprise party!"}})

6. Double-check applied boundaries

iex> Boundaries.can?(List.first(friends).id, :read, party_plan.id)
true
iex> Boundaries.can?(List.first(family).id, :invite, party_plan.id)
true
iex> Boundaries.can?(birthday_girl.id, :see, party_plan.id)
false
iex> Boundaries.load_pointer(party_plan.id, current_user: birthday_girl)
nil

By following these steps, the organizer can effectively manage access to ensure the birthday girl cannot see the party plans, while friends and family can.

Copyright (c) 2020 Bonfire Contributors

This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

Summary

Functions

Returns ACLs for a set of preset boundary names.

Normalizes boundaries represented as text or list.

Returns custom boundaries or a default set of boundaries to use

Checks if a subject has permission to conduct the specified action(s)/verb(s) on an object.

Returns default boundaries to use based on the provided context.

Finds a caretaker stereotype based on the specified caretaker and stereotype IDs.

Finds caretaker stereotypes based on the specified caretaker and stereotype IDs.

Lists grants for a given set of objects.

Lists grants for a given set of objects and verbs.

Lists ACLs for a given object.

Lists boundaries (ACLs and grants) for a given object

Loads a pointer based on the permissions which are checked based on provided options.

Loads pointers based on boundaries (which are checked based on provided options) and returns a list of permitted pointers.

Loads pointers based on boundaries (which are checked based on provided options) and raises an error if not all pointers are permitted.

Checks if a pointer has permission based on the specified options.

Converts an ACL to a preset boundary name based on the object type.

Converts an ACL to a preset boundary tuple based on the object type.

Returns the name of a preset boundary given a list of boundaries or other boundary representation.

Sets or replace boundaries for a given object.

Assigns the user as the caretaker of the given object or objects, replacing the existing caretaker, if any.

Returns the default boundaries to be set for new users from config.

Lists grants for a given set of users on a set of objects.

Lists grants for a given set of users on a set of objects, filtered by verbs.

Functions

Link to this function

acls_from_preset_boundary_names(presets)

View Source

Returns ACLs for a set of preset boundary names.

Examples

iex> Bonfire.Boundaries.acls_from_preset_boundary_names(["public"])
Link to this function

boundaries_normalise(text)

View Source

Normalizes boundaries represented as text or list.

Examples

iex> Bonfire.Boundaries.boundaries_normalise("local,public")
["local", "public"]

iex> Bonfire.Boundaries.boundaries_normalise(["local", "public"])
["local", "public"]
Link to this function

boundaries_or_default(to_boundaries, context \\ [])

View Source

Returns custom boundaries or a default set of boundaries to use

Examples

iex> Bonfire.Boundaries.boundaries_or_default(["local"])
["local"]

iex> Bonfire.Boundaries.boundaries_or_default(nil, current_user: me)
[{"public", "Public"}]
Link to this function

can?(subject, verbs, object, opts \\ [])

View Source

Checks if a subject has permission to conduct the specified action(s)/verb(s) on an object.

Examples

iex> Bonfire.Boundaries.can?(%User{id: 1}, [:see], %{id: 2})
false
Link to this function

default_boundaries(context \\ [])

View Source

Returns default boundaries to use based on the provided context.

Examples

iex> Bonfire.Boundaries.default_boundaries()
[{"public", "Public"}]

iex> Bonfire.Boundaries.default_boundaries(current_user: me)
[{"local", "Local"}]
Link to this function

find_caretaker_stereotype(caretaker, stereotype, from)

View Source

Finds a caretaker stereotype based on the specified caretaker and stereotype IDs.

Link to this function

find_caretaker_stereotypes(caretaker, stereotypes, from \\ Pointer)

View Source

Finds caretaker stereotypes based on the specified caretaker and stereotype IDs.

Examples

iex> Bonfire.Boundaries.find_caretaker_stereotypes(%User{id: 1}, [%{id: 2}])
[%Needle.Pointer{id: 1}]

Lists grants for a given set of objects.

Examples

iex> Bonfire.Boundaries.list_grants_on([1, 2, 3])
Link to this function

list_grants_on(things, verbs)

View Source

Lists grants for a given set of objects and verbs.

Examples

iex> Bonfire.Boundaries.list_grants_on([1, 2, 3], [:see, :read])
Link to this function

list_object_acls(object, opts \\ [])

View Source

Lists ACLs for a given object.

Examples

iex> Bonfire.Boundaries.list_object_acls(%{id: 1})
[%Bonfire.Data.AccessControl.Acl{id: 42}]
Link to this function

list_object_boundaries(object, opts \\ [])

View Source

Lists boundaries (ACLs and grants) for a given object

iex> Bonfire.Boundaries.list_object_boundaries(%{id: 1})
[%Bonfire.Data.AccessControl.Acl{id: 42, grants: [...]}]
Link to this function

load_pointer(item, opts)

View Source

Loads a pointer based on the permissions which are checked based on provided options.

Examples

iex> Bonfire.Boundaries.load_pointer(%{id: 1}, verbs: [:read], current_user: %{id: 2})
%Needle.Pointer{id: 1}
Link to this function

load_pointers(items, opts)

View Source

Loads pointers based on boundaries (which are checked based on provided options) and returns a list of permitted pointers.

Examples

iex> Bonfire.Boundaries.load_pointers([%{id: 1}], verbs: [:read], current_user: %{id: 2})
[%Needle.Pointer{id: 1}]
Link to this function

load_pointers!(items, opts)

View Source

Loads pointers based on boundaries (which are checked based on provided options) and raises an error if not all pointers are permitted.

Link to this function

pointer_permitted?(item, opts)

View Source

Checks if a pointer has permission based on the specified options.

Examples

iex> Bonfire.Boundaries.pointer_permitted?(%{id: 1}, verbs: [:edit], current_user: %{id: 2})
true
Link to this function

preset_boundary_from_acl(acl, object_type \\ nil)

View Source

Converts an ACL to a preset boundary name based on the object type.

Examples

iex> Bonfire.Boundaries.preset_boundary_from_acl(%Bonfire.Data.AccessControl.Acl{id: 1})
{"public", "Public"}
Link to this function

preset_boundary_tuple_from_acl(acl, object_type \\ nil)

View Source

Converts an ACL to a preset boundary tuple based on the object type.

Examples

iex> Bonfire.Boundaries.preset_boundary_tuple_from_acl(%Bonfire.Data.AccessControl.Acl{id: 1})
{"public", "Public"}

iex> Bonfire.Boundaries.preset_boundary_tuple_from_acl(%Bonfire.Data.AccessControl.Acl{id: 1}, :group)
{"open", "Open"}
Link to this function

preset_name(boundaries, include_remote? \\ false)

View Source

Returns the name of a preset boundary given a list of boundaries or other boundary representation.

Examples

iex> Bonfire.Boundaries.preset_name(["admins", "mentions"])
"admins"

iex> Bonfire.Boundaries.preset_name("public_remote", true)
"public_remote"
Link to this function

set_boundaries(creator, object, opts)

View Source

Sets or replace boundaries for a given object.

Set boundaries on a black object

iex> Bonfire.Boundaries.set_boundaries(%User{id: 1}, %{id: 2}, [boundary: "public"])
{:ok, :granted}

Replace boundaries on an existing object that previously had a preset applied

iex> Bonfire.Boundaries.set_boundaries(%User{id: 1}, %{id: 2}, [boundary: "local", remove_previous_preset: "public"])
{:ok, :granted}
Link to this function

take_care_of!(things, user)

View Source

Assigns the user as the caretaker of the given object or objects, replacing the existing caretaker, if any.

Examples

iex> Bonfire.Boundaries.take_care_of!([%{id: 1}], %{id: 2})
[%{id: 1, caretaker: %{id: 1, caretaker_id: 2, caretaker: %{id: 2}}}]
Link to this function

user_default_boundaries()

View Source

Returns the default boundaries to be set for new users from config.

Examples

iex> Bonfire.Boundaries.user_default_boundaries()
[{"public", "Public"}]
Link to this function

users_grants_on(users, things)

View Source

Lists grants for a given set of users on a set of objects.

Examples

iex> Bonfire.Boundaries.users_grants_on([%{id: 1}], [%{id: 2}])
Link to this function

users_grants_on(users, things, verbs)

View Source

Lists grants for a given set of users on a set of objects, filtered by verbs.

Examples

iex> Bonfire.Boundaries.users_grants_on([%{id: 1}], [%{id: 2}], [:see, :read])
[%Bonfire.Boundaries.Summary{object_id: 2, subject_id: 1}]