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, via the relation between user and team.
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 | .relations.team) as $executerTeam | [.results.approvingUsers.entities[] | select(.relations.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 .relations.team key. This newly filtered array is saved as a variable ($) called executerTeam.
"(.results.executingUser.entities | first | .relations.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(.relations.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.