Chapter 4. Conditional policies in Red Hat Developer Hub
The permission framework in Red Hat Developer Hub provides conditions, supported by the RBAC backend plugin (backstage-plugin-rbac-backend
). The conditions work as content filters for the Developer Hub resources that are provided by the RBAC backend plugin.
The RBAC backend API stores conditions assigned to roles in the database. When you request to access the frontend resources, the RBAC backend API searches for the corresponding conditions and delegates them to the appropriate plugin using its plugin ID. If you are assigned to multiple roles with different conditions, then the RBAC backend merges the conditions using the anyOf
criteria.
- Conditional criteria
A condition in Developer Hub is a simple condition with a rule and parameters. However, a condition can also contain a parameter or an array of parameters combined by conditional criteria. The supported conditional criteria includes:
-
allOf
: Ensures that all conditions within the array must be true for the combined condition to be satisfied. -
anyOf
: Ensures that at least one of the conditions within the array must be true for the combined condition to be satisfied. -
not
: Ensures that the condition within it must not be true for the combined condition to be satisfied.
-
- Conditional object
The plugin specifies the parameters supported for conditions. You can access the conditional object schema from the RBAC API endpoint to understand how to construct a conditional JSON object, which is then used by the RBAC backend plugin API.
A conditional object contains the following parameters:
Table 4.1. Conditional object parameters Parameter Type Description result
String
Always has the value
CONDITIONAL
roleEntityRef
String
String entity reference to the RBAC role, such as
role:default/dev
pluginId
String
Corresponding plugin ID, such as
catalog
permissionMapping
String array
Array permission actions, such as
['read', 'update', 'delete']
resourceType
String
Resource type provided by the plugin, such as
catalog-entity
conditions
JSON
Condition JSON with parameters or array parameters joined by criteria
- Conditional policy aliases
The RBAC backend plugin (
backstage-plugin-rbac-backend
) supports the use of aliases in conditional policy rule parameters. The conditional policy aliases are dynamically replaced with the corresponding values during policy evaluation. Each alias in conditional policy is prefixed with a$
sign indicating its special function.The supported conditional aliases include:
-
$currentUser
: This alias is replaced with the user entity reference for the user who requests access to the resource. For example, if user Tom from the default namespace requests access,$currentUser
becomesuser:default/tom
.
-
Example conditional policy object with $currentUser
alias
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/developer", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["delete"], "conditions": { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["$currentUser"] } } }
-
$ownerRefs
: This alias is replaced with ownership references, usually as an array that includes the user entity reference and the user’s parent group entity reference. For example, for user Tom from team-a,$ownerRefs
becomes['user:default/tom', 'group:default/team-a']
.
Example conditional policy object with $ownerRefs
alias
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/developer", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["delete"], "conditions": { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["$ownerRefs"] } } }
4.1. Conditional policies definition
You can access API endpoints for conditional policies in Red Hat Developer Hub. For example, to retrieve the available conditional rules, which can help you define these policies, you can access the GET [api/plugins/condition-rules]
endpoint.
The api/plugins/condition-rules
returns the condition parameters schemas, for example:
[ { "pluginId": "catalog", "rules": [ { "name": "HAS_ANNOTATION", "description": "Allow entities with the specified annotation", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "annotation": { "type": "string", "description": "Name of the annotation to match on" }, "value": { "type": "string", "description": "Value of the annotation to match on" } }, "required": [ "annotation" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } }, { "name": "HAS_LABEL", "description": "Allow entities with the specified label", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "label": { "type": "string", "description": "Name of the label to match on" } }, "required": [ "label" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } }, { "name": "HAS_METADATA", "description": "Allow entities with the specified metadata subfield", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "key": { "type": "string", "description": "Property within the entities metadata to match on" }, "value": { "type": "string", "description": "Value of the given property to match on" } }, "required": [ "key" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } }, { "name": "HAS_SPEC", "description": "Allow entities with the specified spec subfield", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "key": { "type": "string", "description": "Property within the entities spec to match on" }, "value": { "type": "string", "description": "Value of the given property to match on" } }, "required": [ "key" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } }, { "name": "IS_ENTITY_KIND", "description": "Allow entities matching a specified kind", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "kinds": { "type": "array", "items": { "type": "string" }, "description": "List of kinds to match at least one of" } }, "required": [ "kinds" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } }, { "name": "IS_ENTITY_OWNER", "description": "Allow entities owned by a specified claim", "resourceType": "catalog-entity", "paramsSchema": { "type": "object", "properties": { "claims": { "type": "array", "items": { "type": "string" }, "description": "List of claims to match at least one on within ownedBy" } }, "required": [ "claims" ], "additionalProperties": false, "$schema": "http://json-schema.org/draft-07/schema#" } } ] } ... <another plugin condition parameter schemas> ]
The RBAC backend API constructs a condition JSON object based on the previous condition schema.
4.1.1. Examples of conditional policies
In Red Hat Developer Hub, you can define conditional policies with or without criteria. You can use the following examples to define the conditions based on your use case:
- A condition without criteria
Consider a condition without criteria displaying catalogs only if user is a member of the owner group. To add this condition, you can use the catalog plugin schema
IS_ENTITY_OWNER
as follows:Example condition without criteria
{ "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["group:default/team-a"] } }
In the previous example, the only conditional parameter used is
claims
, which contains a list of user or group entity references.You can apply the previous example condition to the RBAC REST API by adding additional parameters as follows:
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/test", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["read"], "conditions": { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["group:default/team-a"] } } }
- A condition with criteria
Consider a condition with criteria, which displays catalogs only if user is a member of owner group OR displays list of all catalog user groups.
To add the criteria, you can add another rule as
IS_ENTITY_KIND
in the condition as follows:Example condition with criteria
{ "anyOf": [ { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["group:default/team-a"] } }, { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": ["Group"] } } ] }
NoteRunning conditions in parallel during creation is not supported. Therefore, consider defining nested conditional policies based on the available criteria.
Example of nested conditions
{ "anyOf": [ { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["group:default/team-a"] } }, { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": ["Group"] } } ], "not": { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": ["Api"] } } }
You can apply the previous example condition to the RBAC REST API by adding additional parameters as follows:
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/test", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["read"], "conditions": { "anyOf": [ { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": ["group:default/team-a"] } }, { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": ["Group"] } } ] } }
The following examples can be used with Developer Hub plugins. These examples can help you determine how to define conditional policies:
Conditional policy defined for Keycloak plugin
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/developer", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["update", "delete"], "conditions": { "not": { "rule": "HAS_ANNOTATION", "resourceType": "catalog-entity", "params": { "annotation": "keycloak.org/realm", "value": "<YOUR_REALM>" } } } }
The previous example of Keycloak plugin prevents users in the role:default/developer
from updating or deleting users that are ingested into the catalog from the Keycloak plugin.
In the previous example, the annotation keycloak.org/realm
requires the value of <YOUR_REALM>
.
Conditional policy defined for Quay plugin
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/developer", "pluginId": "scaffolder", "resourceType": "scaffolder-action", "permissionMapping": ["use"], "conditions": { "not": { "rule": "HAS_ACTION_ID", "resourceType": "scaffolder-action", "params": { "actionId": "quay:create-repository" } } } }
The previous example of Quay plugin prevents the role role:default/developer
from using the Quay scaffolder action. Note that permissionMapping
contains use
, signifying that scaffolder-action
resource type permission does not have a permission policy.
For more information about permissions in Red Hat Developer Hub, see Chapter 3, Permission policies in Red Hat Developer Hub.
4.2. Configuring conditional policies defined in an external file
You can configure and manage conditional policies that are defined in an external file. To define conditional policies, you can directly edit the configuration files and pass them to Developer Hub, instead of using the Developer Hub web UI or API. You can configure Developer Hub to use these files instead of the default files.
Prerequisites
- You are logged in to your OpenShift Container Platform account using the OpenShift Container Platform web console.
You have defined roles and associated policies in a CSV file that serves as a basis for creating roles and permissions. Ensure that you mount the CSV file to Developer Hub.
For more information, see Conditional policies definition and Configuration of permission policies defined in an external file.
Procedure
Define conditional policies in a YAML file, which includes role references, permission mappings, and conditions.
The following is an example of a YAML file defining conditional policies:
Example YAML file defining conditional policies
--- result: CONDITIONAL roleEntityRef: 'role:default/test' pluginId: catalog resourceType: catalog-entity permissionMapping: - read - update conditions: rule: IS_ENTITY_OWNER resourceType: catalog-entity params: claims: - 'group:default/team-a' - 'group:default/team-b' --- result: CONDITIONAL roleEntityRef: 'role:default/test' pluginId: catalog resourceType: catalog-entity permissionMapping: - delete conditions: rule: IS_ENTITY_OWNER resourceType: catalog-entity params: claims: - 'group:default/team-a'
In OpenShift Container Platform, create a ConfigMap to hold the policies as shown in the following example:
Example ConfigMap
kind: ConfigMap apiVersion: v1 metadata: name: rbac-conditional-policy namespace: rhdh data: rbac-policy.yaml: | p, role:default/guests, catalog-entity, read, allow result: CONDITIONAL roleEntityRef: 'role:default/test' pluginId: catalog resourceType: catalog-entity permissionMapping: - read - update conditions: rule: IS_ENTITY_OWNER resourceType: catalog-entity params: claims: - 'group:default/team-a' - 'group:default/team-b'
Open
app-config.yaml
file and specify the path toconditionalPoliciesFile
as shown in the following example:Example
app-config.yaml
filepermission: enabled: true rbac: conditionalPoliciesFile: /some/path/conditional-policies.yaml
To enable automatic reloading of the policy file without restarting the application, add the
policyFileReload
option and set it totrue
:Example
app-config.yaml
filepermission: enabled: true rbac: conditionalPoliciesFile: /some/path/conditional-policies.yaml policies-csv-file: /some/path/rbac-policy.csv policyFileReload: true
Optional: Define nested conditional policies in the YAML file as needed.
Example for nested conditional policies
{ "result": "CONDITIONAL", "roleEntityRef": "role:default/developer", "pluginId": "catalog", "resourceType": "catalog-entity", "permissionMapping": ["delete"], "conditions": { "allOf": [ { "anyOf": [ { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": [ "group" ] } }, { "rule": "IS_ENTITY_OWNER", "resourceType": "catalog-entity", "params": { "claims": [ "$ownerRefs" ] } } ] }, { "not": { "rule": "IS_ENTITY_KIND", "resourceType": "catalog-entity", "params": { "kinds": [ "api" ] } } } ] } }
In the previous example, the
role:default/developer
is granted the condition to delete catalog entities only if they are the entity owner or if the catalog entity belongs to a group. However, this condition does not apply if the catalog entity is an API.