Examples
The following examples demonstrate common patterns for dynamic permissions.
Forbid execution if entity exists
Let's take a look at the following scenario: Say we have an action that scaffolds a new microservice, and as a result creates an entity in our software catalog representing that service. Now say we want to ensure that execution of this action will be blocked if the provided service name already exists in our catalog.
Here is an example of a permissions JSON that achieves this:
Full permissions JSON (click to expand)
{
"execute": {
// the next three keys allow users to specify roles, specific users, and specific teams that may execute this action
"roles": ["Member", "Admin"],
"users": [],
"teams": [],
"ownedByTeam": false, // declares ownership of the action by a team, if desired
"policy": {
"queries": {
"search_entity": {
"rules": [
// fetch all entities created from the "service" blueprint
{
"value": "service",
"operator": "=",
"property": "$blueprint"
},
// fetch all entities whose identifier is equal to the name provided as an input in the action execution
{
"value": "{{ .inputs.name }}",
"operator": "=",
"property": "$identifier"
}
],
"combinator": "and"
}
},
// if our rule results produced no entities, that means that a service with our desired name does not exist, so we can execute the action
"conditions": [
".results.search_entity.entities | length == 0"
]
}
},
"approve": {
"roles": [
"Admin"
],
"users": [],
"teams": []
}
}
Explanation
The two rules that we defined fetch all service entities whose name is the same as the one provided to the action during execution. These rules will return an array of entities that comply with them. If no entities comply with the rules, the array will be empty.
The conditions query checks if the resulting array is empty or not, and returns true or false, respectively. If the array is empty, that means that an entity with the provided name does not exist in our software catalog, so we can return true and allow the action execution.
Team leader approval
In this example we create rules that state that execution of an action can be approved only by the team leader of the user that asked to execute the action.
Note that this example assumes that the relevant team leader has the Moderator role, as you can see in the approvingUsers section of the permissions JSON below.
The example contains two queries:
executingUser- fetches the user who executed the action.approvingUsers- fetches the users who are allowed to approve the action.
The condition checks if the approver is the executer's team leader, by comparing the team meta-property (which contains the user's team memberships) between the executing user and the approving users.
Full permissions JSON (click to expand)
{
"execute": {
// the next three keys allow users to specify roles, specific users, and specific teams that may execute this action
"roles": ["Member", "Admin"],
"users": [],
"teams": [],
"ownedByTeam": false // declares ownership of the action by a team, if desired
// a policy key may be added here, with queries and conditions within it
},
"approve": {
"roles": [],
"users": [],
"teams": [],
"policy": {
"queries": {
// executingUser is a custom query that returns an array of entities
"executingUser": {
"rules": [
// fetches all users from user blueprint
{
"value": "_user",
"operator": "=",
"property": "$blueprint"
},
// filters all users from immediately previous query
// to find only the user who executed the action
{
"value": "{{.trigger.user.email}}",
"operator": "=",
"property": "$identifier"
}
],
"combinator": "and" // both of the conditions above must be true
},
// approvingUsers is a custom query that returns an array of entities
"approvingUsers": {
"rules": [
// fetches all users from user blueprint
{
"value": "_user",
"operator": "=",
"property": "$blueprint"
},
// fetches all users with the `Moderator` role
{
"value": "Moderator",
"operator": "=",
"property": "port_role"
}
],
"combinator": "and" // both of the conditions above must be true
}
},
// see next section for description of what occurs in the jq query below
"conditions": [
"(.results.executingUser.entities | first | .team) as $executerTeam | [.results.approvingUsers.entities[] | select(.team == $executerTeam) | .identifier]"
]
}
}
}
Explanation
The conditions query uses the two arrays produced as a result of the executingUser and approvingUsers queries and returns an array of users who may approve the self-service action.
The query below filters the array produced by the executingUser query down to only the first element in the array, then further filters this array to show only the contents of the .team meta-property (an array of team identifiers). This is saved as a variable ($) called executerTeam.
"(.results.executingUser.entities | first | .team) as $executerTeam"
The next query (.results.approvingUsers.entities[]) takes the entire array from the approvingUsers query, then applies a filter to include only the approving users from that array who are on the same team as the executing user (select(.team == $executerTeam)). Finally, the array is filtered to yield only an array of the .identifier property of all approvers, which Port then uses to dynamically evaluate who may approve this self-service action.
Prevent self-approval
In this example, we will implement a security best practice known as "segregation of duties" by ensuring that the user who executes an action cannot also approve it. This is particularly important for sensitive operations like production deployments, infrastructure changes, or permission modifications.
Full permissions JSON (click to expand)
{
"execute": {
"roles": ["Member", "Admin"],
"users": [],
"teams": [],
"ownedByTeam": false
},
"approve": {
"roles": [],
"users": [],
"teams": [],
"policy": {
"queries": {
"approvingUsers": {
"rules": [
{
"value": "_user",
"operator": "=",
"property": "$blueprint"
},
{
"value": "Moderator",
"operator": "=",
"property": "port_role"
}
],
"combinator": "and"
}
},
"conditions": [
".trigger.user.email as $executor | [.results.approvingUsers.entities[] | select(.identifier != $executor) | .identifier]"
]
}
}
}
Explanation
This configuration implements a "four-eyes principle", which requires that sensitive actions be verified by a second person before being executed.
Here's what's happening in each part:
-
Execute Permissions: Any user with either the
MemberorAdminrole can execute this action. -
Approve Permissions: The approval process is governed by a policy that:
- Queries all users from the
_userblueprint who have theModeratorrole. - Uses a JQ condition to filter out the specific user who executed the action.
- Queries all users from the
-
The Key Condition:
".trigger.user.email as $executor | [.results.approvingUsers.entities[] | select(.identifier != $executor) | .identifier]"This JQ expression:
- Takes all moderator users from our query results.
- Filters out any user whose identifier matches the email of the person who triggered the action.
- Returns only the identifiers of the remaining users.
The result is a dynamic list of all users who are authorized to approve the action, excluding the original executor. This ensures that no single person can both initiate and approve a sensitive change, reducing the risk of unauthorized or accidental changes.
Manager approval for team-owned entities
In this example, we ensure that actions on team-owned entities require approval from the manager of the owning team. This is useful when you want to enforce hierarchical approval for changes to resources owned by specific teams, such as infrastructure, services, or projects.
Note that this example assumes:
- Your software catalog has a
_teamblueprint with amanagerrelation pointing to user entities. - Your entities have a
teamproperty or relation indicating ownership. - Important: Replace
yourBlueprintin the example below with your actual blueprint identifier (e.g.,service,environment,deployment, etc.).
Full permissions JSON (click to expand)
{
"execute": {
"roles": ["Member", "Admin"],
"users": [],
"teams": [],
"ownedByTeam": false
},
"approve": {
"roles": [],
"users": [],
"teams": [],
"policy": {
"queries": {
"allTeams": {
"rules": [
{
"value": "_team",
"operator": "=",
"property": "$blueprint"
}
],
"combinator": "and"
},
"selectedEntity": {
"rules": [
{
"value": "yourBlueprint",
"operator": "=",
"property": "$blueprint"
},
{
"value": "{{ .entity.identifier }}",
"operator": "=",
"property": "$identifier"
}
],
"combinator": "and"
}
},
"conditions": [
"(.results.selectedEntity.entities | first | .team) as $entityTeams | [.results.allTeams.entities[] | select(.identifier as $teamId | $entityTeams | index($teamId)) | .relations.manager.identifier] | unique"
]
}
}
}
Explanation
This configuration requires that actions on team-owned entities be approved by the manager of the team that owns the entity.
Here's how it works:
-
Execute Permissions: Any user with the
MemberorAdminrole can execute this action. -
Queries:
allTeams: Fetches all team entities from the_teamblueprint.selectedEntity: Fetches the specific entity that the action is being performed on, using{{ .entity.identifier }}to reference the entity being acted upon. Remember to replaceyourBlueprintwith your actual blueprint identifier.
-
The Condition:
"(.results.selectedEntity.entities | first | .team) as $entityTeams | [.results.allTeams.entities[] | select(.identifier as $teamId | $entityTeams | index($teamId)) | .relations.manager.identifier] | unique"This JQ expression:
- Extracts the
teamproperty from the selected entity and stores it in the$entityTeamsvariable. - Iterates through all teams and filters to find those whose identifier matches the entity's team.
- For each matching team, extracts the manager's identifier from the
managerrelation. - Returns a unique list of manager identifiers who can approve the action.
- Extracts the
The result is that only the manager(s) of the team that owns the entity can approve actions performed on it, ensuring proper hierarchical oversight.
Restrict execution to owning team members
In this example, we restrict action execution to users who are members of the team that owns the service (for day-2 actions).
Full permissions JSON (click to expand)
{
"execute": {
"roles": ["Member", "Admin"],
"users": [],
"teams": [],
"policy": {
"queries": {
"owningTeamMembers": {
"rules": [
{
"property": "$blueprint",
"operator": "=",
"value": "_user"
},
{
"property": "$team",
"operator": "contains",
"value": "{{ .entity.relations.owning_team }}"
}
],
"combinator": "and"
}
},
"conditions": [
".trigger.user.email as $user | [.results.owningTeamMembers.entities[].identifier] | any(. == $user)"
]
}
},
"approve": {
"roles": ["Admin"],
"users": [],
"teams": []
}
}
Explanation
This configuration ensures only team members who own the service can perform day-2 actions on it.
-
Query: Fetches all users from the
_userblueprint whose$teammeta-property contains the owning team of the entity (accessed via.entity.relations.owning_team). -
Condition: Checks if the executing user's email exists in the list of team member identifiers:
.trigger.user.email as $user- stores the executor's email..results.owningTeamMembers.entities[].identifier- gets all user emails from the query.any(. == $user)- returnstrueif the executor is in the list.
For day-2 actions, you can access the entity's properties and relations using {{ .entity.properties.X }} and {{ .entity.relations.X }} in your query rules. This allows you to create ownership-based permissions.
Owning team manager approval using relatedTo
In this example, a user triggers a day-2 action (such as "Deploy to production") on a Microservice entity. The approval request is routed to the manager of whichever team owns that service.
This example requires a manager relation on the _team blueprint, pointing to _user. This relation does not exist by default, you need to add it to your _team blueprint. The manager entity identifier should be the user's email address.
The query uses the relatedTo operator to find _team entities related to the triggered service, then the condition extracts the manager relation (a _user identifier / email) from the first matching team.
Approval policy JSON (click to expand)
{
"approve": {
"roles": [],
"users": [],
"teams": [],
"policy": {
"queries": {
"owningTeam": {
"rules": [
{
"operator": "=",
"property": "$blueprint",
"value": "_team"
},
{
"operator": "relatedTo",
"blueprint": "Microservice",
"value": "{{ .entity.identifier }}"
}
],
"combinator": "and"
}
},
"conditions": [
"[.results.owningTeam.entities[0].relations.manager.identifier] | map(select(. != null))"
]
}
}
}
Explanation
- Query: Fetches all
_teamentities related to the triggeredMicroservicevia therelatedTooperator. - Condition: Reads
.relations.manager.identifierfrom the first team. In the policy JQ runtime, relations are objects with.identifierand.titleproperties, so.identifierextracts the manager's email. If no manager is set,map(select(. != null))produces an empty array.
A service can be owned by more than one team. This example uses entities[0] to get the first owning team's manager. To get managers from all owning teams, use [.results.owningTeam.entities[].relations.manager.identifier] | map(select(. != null)) instead.
Returning an empty array from approve conditions means no one can approve, not that the action is auto-approved. If you want certain users (such as the team manager themselves) to skip the approval step, pair your approval policy with an automation that calls Port's approve API. See Automate self-service action approvals for a step-by-step guide.
Approval from a team chosen in the form
In this example, an action lets users request access to a team. The form includes a selected_team entity-picker input (blueprint: _team). Approval is routed to the manager of the team the user selected.
This example requires a manager relation on the _team blueprint, pointing to _user. See the prerequisite note above.
Action input definition:
{
"userInputs": {
"properties": {
"selected_team": {
"type": "string",
"format": "entity",
"blueprint": "_team",
"title": "Team to join"
}
},
"required": ["selected_team"]
}
}
Approval policy JSON (click to expand)
{
"approve": {
"roles": [],
"users": [],
"teams": [],
"policy": {
"queries": {
"chosenTeam": {
"rules": [
{
"operator": "=",
"property": "$blueprint",
"value": "_team"
},
{
"operator": "=",
"property": "$identifier",
"value": "{{ .inputs.selected_team.identifier }}"
}
],
"combinator": "and"
}
},
"conditions": [
"[.results.chosenTeam.entities[0].relations.manager.identifier] | map(select(. != null))"
]
}
}
}
Explanation
- Query: Fetches the
_teamentity whose identifier matches the user's form selection via{{ .inputs.selected_team.identifier }}. - Condition: Extracts the manager's email via
.relations.manager.identifierfrom the first result. Returns an empty array if no manager is set.
Role-based execution with conditional approval
This example covers a "Delete service" action. Only Admins or members of the owning team can execute it. When a team member executes it, the team manager must approve.
This example requires a manager relation on the _team blueprint, pointing to _user. See the prerequisite note above.
Since approve.roles includes Admin, any Admin can manually approve runs from other users. To fully auto-approve runs triggered by Admins, pair this policy with an automation. See Automate self-service action approvals for details.
Full permissions JSON (click to expand)
{
"execute": {
"roles": ["Admin"],
"users": [],
"teams": [],
"ownedByTeam": true
},
"approve": {
"roles": ["Admin"],
"users": [],
"teams": [],
"policy": {
"queries": {
"owningTeam": {
"rules": [
{
"operator": "=",
"property": "$blueprint",
"value": "_team"
},
{
"operator": "relatedTo",
"blueprint": "Microservice",
"value": "{{ .entity.identifier }}"
}
],
"combinator": "and"
}
},
"conditions": [
"[.results.owningTeam.entities[0].relations.manager.identifier] | map(select(. != null))"
]
}
}
}
Explanation
- Execute permissions: The
"roles": ["Admin"]combined with"ownedByTeam": trueallows Admins and owning team members to run the action. - Query:
owningTeamfetches the_teamentity related to the triggeredMicroservice. - Condition: Resolves the owning team's manager as the dynamic approver via
.relations.manager.identifier. If no manager is set, the condition produces an empty array (meaning no approvers are resolved from the policy, though Admins inapprove.rolescan still approve manually).
Quick reference
Available context in conditions
| Expression | Type | Description |
|---|---|---|
.trigger.user.email | string | Email of the user who triggered the action. |
.entity.identifier | string | Identifier of the triggered entity (day-2 only). |
.inputs.<name>.identifier | string | Identifier of an entity selected in a form input. |
.inputs.<name> | any | Value of a scalar form input. |
.results.<queryName>.entities[] | array | Entities returned by a named query. |
Common JQ patterns
| Goal | Expression |
|---|---|
| Get manager email from team | .entities[0].relations.manager.identifier |
| Check if executor is on a team | .trigger.user.email as $user | [.results.teamMembers.entities[].identifier] | any(. == $user) |
| Get all owning team managers | [.results.owningTeam.entities[].relations.manager.identifier] | map(select(. != null)) |
| Filter out null values | [...] | map(select(. != null)) |