Policy Snippets and Policy Snippet Versions are critical components in Policy composition. This reference will discuss the scope of policy snippets and importantly, the semantics to be used when creating policy snippet versions that can be used in policies.
Policy Snippets have different scopes, these include:
Policy Snippets are objects in SGNL that group Policy Snippet Versions. Policy Snippets are able to be created and partially updated after creation, however Policy Snippets are not able to be deleted once created. APIs are available to create and manage Policy Snippets.
In order to create a Policy Snippet, you must specify:
{
"displayName": "Name",
"description": "Description",
"scope": "PRINCIPAL",
"type": "STATE_MATCH",
"tenantId": "360E7BC5-9C00-4855-93CB-E74C2AA09C92"
}
A new Policy Snippet that will be Principal scoped
Property Name | Description |
---|---|
displayName | The display name you want to give to this snippet, this will appear during policy creation and management |
description | The description for this snippet, this will appear during policy creation and management |
scope | Value should be one of PRINCIPAL, ACTION, ASSET, or CONDITIONAL depending on the scope of the snippet you are creating |
type | Value should be STATE_MATCH - in the future, type will aid in the classification of snippets |
tenantId | The tenantId for the tenant in your SGNL client that you are managing |
In order to use a Policy Snippet, at least one Policy Snippet Version must be available. Policy Snippet Versions are created via the SGNL APIs.
Snippet Versions leverage SGNL’s Graph Directory for fast, efficient, and simplified evaluation. In order to create Policy Snippet Versions against entities in the graph, SGNL makes use of Nodes, Relationships, and Conditions to pattern match in the graph.
{
"snippetId": "360E7BC5-9C00-4855-93CB-E74C2AA09C92",
"description": "Description",
"nodes": [{}],
"relationships": [{}],
"condition": {},
"version": 1
}
Property Name | Property Type | Description |
---|---|---|
snippetId | string | The identifier for the Snippet you wish to create this Snippet Version within, this is available via the Snippet APIs |
description | string | A friendly description that describes this version of the Policy Snippet |
nodes | array of objects | Optional field to describe the types of entities that will be described in your Policy Snippet Version, see below |
relationships | array of objects | Optional field to define relationships between nodes used to evaluate the policy, see below |
condition | object | Optional field to constrain the search criteria of the nodes that should be evaluated, see below |
version | integer | The version for this Snippet Version, must be greater than the latest existing Snippet Version |
Nodes describe the types of entities that will be described in your Policy Snippet Version. They directly reference entities that are synchronized from the various data sources and entities you have configured inside of SGNL.
It is useful to define Nodes when your Policy Snippet Version refers to specific entities as part of the snippet evaluation. For instance, if your Policy Snippet Version evaluates whether an Okta User is from the Engineering Department, use Nodes to specify the Okta User node. You can then use the attributeName
from the Okta User node you’ve defined to specify a Condition that evaluates whether the user’s profile__department attribute is EqualTo
Engineering.
However, you do not need to define Nodes if your snippet evaluates whether a principal from any Data Source has privileged access to secure assets following your organizational naming convention (i.e. principalId
starts with “privileged-user”).
For example, you might want to use Nodes to reference user entities and group entities in the Policy Snippet Version you are creating from your Corporate Azure AD Tenant.
"nodes": [
{
"nodeName": "user",
"datasourceId": "{{aadDatasourceId}}",
"entityId": "{{aadUserEntityId}}",
"queryFieldName": "principalId"
},
{
"nodeName": "group",
"datasourceId": "{{aadDatasourceId}}",
"entityId": "{{aadGroupEntityId}}"
}
],
Property Name | Property Type | Description |
---|---|---|
datasourceId | string | The data source identifier for the datasource that contains the entity you’re reference in the snippet version, this identifier is available from the data sources API as ‘Id’, e.g. 1a82af25-291c-4eee-9d86-27d1a829c109 |
entityId | string | The entity identifier for the entity in your data source (e.g. Azure AD User) that you are referencing in the snippet version, this identifier is available from the entities API as ‘Id’, e.g. c4194907-ec87-4018-9a82-b84c73b3eb56 |
nodeName | string | Assign the node you are referencing a name, so that you can refer to it in other parts of the policy snippet version, when defining relationships or conditions |
queryFieldName | string | (Optional) This is the field name of a property in the access request, that you might want to use as an operand against one of the nodes (i.e. does the principalId in the access request relate to this node) |
Relationships reference the nodes you’ve expressed in the ’nodes’ expression of your Snippet Version in order to define which edges in the graph (the relationships) to traverse in order to evaluate policy.
It is useful to define Relationships when your Policy Snippet Version makes snippet evaluation decisions based on relationships between nodes. For instance, if your Policy Snippet Version evaluates whether an Okta User is a Manager of another Okta User, use Relationships to assess whether the principal requesting access has that relationship.
However, you do not need to define Relationships if your snippet version does not draw upon one of the well-defined relationships that SGNL creates within a datasource (see table on Relationship Names below for further detail) or a custom relationship that you have defined.
For example, you might want to use Relationships to take the user and group entities you referenced in the ‘nodes’ section of your Snippet Version to evaluate if the calling principal is a direct or indirect member of a group.
"relationships": [
{
"relationshipName": "GROUP",
"fromNodeName": "user",
"toNodeName": "group",
"transitive": false
}
],
Property Name | Property Type | Description |
---|---|---|
relationshipName | string | Determines the type of relationship to traverse, this can be one of the well-defined relationships that SGNL creates within a datasource, the join attribute used for entities of the same type across data sources, or a custom relationship type, see below for further detail |
fromNodeName | string | The name of the node to start this evaluation from, with a name as declared in the nodes expression in this snippet |
toNodeName | array of objects | The name of the node to terminate this evaluation, with a name as declared in the nodes expression in this snippet |
transitive | boolean | Can multiple relationships of this type be traversed to determine whether this snippet matches (e.g. direct member of a group, or indirect via nesting; direct report to a manager or an indirect report via reporting structure) |
Relationship Name | Description |
---|---|
MAPPED_TO | Created by a SGNL Administrator expressing Join Rules between entities of the same type across different data sources. MAPPED_TO relationships are automatically created when Join Rules are specified through the SGNL Console User Interface during Data Source configuration; they do not need to be specified again via Policy Snippet Versions. As an example, a Join Rule representing a MAPPED_TO relationship can be used to connect users in ServiceNow and users in AzureAD based on matching email addresses |
GROUP | Created by the SGNL Platform between entities (commonly users and/or groups) and groups. As an example, a GROUP relationship would commonly exist between 2 or more entities in a single data source (e.g. Azure AD), and commonly have a high cardinality of transitive relationships |
MANAGER | Created by the SGNL Platform between two user entities based on reporting hierarchy. As an example, a MANAGER relationship would exist between 2 or more entities in a single data source (e.g. Azure AD) based on manager relationships. Manager relationships are commonly transitive |
ASSIGNED_TO | Created by the SGNL Platform between user entities and cases/tickets. As an example, an ASSIGNED_TO relationship would commonly exist between a single user and 1 or more case/ticket entities in ITSM solutions (e.g. ServiceNow, Zendesk, JIRA). It is common for a single user to have many ASSIGNED_TO relationships |
ACCOUNT | Created by the SGNL Platform between case/ticket entities and Customers/Accounts. As an example, an ACCOUNT relationship would commonly exist between a single case/ticket and a single customer/account in the ITSM solution. |
Custom | Created by a SGNL Admin between an entity and another entity, based on a common join attribute and given a custom relationship name. Custom relationships are available to be created via the Relationships API. |
Conditions constrain the search criteria of the nodes that should be evaluated as part of the snippet evaluation and of the evaluation of the policy.
Conditions are extremely flexible, but we’ll start with a simple example. Say you want to ensure that a Principal Snippet Version should only be evaluated for users in a certain group, you would specify Node and Relationship fields as we have above, and in the conditions - you would specify a constraint that the group had to have a specific identifier to be matched.
"condition": {
"predicate": {
"lhs": {
"attributeName": "objectSid",
"nodeName": "group"
},
"op": "EqualTo",
"rhs": {
"string": "S-1-5-21-2562418665-3218585558-1813906818-1576"
}
}
}
Property Name | Property Type | Description |
---|---|---|
lhs | object | Specifies the “left-hand-side” of the condition evaluation, or the thing that should be compared - this can be any of the comparators detailed below |
op | string | The operator to use for the condition evaluation, supported operators include: Contains, EndsWith, EqualTo, GreaterThan, GreaterThanOrEqual, LessThan, LessThanOrEqualTo, RegexEqualTo, StartsWith |
rhs | object | Specifies the “right-hand-side” of the condition evaluation, or the thing that should be compared to - this can be any of the comparators detailed below |
Comparator | Description |
---|---|
queryFieldName | One of the fields in the request to the access service (e.g. “queryFieldName”: “action” ) |
nodeName, attributeName | Reference to a specific node declared in this snippet (from the nodes above) and an attribute on that node (e.g. "attributeName": "objectSid", "nodeName": "group") |
boolean | Whether a specific value is true or false |
date | Evaluates a date in the form of “YYYY-MM-DD” (e.g. "2022-10-18") |
dateTime | Evaluates a date in the form of “YYYY-MM-DDThh:mm:ssTZD” (e.g. "2022-10-19T05:24:06+00:00") |
integer | Evaluates an integer (e.g. 12) |
number | Evaluates a number (e.g. 12.32) |
string | Evaluates a string (e.g. "S-1-5-21-2562418665-3218585558-1813906818-1576") |
Thus far, we’ve looked only at Conditions that contain a single predicate, but conditions can support multiple predicates that can be evaluated together using additional operands.
"condition": {
"and": [
{
"predicate": {
"lhs": {
"attributeName": "employeeType",
"nodeName": "aadUser"
},
"op": "EqualTo",
"rhs": {
"string": "FTE"
}
}
},
{
"predicate": {
"lhs": {
"attributeName": "employeeId",
"nodeName": "aadUser"
},
"op": "RegexEqualTo",
"rhs": {
"string": ".*"
}
}
}
]
}
Predicate Operand | Description |
---|---|
and | All of the predicates need to be evaluated as true for this policy snippet to match |
or | Any of the predicates need to be evaluated as true for this policy snippet to match |
not | These predicates cannot be evaluated as true for this policy snippet to match |
Evaluate whether a principal is a direct member of an Azure AD Group
{
"nodes": [
{
"nodeName": "user",
"datasourceId": "{{aadDatasourceId}}",
"entityId": "{{aadUserEntityId}}",
"queryFieldName": "principalId"
},
{
"nodeName": "group",
"datasourceId": "{{aadDatasourceId}}",
"entityId": "{{aadGroupEntityId}}"
}
],
"relationships": [
{
"relationshipName": "GROUP",
"fromNodeName": "user",
"toNodeName": "group",
"transitive": false
}
],
"condition": {
"predicate": {
"lhs": {
"attributeName": "objectSid",
"nodeName": "group"
},
"op": "EqualTo",
"rhs": {
"string": "S-1-5-21-2562418665-3218585558-1813906818-1576"
}
}
},
"description": "Belongs to our AAD Employee Group",
"snippetId": "{{snippetId}}",
"version": 1
}
Evaluate whether a principal has a title of Admin or Developer as provided by Okta
{
"nodes": [
{
"nodeName": "oktaUser",
"datasourceId": "{{oktaDatasourceId}}",
"entityId": "{{oktaUserEntityId}}",
"queryFieldName": "principalId"
}
],
"condition": {
"or": [
{
"predicate": {
"lhs": {
"attributeName": "profile__title",
"nodeName": "oktaUser"
},
"op": "EqualTo",
"rhs": {
"string": "admin"
}
}
},
{
"predicate": {
"lhs": {
"attributeName": "profile__title",
"nodeName": "oktaUser"
},
"op": "EqualTo",
"rhs": {
"string": "developer"
}
}
}
]
},
"description": "User is an Admin or a Developer",
"snippetId": "{{snippetId}}",
"version": 1
}
Determine whether the asset attempting to be accessed is a Customer Salesforce Account
{
"nodes": [
{
"queryFieldName": "assetId",
"datasourceId": "{{sfdcDatasourceId}}",
"entityId": "{{sfdcAccountEntityId}}",
"nodeName": "account"
}
],
"condition": {
"predicate": {
"lhs": {
"attributeName": "Type",
"nodeName": "account"
},
"op": "EqualTo",
"rhs": {
"string": "Customer"
}
}
},
"description": "Salesforce Customer Accounts",
"snippetId": "{{snippetId}}",
"version": 1
}
Determine whether the asset attempting to be accessed is one of the sensitive infrastructure assets, following our organizational naming convention
{
"condition": {
"predicate": {
"lhs": {
"queryFieldName": "assetId"
},
"op": "StartsWith",
"rhs": {
"string": "customer-prod-"
}
}
},
"description": "Production Assets owned by a Customer",
"snippetId": "{{snippetId}}",
"version": 1
}
Determine whether the calling principal is the assignee on an active case that belongs to the customer they are requesting access for
{
"nodes": [
{
"datasourceId": "{{snowDatasourceId}}",
"entityId": "{{snowUserEntityId}}",
"nodeName": "snowUser",
"queryFieldName": "principalId"
},
{
"datasourceId": "{{snowDatasourceId}}",
"entityId": "{{snowCaseEntityId}}",
"nodeName": "snowCase"
},
{
"datasourceId": "{{snowDatasourceId}}",
"entityId": "{{snowAccountEntityId}}",
"nodeName": "snowAccount",
"queryFieldName": "assetId"
}
],
"relationships": [
{
"relationshipName": "ASSIGNED_TO",
"fromNodeName": "snowCase",
"toNodeName": "snowUser"
},
{
"relationshipName": "ACCOUNT",
"fromNodeName": "snowCase",
"toNodeName": "snowAccount"
}
],
"condition": {
"predicate": {
"lhs": {
"attributeName": "active",
"nodeName": "snowCase"
},
"op": "EqualTo",
"rhs": {
"boolean": true
}
}
},
"description": "User is assigned active case for the customer",
"snippetId": "{{snippetId}}",
"version": 1
}
Determine whether the calling principal is the manager of a ServiceNow Group
{
"nodes": [
{
"datasourceId": "{{snowDatasourceId}}",
"entityId": "{{snowUserEntityId}}",
"nodeName": "snowUser",
"queryFieldName": "principalId"
},
{
"datasourceId": "{{snowDatasourceId}}",
"entityId": "{{snowGroupEntityId}}",
"nodeName": "snowGroup"
}
],
"relationships": [
{
"relationshipName": "MANAGER",
"fromNodeName": "snowGroup",
"toNodeName": "snowUser"
}
],
"description": "Group Manager",
"snippetId": "{{snippetId}}",
"version": 1
}
Evaluate whether a principal can perform a login action
{
"condition": {
"predicate": {
"lhs": {
"queryFieldName": "action"
},
"op": "EqualTo",
"rhs": {
"string": "login"
}
}
},
"version": 1,
"description": "Perform a Login Action",
"snippetId": "{{snippetId}}"
}
Evaluate whether a principal can perform a read action
{
"condition": {
"predicate": {
"lhs": {
"queryFieldName": "action"
},
"op": "EqualTo",
"rhs": {
"string": "read"
}
}
},
"version": 1,
"description": "Perform a Read Action",
"snippetId": "{{snippetId}}"
}