Skip to main content
Impersonation lets users act on behalf of others—like support reps accessing customer accounts—by temporarily inheriting a subset of their permissions through a combination of impersonation facts and policy rules.

Implementation

There are two main components to impersonation:
  1. A fact that indicates whether a user is impersonating another user.
  2. A policy that grants permissions to users who are impersonating another user.
In our example, members with a “support” role need to impersonate admins.
actor User {
  permissions = ["impersonate"];

  "impersonate" if global "support";
}

global {
  roles = ["support"];
}

resource Organization {
  roles = ["admin", "member"];
  permissions = ["read", "write"];

  "member" if "admin";

  "read" if "member";
  "write" if "admin";
}

# a user can do anything some other user can do
# if they are allowed to impersonate that user and
# are currently impersonating them
allow(user: User, action: String, resource: Resource) if
  other_user matches User and
  has_permission(user, "impersonate", other_user) and
  is_impersonating(user, other_user) and
  has_permission(other_user, action, resource);

# we need to specify the default allow rule here
# because we added our own custom one above
allow(user: User, action: String, resource: Resource) if
  has_permission(user, action, resource);

test "global support users can read user organizations via impersonation" {
  setup {
    has_role(User{"alice"}, "support");
    has_role(User{"bob"}, "admin", Organization{"acme"});
    has_role(User{"charlie"}, "member", Organization{"bar"});
    is_impersonating(User{"alice"}, User{"bob"});
  }

  # bob can read as a member
  assert allow(User{"bob"}, "read", Organization{"acme"});

  # alice can impersonate bob
  assert allow(User{"alice"}, "impersonate", User{"bob"});

  # alice can read via Bob by impersonating bob
  assert allow(User{"alice"}, "read", Organization{"acme"});

  # charlie can read as a member
  assert allow(User{"charlie"}, "read", Organization{"bar"});

  # alice cannot read because alice is not impersonating charlie
  assert_not allow(User{"alice"}, "read", Organization{"bar"});
}
In our test case:
  1. Alice is a customer support rep and is impersonating Bob.
  2. Bob is an admin of the acme organization.
  3. And so Alice can see anything Bob can.

Common scenarios

There are several ways to define who has permission to impersonate another user. Common ones include: From a relationship with the user:
actor User {
  permissions = ["impersonate"];
  relations = { manager: User };

  "impersonate" if "manager";
}
Or from a role on the organization:
actor User {
  permissions = ["impersonate"];
}

resource Organization {
  roles = ["member", "admin"];
}

# Organization admins can impersonate members
has_permission(user: User, "impersonate", other_user: User) if
  org matches Organization and
  has_role(user, "admin", org) and
  has_role(other_user, "member", org);
The is_impersonating fact can be included as an ephemeral context fact or more durably synced to Oso Cloud.
  • If included as a context fact, when a user “ends” an impersonation session, the application stops sending the impersonation context fact.
  • If stored in Oso Cloud, you need to delete the persisted fact to end the impersonation.
Use context facts to scope impersonation to a single application or service, and persisted facts to share impersonation sessions across multiple services.