View Source Bonfire.Boundaries (Bonfire v0.9.10-classic-beta.169)
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
Term | Definition |
---|---|
Subject or User | An individual who interacts with the system. |
Circle | A categorization method for users, allowing users to group other users (e.g., colleagues, friends). |
Verb | An action that a user can perform (e.g., read, reply). |
Permission | A value indicating whether an action is allowed (true ), denied (false ), or nil . |
Grant | Links a user or circle with a verb and permission. |
ACL | Access Control List, a collection of grants. Also called "boundary" or "boundary preset" in the app. |
Controlled | Links an object to one or more ACLs, to determine access based on the grants. |
Role | A 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 allowedfalse
: 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 Grant
s 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:
input | input | result |
---|---|---|
nil | nil | nil |
nil | true | true |
nil | false | false |
true | nil | true |
true | true | true |
true | false | false |
false | nil | false |
false | true | false |
false | false | false |
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 and License
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
Returns ACLs for a set of preset boundary names.
Examples
iex> Bonfire.Boundaries.acls_from_preset_boundary_names(["public"])
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"]
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"}]
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
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"}]
Finds a caretaker stereotype based on the specified caretaker and stereotype IDs.
find_caretaker_stereotypes(caretaker, stereotypes, from \\ Pointer)
View SourceFinds 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])
Lists grants for a given set of objects and verbs.
Examples
iex> Bonfire.Boundaries.list_grants_on([1, 2, 3], [:see, :read])
Lists ACLs for a given object.
Examples
iex> Bonfire.Boundaries.list_object_acls(%{id: 1})
[%Bonfire.Data.AccessControl.Acl{id: 42}]
Lists boundaries (ACLs and grants) for a given object
iex> Bonfire.Boundaries.list_object_boundaries(%{id: 1})
[%Bonfire.Data.AccessControl.Acl{id: 42, grants: [...]}]
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}
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}]
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.
Examples
iex> Bonfire.Boundaries.pointer_permitted?(%{id: 1}, verbs: [:edit], current_user: %{id: 2})
true
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"}
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"}
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"
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}
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}}}]
Returns the default boundaries to be set for new users from config.
Examples
iex> Bonfire.Boundaries.user_default_boundaries()
[{"public", "Public"}]
Lists grants for a given set of users on a set of objects.
Examples
iex> Bonfire.Boundaries.users_grants_on([%{id: 1}], [%{id: 2}])
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}]