Skip to main content
Entitlements let you control feature access based on both a user’s role and their organization’s subscription plan—including quota limits tied to paid tiers—making them ideal for managing access in tiered offerings.

Implementation

The core part of the entitlements logic is to conditionally grant a user permission based on a combination of their role and attributes of the organization. For example, we can grant users permission to create repositories for an organization if:
  • They have the “member” role for that organization, and
  • The organization has not reached their paid quota of repositories
actor User { }

resource Organization {
  roles = ["admin", "member"];
  permissions = ["repository.create"];

  "member" if "admin";
}

resource Plan { }

resource Feature {
  relations = {plan: Plan};
}

# Define the permission check
has_permission(user: User, "repository.create", org: Organization) if
  has_role(user, "member", org) and
  has_quota_remaining(org, Feature{"repository"});

has_quota_remaining(org: Organization, feature: Feature) if
  quota matches Integer and
  has_quota(org, feature, quota) and
  used matches Integer and
  quota_used(org, feature, used) and
  used < quota;

has_quota(org: Organization, feature: Feature, quota: Integer) if
  plan matches Plan and
  has_relation(plan, "subscribed", org) and
  plan_quota(plan, feature, quota);

declare plan_quota(Plan, Feature, Integer);
declare quota_used(Organization, Feature, Integer);

plan_quota(Plan{"pro"}, Feature{"repository"}, 10);
plan_quota(Plan{"basic"}, Feature{"repository"}, 0);

test "members can create repositories if they have quota" {
  setup {
    quota_used(Organization{"apple"}, Feature{"repository"}, 5);
    quota_used(Organization{"netflix"}, Feature{"repository"}, 10);
    quota_used(Organization{"amazon"}, Feature{"repository"}, 0);
    has_relation(Plan{"pro"}, "subscribed", Organization{"apple"});
    has_relation(Plan{"pro"}, "subscribed", Organization{"netflix"});
    has_relation(Plan{"basic"}, "subscribed", Organization{"amazon"});
    has_role(User{"alice"}, "member", Organization{"apple"});
    has_role(User{"bob"}, "member", Organization{"netflix"});
    has_role(User{"charlie"}, "member", Organization{"amazon"});
  }
  assert has_quota_remaining(Organization{"apple"}, Feature{"repository"});
  # Apple has quota remaining, so all good
  assert allow(User{"alice"}, "repository.create", Organization{"apple"});
  # Netflix has used all quota
  assert_not allow(User{"bob"}, "repository.create", Organization{"netflix"});
  # Amazon doesn't have any quota left
  assert_not allow(User{"charlie"}, "repository.create", Organization{"amazon"});
}
To test this, we define:
  • Organizations with different subscription plans and quota usage.
  • Users assigned to each organization.
  • Assertions to verify which users can create repositories based on remaining quota.