# A membership role

Use a role when people get access by belonging to something, such as the
editors of a company. A role is stored in a record of its own: a
membership entity, whose `membership:` section names three of its fields:

- `scope`: the record the role is held in, such as the company;
- `member`: who holds it, a reference to the user entity;
- `role`: the field that holds the role, typed with an enum.

The role enum lives beside the membership in the same domain, and other
domains use it by its full name. There is no global roles file.

```jadpo
// domains/accounts/types/company_role.jadpo
type CompanyRole = Enum {
    owner
    editor
    viewer
}

// domains/accounts/entities/company_membership.jadpo
entity CompanyMembership {
    id: Uuid
    company_id: Company.id {
        on_delete: OnDelete.cascade
    }
    user_id: User.id {
        on_delete: OnDelete.cascade
    }
    role: CompanyRole

    membership: {
        scope: company_id
        member: user_id
        role: role
    }

    policy: {
        create: [CompanyRole.owner]
        read: [CompanyRole.owner, user_id]
        update: [CompanyRole.owner]
        delete: [CompanyRole.owner, user_id]
    }
}
```

Here only a company's owners may add, change or remove members. Each
member may also read their own membership, and delete it to leave.

## Using a role on other records

A role applies to another entity through that entity's reference to the
scope, the same reference [tenancy](tenancy.md) uses. A `Project` with a
`company_id` takes its grants from the roles held in its company:

```jadpo
// domains/projects/entities/project.jadpo
entity Project {
    id: Uuid
    company_id: Company.id {
        on_delete: OnDelete.cascade
    }
    name: ProjectName

    policy: {
        create: [CompanyRole.editor]
        read: [CompanyRole.viewer, CompanyRole.editor]
        update: [CompanyRole.editor]
        delete: [CompanyRole.owner]
    }
}
```

## How roles combine

- Roles have no ranking. A role lets in only where a list names it, so an
  owner who may also edit is listed under both.
- Only current memberships count. A membership its lifecycle hides grants
  nothing, and removing one takes effect on the next request.
- A role held across the whole application, such as staff, has a
  membership with `scope: MembershipScope.application`.
- An entity can have two references to the scope, such as a handover with
  a `from_team_id` and a `to_team_id`. Then each grant names the reference
  it means: `TeamRole.lead(from_team_id)` is the lead of the from-team.
