Skip to content

OPL permissions have inconsistencies when using this.permits(ctx) vs calling a transitive permits directly #1626

@AustinCase

Description

@AustinCase

Preflight checklist

Ory Network Project

No response

Describe the bug

When using the OPL file, the this.permits(ctx) not translating into the proper permissions structure when calling self that calls a transitive relation.

Reproducing the bug

The following example returns different results when calling the check permission api for read relation then the update, delete, create_media_stack, and list_media_stacks. The read relation returns true while all others return false. We have other Namespaces that do NOT exhibit this behavior. They work as expected.

class Bin implements Namespace {
  related: {
    projects: Project[];
  };
  permits = {
    read: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
    update: (ctx: Context): boolean => this.permits.read(ctx),
    delete: (ctx: Context): boolean => this.permits.read(ctx),
    create_media_stack: (ctx: Context): boolean => this.permits.read(ctx),
    list_media_stacks: (ctx: Context): boolean => this.permits.read(ctx),
  };
}

When changing the above to the following, everything works as expected:

class Bin implements Namespace {
  related: {
    projects: Project[];
  };
  permits = {
    read: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
    update: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
    delete: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
    create_media_stack: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx))),
    list_media_stacks: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
  };
}

Relevant log output

No response

Relevant configuration

The entirety of the OPL file ( that is relevant to this issue ) is copied below

import { Namespace, Context } from "@ory/keto-namespace-types";

class User implements Namespace {}

class Workspace implements Namespace {
  related: {
    owners: User[];
    admins: User[];
    members: User[];
  };
  permits = {
    read: (ctx: Context): boolean =>
      this.related.owners.includes(ctx.subject) ||
      this.related.admins.includes(ctx.subject) ||
      this.related.members.includes(ctx.subject),
    update: (ctx: Context): boolean =>
      this.related.owners.includes(ctx.subject) || this.related.admins.includes(ctx.subject),
    deactivate: (ctx: Context): boolean => this.related.owners.includes(ctx.subject),
    transfer_ownership: (ctx: Context): boolean => this.related.owners.includes(ctx.subject),
    manage_members: (ctx: Context): boolean => this.permits.update(ctx),
    create_project: (ctx: Context): boolean => this.permits.read(ctx),
    list_projects: (ctx: Context): boolean => this.permits.read(ctx),
  };
}

class Project implements Namespace {
  related: {
    workspaces: Workspace[];
    admins: User[];
    members: User[];
  };
  permits = {
    read: (ctx: Context): boolean =>
      this.related.workspaces.traverse((workspace) => workspace.permits.list_projects(ctx)) ||
      this.related.admins.includes(ctx.subject) ||
      this.related.members.includes(ctx.subject),
    update: (ctx: Context): boolean =>
      this.related.workspaces.traverse((workspace) => workspace.permits.list_projects(ctx)) ||
      this.related.admins.includes(ctx.subject),
    delete: (ctx: Context): boolean =>
      this.related.workspaces.traverse((workspace) => workspace.permits.list_projects(ctx)) ||
      this.related.admins.includes(ctx.subject),
    invite: (ctx: Context): boolean =>
      this.related.workspaces.traverse((workspace) => workspace.permits.manage_members(ctx)) ||
      this.related.admins.includes(ctx.subject),
    create_bin: (ctx: Context): boolean => this.permits.read(ctx),
    list_bins: (ctx: Context): boolean => this.permits.read(ctx),
    analyze_assets: (ctx: Context): boolean => this.permits.read(ctx),
    transcribe_assets: (ctx: Context): boolean => this.permits.read(ctx),
  };
}

class Bin implements Namespace {
  related: {
    projects: Project[];
  };
  permits = {
    read: (ctx: Context): boolean => this.related.projects.transitive((project) => project.permits.list_bins(ctx)),
    update: (ctx: Context): boolean => this.permits.read(ctx),
    delete: (ctx: Context): boolean => this.permits.read(ctx),
    create_media_stack: (ctx: Context): boolean => this.permits.read(ctx),
    list_media_stacks: (ctx: Context): boolean => this.permits.read(ctx),
  };
}

Version

0.13.0-alpha.0

On which operating system are you observing this issue?

macOS

In which environment are you deploying?

Other

Additional Context

This is in our devenv nix environment, same behavior with docker. Postgres backend.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is not working.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions