BetterIAM
Server API

relationships

Relationships record that a person or group stands in a named relation, such as owner or viewer, to one resource.

Relationships record that a person or group stands in a named relation, such as owner or viewer, to one resource. Alice is an owner of folder/plans; the design group are viewers of folder/plans. They express per-resource sharing and ownership (relationship-based access control) without writing resource ids into policies: one role says "viewers may read", and sharing a folder is a single tuple instead of a policy edit. The relationships guide shows the full pattern.

How policies read relations

A tuple is {type}/{id}#{relation}@{subjectType}:{subjectId}. The relation must be declared on the resource type (relations in permissions.resourceTypes, or on a tenant-defined type). The subject is an identity or a group; a group's tuples apply to its current members.

When a decision is made, the caller's live relations on the evaluated resource appear as resource.relations (a sorted array of names) and those on its registered parent as resource.parentRelations. Test them with ArrayContains:

{ effect: 'allow', actions: ['files:read'], resources: ['file/*'],
  conditions: { ArrayContains: { 'resource.parentRelations': ['viewer', 'editor', 'owner'] } } }

Administrative calls on iam/{type}/{id} see the relations on the named resource too, which is how an owner can share their own folder without a tenant-wide administrator role: grant iam:relationships:create on iam/folder/* under the condition ArrayContains: { 'resource.relations': ['owner'] }. Role sessions hold no relations. Expired tuples stop counting at once and are removed later by iam.sweepExpired(). Tuples are also removed with their identity, their group, or their managed resource. listAccessible takes relations into account.

Methods3
Serveriam.api.relationships
Clientclient.relationships
HTTPPOST /api/iam/relationships/*
MethodWhat it doesAccess
createGives an identity or group a declared relation on one resource, optionally until a given time.Credential
deleteRemoves one relationship tuple, ending the access it gave.Credential
listLists relationship tuples of one resource, one subject, one type, or the whole tenant, newest first.Credential

create

Gives an identity or group a declared relation on one resource, optionally until a given time.

POST/api/iam/relationships/create
client.relationships.create()Credential

Used inRelationships,Sharing and access questions

  • Permission: iam:relationships:create on iam/{type}/{id}.
  • Audited as: iam:relationships:create, on {type}/{id}.
  • Errors: INVALID_RESOURCE_TYPE when the type is not declared; INVALID_INPUT when the relation is not declared for the type, the subject type is not identity or group, or expiresAt is not in the future (at most ten years out); NOT_FOUND when a managed resource is not registered, the identity is not in this tenant or was deleted, or the group is not in this tenant; INVARIANT_VIOLATION when an enforced access invariant would newly fail.

Resources of managed types must be registered first; resources of application-owned types are accepted as named. Creating a tuple that already exists replaces it instead of failing: its expiresAt becomes the one you pass (none makes it permanent) and the caller is recorded as createdBy. Use expiresAt for time-boxed sharing, such as giving an auditor viewer on a folder for a week.

await iam.api.relationships.create(credential, {
  tenantId,
  type: 'folder',
  id: 'plans',
  relation: 'viewer',
  subjectType: 'group',
  subjectId: designGroupId,
  expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000,
});
Input

Prop

Type

Returns

A Relationship object:

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/relationships/create" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "type": "<type>",
  "id": "<id>",
  "relation": "<relation>",
  "subjectType": "identity",
  "subjectId": "<subjectId>"
}'
Signature
iam.api.relationships.create(
  credential: CredentialInput,
  input: {
    tenantId: string;
    type: string;
    id: string;
    relation: string;
    subjectType: 'identity' | 'group';
    subjectId: string;
    expiresAt?: number;
  },
): Promise<Relationship>

delete

Removes one relationship tuple, ending the access it gave.

POST/api/iam/relationships/delete
client.relationships.delete()Credential
  • Permission: iam:relationships:delete on the tuple's resource, iam/{type}/{id}.
  • Audited as: iam:relationships:delete, on {type}/{id}.
  • Errors: NOT_FOUND when the tuple is not in this tenant; INVARIANT_VIOLATION when an enforced access invariant would newly fail.

Pass the tuple's id, as returned by create or list. The change applies to the next decision.

Input

Prop

Type

Returns

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/relationships/delete" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>",
  "relationshipId": "<relationshipId>"
}'
Signature
iam.api.relationships.delete(
  credential: CredentialInput,
  input: { tenantId: string; relationshipId: string },
): Promise<{ deleted: boolean }>

list

Lists relationship tuples of one resource, one subject, one type, or the whole tenant, newest first.

POST/api/iam/relationships/list
client.relationships.list()Credential
  • Permission: iam:relationships:read on iam/{type}/{id} when type and id are given, on iam/{type}/* when only type is, otherwise on iam/*.
  • Audited as: iam:relationships:read.
  • Errors: INVALID_INPUT for a subject type other than identity or group.

Filter by type, id, relation, subjectType, and subjectId in any combination: "who can see this folder" is { type, id }, and "what has been shared with this group" is { subjectType: 'group', subjectId }. Expired tuples are left out unless includeExpired is true. Because the permission is checked on the resource, an owner allowed to read relationships on their own folder can review who it is shared with.

Input

Prop

Type

Returns

An array of Relationship.

Prop

Type

Example HTTP request

Only the required fields are shown; replace each <placeholder>. The response is { "data": … } on success or { "error": { "code", "message" } }.

curl -X POST "$IAM_URL/api/iam/relationships/list" \
  -H "Authorization: Bearer $BETTER_IAM_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Better-IAM: 1" \
  -d '{
  "tenantId": "<tenantId>"
}'
Signature
iam.api.relationships.list(
  credential: CredentialInput,
  input: {
    tenantId: string;
    type?: string;
    id?: string;
    relation?: string;
    subjectType?: 'identity' | 'group';
    subjectId?: string;
    includeExpired?: boolean;
  },
): Promise<Relationship[]>

Was this page helpful?

Better IAM is created by Sean Filimon

Last updated

On this page