This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Sample Protection Workflows

Workflows to show how to protect sample data.

This section provides workflows on how to protect the following sample data:

  • Credit Card Number (CCN)
  • Date of Birth (DOB)

1 - Policy Workflow

Explaining the policy workflow.

Summary

This section outlines a workflow for creating a policy. This workflow is used in examples and scripts that are provided in related sections of the documentation.

Here are the general steps of the Policy Workflow:

  1. Initialize Policy Management
  2. Prepare Data Element
  3. Create Member Source
  4. Create Role
  5. Assign Member Source to Role
  6. Create Policy Shell
  7. Define Rule with Data Element and Role
  8. Create Datastore
  9. Deploy Policy to a Datastore
  10. Confirm Deployment

Each step of the workflow has a sub-page that describes the workflow in detail, including a description, purpose, inputs, and outputs. Other sections of this documentation show examples of these steps for creating a policy, such as a policy to protect a credit card number.

Description

The workflow described in this section assumes that the reader is working in an environment where the core Protegrity platform components are already installed, accessible, and functioning correctly. The steps focus on policy creation and deployment, not on platform installation, infrastructure provisioning, or troubleshooting underlying system issues.

Assumptions

To execute any CLI or API command in this example, the following assumptions have been made:

  • You are operating on a new AI Team Edition setup.
    • Set up the AI Team Edition by installing the Protegrity Provisioned Cluster. For more information about installing the PPC, refer to the section Installing PPC.
  • You are connected to the Policy Manager container.
    • Connect to the Policy Manager container by deploying the Protegrity Policy Manager. For more information about deploying the Protegrity Policy Manager, refer to the section Installing Policy Workbench.

CLI Examples

To execute any CLI command in this example, the following additional assumption has been made:

API Examples

To execute any API command in this example, the following additional assumption has been made:

  • You have access to the Protegrity Policy Management REST APIs.

Purpose

To clearly establish the scope of the workflow and avoid ambiguity about what the documentation covers versus what is expected to be completed beforehand. By defining these assumptions up front, the workflow can focus on explaining policy behavior and intent, rather than environmental setup.

Outcome

With these assumptions satisfied, the reader can proceed through the workflow steps with the expectation that each command or configuration action will succeed without requiring additional environment preparation.

Tips

  • If any assumption is not met, resolve it before continuing with the workflow to avoid misleading errors later.
  • For environment setup, installation, or operational guidance, refer to the dedicated platform installation and operations documentation rather than this workflow.

1.1 - Initialize Policy Management

Workflow to initialize policy management.

Summary

Initialize the Policy Management environment so it can store keys, policies, and configuration data required for all subsequent steps.

Description

This step prepares the Policy Management subsystem by creating the internal key material and policy repository used by the API. Initialization ensures that the environment is in a valid state before you create any data elements, roles, policies, or datastores.

Purpose

To set up the foundational Policy Management environment so that all future API commands operate against a valid and initialized repository.

Prerequisites

None.

Initialization is the first action performed before any policy‑related configuration can occur.

Inputs

No inputs are required.

The initialization command runs with system defaults and prepares the environment automatically.

Outcome

Policy Management is fully initialized, and the system is ready to accept policy configuration commands. After this step completes, proceed to create data elements, roles, member sources, and policies using the API.

Conceptual Examples

  • Example 1: A new environment has just been installed. Initialize the internal structures needed so that the administrator can begin defining data protection policies.
  • Example 2: A test or sandbox environment is reset. Initialization is performed again to rebuild the policy repository before running new API‑based examples or scripts.

Tips

None.

1.2 - Prepare Data Element

Workflow to prepare data element.

Summary

Create a Data Element that defines the sensitive data type and how it will be protected. For example, whether the data is tokenzied, encrypted, or masked.

Description

A Data Element describes a category of sensitive information, such as credit card numbers, Social Security numbers, names, or email addresses. It then defines the protection method that applies to the category. This includes the protection algorithm, formatting constraints, visibility rules, and validation options. A Data Element is the foundation of all policy rules. Policies reference Data Elements to determine how data is protected and under which circumstances it may be revealed or transformed.

Purpose

To formally define what data will be protected and how it should be processed. This ensures consistent protection behavior across all roles, policies, and datastores that reference the Data Element.

Prerequisites

None.

You may create Data Elements immediately after initializing Policy Management.

Inputs

Typical inputs may include:

  • Data Element name
  • Description
  • Protection method. For example, tokenization, encryption, and masking.
  • Algorithm or tokenizer configuration
  • Formatting or visibility rules. For example, keep last four digits.
  • Validation rules. For example, Luhn checks for credit cards.

Sub-tasks

Sometimes you might want to create a mask or use a special alphabet in your policy.

Create Mask
  • When and why
    • Create a Mask when you need to partially or fully hide sensitive data during presentation to end‑users. Masks allow you to obfuscate some or all characters. For example, showing only the last four digits. Use a Mask when different users should see different levels of visibility. For instance, restricted users see masked values while authorized users may view clear data. Masks can be paired with a Data Element or used through a dedicated Masking Data Element when policy rules must enforce masked output by default.

Create Alphabet

  • When and why
    • Create an Alphabet when the data you are protecting includes characters from specific languages or extended Unicode sets, such as Spanish, Polish, Korean, or other multilingual inputs. Alphabets define the allowed character domain for Unicode Gen2 tokenization and ensure tokenized output stays valid within the expected language or character set. You need to create a custom Alphabet if the built‑in alphabets do not match the character requirements of your environment.

Outcome

A Data Element is created and stored in the Policy Management environment. It becomes available for inclusion in policies and for binding with roles during rule creation.

Conceptual Examples

  • Example 1: Credit Card Tokenization
    • A Data Element named de_credit_card is created to tokenize credit card numbers using a chosen tokenizer. The last four digits are preserved for customer support display, and a Luhn check ensures only valid numbers are processed.
  • Example 2: Email Address Masking
    • A Data Element named de_email is created to enforce consistent masking of email addresses, such as replacing the user portion with asterisks while preserving the domain.

Tips

  • Use descriptive names so Data Elements are easy to identify when building policies.
  • Choose protection methods based on business use cases. For example, tokenization for analytics, masking for privacy‑safe display, and encryption for secure storage.
  • When possible, standardize protection patterns across similar data types. For example, all PAN fields follow the same tokenization rule.
  • Before creating many Data Elements, define a naming convention. For example, de_<datatype>_<method>.

1.3 - Create Member Source

Workflow to create a member source.

Summary

Create a Member Source that defines the external system from which user and group identities will be imported for use in roles and policies.

Description

A Member Source establishes a connection to an identity provider, such as a directory service, a database, or a simple user or group file. This ensures that real users and service accounts can be referenced within policy roles. Member Sources supply the identities that roles draw from, allowing the system to stay aligned with organizational updates to accounts, groups, and permissions.

Purpose

To provide a trusted and maintainable source of user and group information for policy enforcement. Member Sources ensure that roles are populated automatically or programmatically using authoritative identity data rather than manual user entry.

Prerequisites

None.

Member Sources can be created at any time, though they are typically defined before assigning them to roles.

Inputs

Inputs vary depending on the type of Member Source, but commonly include:

  • Source type. For example, file, directory, database, and API.
  • Location or connection settings. For example, paths, URLs, and hostnames.
  • User and group data. For example, lists, queries, or mappings.
  • Access credentials if required.

Outcome

A Member Source is created and available for assignment to one or more roles. Once assigned, the Member Source becomes the mechanism through which those roles obtain their user and group membership.

Conceptual Examples

  • Example 1: File‑Based Member Source for Testing
    • A small file containing sample users and groups is created for a development environment. A Member Source is configured to read from this file, populating roles without connecting to a production identity system.
  • Example 2: Directory‑Backed Member Source for Production
    • A Member Source is configured to point to an organization’s central directory service. When new employees join or leave teams, their group membership updates automatically in the Member Source, and corresponding roles inherit those changes.

Tips

  • Use file‑based Member Sources for demos, pilots, and sandbox environments. They are easy to set up and reset.
  • For production, use a centralized identity provider to avoid manually updating user lists.
  • Keep Member Source names descriptive. For example, ms_hr_directory and ms_test_users.
  • Confirm that users and groups in the Member Source align with your expected role design to avoid misconfiguration during rule creation.

1.4 - Create Role

Workflow to create a role.

Summary

Create a Role to represent a group of users or service accounts that will receive specific permissions in a policy.

Description

A Role is a logical container that defines who will receive access to a Data Element within a policy. Roles do not hold permissions on their own. Instead, they become meaningful when paired with Data Elements and permissions in policy rules. Roles allow you to centralize and standardize access behavior across multiple users by grouping identities into functional categories such as Data Analysts, Customer Support, or Payment Service Applications.

Purpose

To establish an authorization boundary that policies can reference when granting or restricting access to sensitive data. Roles allow policies to express business intent clearly. For example, This group may tokenize credit card data,” or Only this role may unprotect values.

Prerequisites

None.

Roles can be created at any time, although they become active only after a Member Source is assigned in the next step.

Inputs

Typical inputs when creating a Role include:

  • Role name.
  • Description of its business purpose.
  • Assignment mode. For example, manual assignment versus assignment from a Member Source.

These inputs help clearly define the role’s identity and intended usage in policy rules.

Outcome

A Role is created and ready to populate with members. It can now be linked to a Member Source and later associated with Data Elements and permissions within a policy.

Conceptual Examples

  • Example 1: Protection Role
    • A role named r_cc_protect is created for payment‑processing applications responsible for protecting credit card numbers using tokenization before storage.
  • Example 2: Limited‑Access Role
    • A role named r_customer_support_masked is created for agents who may view masked customer data but cannot unprotect or view clear‑text values.

Tips

  • Keep role names short but descriptive. For example r_<domain>_<capability>
  • Use separate roles for different permission levels, such as protect versus unprotect, to keep policies clean and auditable.
  • Avoid putting too many responsibilities in a single role. For example, smaller, purpose‑specific roles simplify long‑term maintenance.
  • If possible, design roles around business functions and not individuals, to avoid maintenance churn.
  • Note this can be created with the option of ALL_USERS.

1.5 - Assign Member Source to Role

Workflow to assign member source to role.

Summary

Assign a user or group from a Member Source to a Role so the Role is backed by real identities that can receive policy permissions. This step links the Role to the identities it should represent and, when synchronized, imports current membership from the source into the Role.

Description

This step connects a previously created Role to a specific user or group that exists in a Member Source. For example, LDAP, Active Directory, Azure AD, a database, or a file-based source. Using pim create roles members, you define which source-backed identity should belong to the Role. After that, running a role sync updates the Role with membership information from the source.

This is the point where a Role stops being only a named container and becomes tied to actual enterprise identities. Once this binding exists, the Role can be used meaningfully in policy rules, because the system can map policy access decisions back to real users, groups, or service accounts.

Purpose

To bind a Role to authoritative identities from a Member Source so that policy permissions apply to real users or groups rather than to an empty logical object. This ensures policy enforcement reflects the organization’s existing identity model and can stay aligned with membership changes in the source system over time.

Prerequisites

  • A Role must already exist.
  • A Member Source must already be created and available.
  • The user or group to be assigned must exist in that Member Source. It should also be identifiable by name and type, with an optional synchronization identifier if required by the source.

Inputs

Typical inputs for this step include:

  • Role UID or Role identifier.
  • Member name from the source, such as user or group name.
  • Source UID identifying the Member Source.
  • Member type, such as USER or GROUP.
  • Optional synchronization identifier, depending on the source and membership model.

You may also optionally run a synchronization operation after assignment so that the Role reflects current membership from the source immediately.

Outcome

The Role now has a source-backed member assignment and can be used as an identity-backed object in policy rules. After synchronization, the Role reflects the current membership information from the Member Source, allowing policy access to apply to actual users, groups, or service accounts. Without this step, policies may be defined correctly but still not grant access to anyone in practice.

Conceptual Examples

  • Example 1: Assigning an LDAP Group to a Protection Role
    • A Role named r_cc_protect is linked to a group such as pci_analysts from an LDAP Member Source. After the role is synchronized, all current members of that LDAP group become the effective identities behind the Role. This allows them to receive the permissions defined later in the policy.
  • Example 2: Assigning a Service Account User from a File-Based Source
    • In a test environment, a file-based Member Source contains sample users. A specific service account user is attached to a Role so that demo or automation workflows can exercise the policy. After synchronization, that Role can be referenced in rules just like a production role backed by a centralized identity provider.

Tips

  • Prefer assigning groups instead of individual users when possible. This reduces maintenance and keeps Role design aligned with business functions. This is consistent with the examples and scripts, which commonly model role membership using source groups such as pci_analysts or hr_analysts. Note that some of the examples will not use groups.
  • Run a role synchronization after assigning the member source so the Role reflects current source membership immediately. The example workflow explicitly marks sync as recommended.
  • Use clear naming and role design so the source membership aligns with the intended policy behavior. A mismatch between Role purpose and source membership can make later rule definitions misleading or ineffective. This follows the workflow guidance that roles should map to business purpose and member sources should align with expected role design.

1.6 - Create Policy Shell

Workflow to create a policy shell.

Summary

Create an empty Policy Shell that acts as the container for roles, data elements, rules, and deployment configuration.

Description

A Policy Shell is the foundational policy object that holds all components of a complete policy but initially contains no rules or assignments. It defines the policy’s identity, which is its name, description, and purpose, and prepares the environment for adding data elements, roles, permissions, and datastores. Creating a Policy Shell is the administrative starting point for constructing a full policy.

Purpose

To establish a dedicated policy container that will later be populated with rules governing how sensitive data is protected and who may access it. The Policy Shell provides organizational structure and acts as the anchor for all subsequent policy configuration steps.

Prerequisites

  • Policy Management must be initialized. For more information about the initialization step, refer to section Initialize Policy Management.
  • Any Data Elements, Roles, or Member Sources you plan to use may optionally be created beforehand, but are not required at this step.

Inputs

Typical inputs for this step include:

  • Policy name.
  • Policy description.
  • Optional metadata or tags for categorization.

At this stage, no data elements, roles, or permissions are defined.Only the policy container itself is defined.

Outcome

A new, empty policy is created and ready to be configured. You can now begin attaching Data Elements, assigning Roles, defining permissions, and associating Datastores.

Conceptual Examples

  • Example 1: Credit Card Protection Policy
    • An administrator creates a new policy shell named policy_credit_card intended to govern how credit card numbers are tokenized and which users can unprotect them.
  • Example 2: Customer Support Access Policy
    • A policy shell named policy_support_data is created to organize rules that provide masked data to customer service roles while restricting access to full values.

Tips

  • Choose clear and descriptive names so the purpose of the policy is immediately recognizable.
  • Create separate policies for distinct business domains to simplify auditing and updates. For example payments, HR, or analytics.
  • Avoid overloading one policy with too many unrelated Data Elements. Smaller policies are easier to manage and review.
  • Think of the Policy Shell as the project folder for everything that will follow.

1.7 - Define Rule with Data Element and Role

Workflow to define rule with Data Element and Role.

Summary

Define a rule that specifies how a Role may interact with a Data Element by assigning permissions such as protect, unprotect, mask, or view.

Description

A Rule establishes the relationship between a Data Element and a Role within a policy. It defines which operations members of that Role are allowed to perform on the Data Element. For example, protecting the data using tokenization, viewing masked values, or unprotecting the data if permitted. Rules are the core of policy logic. They determine the behavior of the system when a user or application attempts to access or process sensitive data.

Purpose

To define who, the Role; can do what, permission; to which data, the Data Element. Rules translate business intent into enforceable policy logic and ensure consistent application of protection standards across all datastores.

Prerequisites

  • A Policy Shell must exist. For more information about creating a policy shell, refer to the section Create Policy Shell.
  • A Data Element must be created. For more information about creating a data element, refer to the section Prepare Data Element.
  • A Role must be created and associated with a Member Source. For more information about creating a member source, creating a role, and assigning member source to the role, refer to the following sections:

Inputs

Typical inputs for this step include:

  • Role to which the rule applies.
  • Data Element being controlled.
  • Permissions. For example, protect, unprotect, mask, and view.
  • Optional output behavior. For example, allow masked return only.
  • Optional masking configuration if applicable.

Outcome

A rule is added to the policy, granting or restricting specific interactions between the designated Role and Data Element. The policy now contains enforceable access logic that dictates how protected data will behave for different types of users or applications.

Conceptual Examples

  • Example 1: Protect‑Only Rule
    • A rule is created to allow the r_cc_protect role to protect credit card numbers using tokenization but not unprotect them. Applications using this role can store sensitive data safely, but cannot retrieve clear values.
  • Example 2: Masked‑View Rule
    • A rule is created for the r_support_masked role, allowing customer support teams to view masked data but not access clear text or perform protection operations.

Tips

  • Define rules with the principle of least privilege. Only grant operations that are required for the role’s function.
  • Avoid giving unprotect permissions unless absolutely necessary. Restricting this keeps sensitive data safe.
  • Use naming conventions to help visually match roles to rule types. For example, r_<domain>_protect and r_<domain>_viewmasked.
  • For complex policies, document why each rule exists to simplify future audits or updates.

1.8 - Create Datastore

Workflow to create datastore.

Summary

Create a Datastore entry that represents the application, service, or infrastructure component where the policy will be deployed and enforced.

Description

A Datastore defines the environment in which a policy will operate, such as an application server, a database engine, an API endpoint, or another enforcement point. It represents the location where data is accessed or processed and where the policy rules, which have been defined earlier through roles and data elements, will be applied. Creating a Datastore registers this target environment with the policy management system so that policies can later be deployed to it.

Purpose

To identify and register a policy enforcement location so the policy system knows where the rules should run. Without a Datastore, a policy cannot be enforced, because the system has no target environment to push the configuration to.

Prerequisites

  • A Policy Shell must exist. For more information about creating a policy shell, refer to the section Create Policy Shell.
  • Rules that define how roles interact with data elements should already be created. For more information about defining roles, refer to the section Define Rule with Data Element and Role.
  • The environment where the Datastore will be mapped. For example, application, service, or host should be known.

Inputs

Typical Datastore inputs include:

  • Name of the Datastore.
  • Type. For examle, application, service, and database.
  • Connection information. For example, hostnames, endpoints, and identifiers.
  • Optional metadata. For example, environment tags such as dev, test, or production.

Actual inputs depend on the type of enforcement point being registered.

Outcome

A Datastore is created and available for policy deployment. Policies can now be associated with this Datastore so that enforcement can occur during real data access operations.

Conceptual Examples

None.

Tips

  • Use consistent naming to distinguish environments. For example, ds_payments_prod and ds_analytics_dev.
  • Create separate Datastores for different systems, even if they use the same policy, to maintain clear deployment boundaries.
  • Map Datastores to actual data‑flow locations. Wherever sensitive data is read, processed, or stored, a Datastore should exist.
  • Confirm the destination system is reachable or properly registered before deploying policies to avoid deployment failures.

1.9 - Deploy Policy to a Datastore

Workflow to deploy policy to datastore.

Summary

Deploy the completed policy to a Datastore so that its rules are actively enforced during real data access operations.

Description

Deploying a policy makes it operational on a specific Datastore, such as an application, service, database, or other enforcement point. Until deployment occurs, a policy exists only as a configuration object. Deployment pushes all rules, including Data Elements, Roles, and permissions, to the target Datastore. This ensures that the runtime environment can apply them when users or applications interact with sensitive data.

Purpose

To activate the policy in an environment where protected data is accessed. Deployment ensures that the Datastore enforces the correct behavior, such as tokenization, masking, unprotect permissions, or other rules, based on the policy definition.

Prerequisites

  • A Policy Shell must be created. For more information about creating a policy shell, refer to the section Create Policy Shell.
  • The policy must contain rules that bind Data Elements to Roles. For more information about defining roles, refer to the section Define Rule with Data Element and Role.
  • A Datastore must exist. For more information about creating a datastore, refer to the section Create Datastore.
  • Connectivity or registration between Policy Management and the Datastore should be confirmed.

Inputs

Typical deployment inputs include:

  • Policy name or ID.
  • Datastore name or ID.
  • Optional deployment parameters, depending on environment. For example, environment tags and version notes.

Outcome

The policy is successfully deployed to the specified Datastore. The enforcement point is now configured to apply the defined data protection rules whenever sensitive data is read, written, or processed.

Conceptual Examples

Not applicable.

Tips

  • Always verify that the Datastore is correctly registered before deploying to avoid deployment errors.
  • If you maintain separate development, test, and production environments, use clearly named Datastores for each to avoid mis-deployment.
  • After deployment, test a few representative access scenarios to confirm enforcement works as intended.
  • Consider using versioning or descriptions on deployments for auditability and rollback clarity.

1.10 - Confirm Deployment

Workflow to confirm deployment.

Summary

Verify that the policy has been successfully deployed to the intended Datastore by retrieving deployment information.

Description

After deploying a policy, it is important to confirm that the system has registered the deployment correctly. The API provides a command to retrieve a list of all Datastores along with the policies currently connected to them. This verification step ensures that the deployment completed successfully and that the Datastore is now enforcing the appropriate policy rules.

Purpose

To confirm that the policy deployment is active and correctly associated with the target Datastore. This step provides assurance that the configuration is in effect and ready for runtime enforcement.

Prerequisites

A policy must have been deployed to a Datastore. For more information about deploying a policy, refer to the section Deploy Policy to a Datastore.

Inputs

No inputs are required. The confirmation command runs without arguments.

Outcome

You receive a deployment report listing each Datastore by UID and the policies associated with it. If the policy appears in the list, then deployment is confirmed.

Conceptual Examples

Not applicable.

Tips

  • If the expected policy does not appear in the list, re‑run the deployment or check for configuration errors.
  • Use this command routinely when validating changes or troubleshooting application behavior.
  • Keep track of Datastore UIDs to avoid confusion in complex environments.

2 - Create a policy to protect Credit Card Number (CCN)

Workflow example to protect CCN.

Goal

Create a policy that protects Credit Card Number (CCN) using CCN data element, with:

  • At least one role.
  • At least one member source assigned to that role.
  • Deployed to at least one datastore.

This example provides a walkthrough of the complete workflow to create a policy to protect a Credit Card Number (CCN) with tokenization using Protegrity CLI and REST APIs. The example includes defining a CCN Data Element and access controls to deploy a policy that protectors can enforce at runtime. The CCNs have a specific format and must comply with existing regulations. Hence, this example uses the Credit Card token type, with a common usability pattern of keeping the last four digits visible while tokenizing the rest.

Before using the CLI or the REST APIs, determine the properties required for the CCNs. For example:

  • How many digits should be in the clear.
  • Whether invalid values should be rejected or tokenized, for example, via Luhn handling.
  • What security operations should be allowed. For example, protect, unprotect, or reprotect.

These properties determine how the data element and the policy rules that are configured. They determine what applications and users will experience when data is protected or unprotected.

A key design choice specific to tokenization is selecting the tokenizer. You need to choose a tokenizer because it defines the tokenization engine and lookup-table strategy. Protegrity uses the tokenizer to deterministically transform a CCN into a same-length token. The tokenizer controls how the CCN digits are mapped into tokens so the protector can reliably produce and resolve tokens under policy. Protegrity offers multiple Static Lookup Table (SLT) tokenizer variants, such as, SLT_1_3, SLT_2_3, SLT_1_6, and SLT_2_6, which differ mainly in lookup-table design and operational footprint. For most CCN use cases, this example uses SLT_2_3 because it strikes a practical balance of memory usage and performance while working well for standard PAN lengths. This avoids the much larger memory footprint of the _6 options unless specifically required.

Assumptions

To execute any CLI or API command in this example, the following assumptions have been made:

  • You are operating on a new AI Team Edition setup.
    • Set up the AI Team Edition by installing the Protegrity Provisioned Cluster. For more information about installing the PPC, refer to the section Installing PPC.
  • You are connected to the Policy Manager container.
    • Connect to the Policy Manager container by deploying the Protegrity Policy Manager. For more information about deploying the Protegrity Policy Manager, refer to the section Installing Policy Workbench.

CLI Examples

To execute any CLI command in this example, the following additional assumption has been made:

API Examples

To execute any API command in this example, the following additional assumption has been made:

  • You have access to the Protegrity Policy Management REST APIs.

2.1 - Initialize Policy Management

Initializing the policy management.

This step initializes the Policy Management system. This step needs to be executed only once.

CLI Code

pim invoke init

CLI Actual Output

✅ PIM successfully initialized (bootstrapped).

API Endpoint

POST /pim/init

Get Gateway host address

export GW_HOST="$(kubectl get gateway pty-main -n api-gateway -o jsonpath='{.status.addresses[0].value}')"

Generate JWT token

TOKEN=$(curl -k -s  https://$GW_HOST/api/v1/auth/login/token \
## -X Post \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'loginname=workbench' \
-d 'password=Admin123!' \
-D - -o /dev/null 2>&1 | grep -i 'pty_access_jwt_token:' | sed 's/pty_access_jwt_token: //' | tr -d '\r') && echo "${TOKEN:0:10}"

API Code

curl -k -H "Authorization: Bearer ${TOKEN}" -X POST "https://{$GW_HOST}/pty/v2/pim/init" -H "accept: application/json"

API Actual Output:

It does not return any output as response.

2.2 - Prepare Data Element

Create a data element.

What you are doing

Creating the data element that defines:

  • What is protected: CCN.
  • How it is protected: Tokenization settings.

Why it matters

Data elements are the protection building blocks that will be granted the permissions by the policy.

Tips

  • To keep nothing in clear: set --from-left 0 --from-right 0.
  • To avoid Luhn enforcement: omit --invalid-luhn-digit.

CLI Code

pim create dataelements token credit-card --name "de_ccn_token" --description "Tokenize credit card numbers, keeping last 4 chars in clear" --tokenizer "SLT_1_6" --from-left 0 --from-right 4 --invalid-luhn-digit

CLI Actual Output

UID NAME DESCRIPTION                                                TOKENIZER  FROMLEFT  FROMRIGHT  VALUEIDENTIFICATION
15  de_ccn_token  Tokenize credit card numbers, keeping last 4 chars in clear  SLT_1_6    0         4          {'invalidCardType': False, 'invalidLuhnDigit': True, 'alphabeticIndicator': False, 'alphabeticIndicatorPosition': 1}

API Endpoint

POST /pim/dataelements

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/dataelements" \
-d '{
"name": "de_ccn_token",
"description": "Tokenize credit card numbers, keeping last 4 chars in clear",
"creditCardToken": {
"tokenizer": "SLT_1_6",
"fromLeft": 0,
"fromRight": 4,
"valueIdentification": {
"invalidCardType": false,
"invalidLuhnDigit": true,
"alphabeticIndicator": false,
"alphabeticIndicatorPosition": 1
}
}
}'

API Actual Output

{"uid":"1","name":"de_ccn_token","description":"Tokenize credit card numbers, keeping last 4 chars in clear","creditCardToken":{"tokenizer":"SLT_1_6","fromLeft":0,"fromRight":4,"valueIdentification":{"invalidCardType":false,"invalidLuhnDigit":true,"alphabeticIndicator":false,"alphabeticIndicatorPosition":1}}}

2.2.1 - Create Mask

Create mask for the CCN.

What you are doing

Creating the mask that is applied to the unprotection permission of a data element to customize how data is displayed. The mask is to be used later on de_ccn_token.

Why it matters

Creating a mask defines what characters are displayed in the clear when the data is unprotected. The mask is optionally applied to an unprotection to display only a certain value to a consumer of the data.

CLI Code

pim create masks --name "clear_mask" --from-left 0 --from-right 4 --character "*"

CLI Actual Output

## Name        Description  Fromleft  Fromright  Masked  Character  Uid
clear_mask               0         4          False   *          1

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/masks" \
-d '{
"name": "clear_mask",
"masked": true,
"fromLeft": 0,
"fromRight": 4,
"character": "*"
}'

API Endpoint

POST /pim/masks

API Actual Output

{"uid":"1","name":"clear_mask","description":"","fromLeft":0,"fromRight":4,"masked":true,"character":"*"}

2.3 - Create Member Source

Create member source for the CCN.

What you are doing

You are creating a Member Source, which is a source that informs Protegrity where user identities are stored. For example, Active Directory, LDAP, Azure AD or a text file. This configuration stores the connection and the lookup context that Protegrity needs to discover users and groups that are then mapped to Roles.

Why it matters

Policies do not grant access to individual users directly. Access is granted through roles, and roles are populated from external identity systems via member sources. Without a source, you cannot reliably attach real enterprise groups or users to roles, synchronize memberships, or enforce policy access the same way your organization manages identity.

CLI Code

pim create sources file --name test-file --user-file exampleusers.txt --group-file examplegroups.txt

CLI Actual Output

NAME       DESCRIPTION  TYPE             USERFILE          GROUPFILE          TIMEOUT  UID
test-file               SourceType.FILE  exampleusers.txt  examplegroups.txt  120      1

API Endpoint

POST /pim/sources

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/sources" \
-d '{
"name": "test-file",
"type": "FILE",
"connection": {
"userFile": "exampleusers.txt",
"groupFile": "examplegroups.txt"
}
}'

API Actual Output

{"name":"test-file","description":"","type":"FILE","connection":{"userFile":"exampleusers.txt","groupFile":"examplegroups.txt"},"uid":"1"}

2.3.1 - Test New Member Source

Test whether the member source has been successfully created.

CLI Code

pim invoke sources test 1

CLI Actual Output

+----------------+--------+---------+
|      type      | passed | message |
+----------------+--------+---------+
|   connection   |  True  |         |
| authentication |  True  |         |
|     groups     |  True  |         |
|     users      |  True  |         |
+----------------+--------+---------+ 

API Endpoint

POST /pim/sources/{SOURCE_UID}/test

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/sources/1/test"

API Actual Output

{"connection":{"passed":true,"message":""},"authentication":{"passed":true,"message":""},"groups":{"passed":true,"message":""},"users":{"passed":true,"message":""}}

2.4 - Create Role

Create a role.

What you are doing

Creating the role that represents who can perform operations against the data element.

Why it matters

Permissions are granted to roles and roles map to users or groups, ideally from member sources.

Tips

  • If you keep the --allow-all option in the command, then set ALLOWALL to True.
  • Consider which user needs what level of access and create a role for each set of users.

CLI Code

pim create roles role --name "role_protect_ccn" --description "This role have access to protect CCN data" --mode "MANUAL"

CLI Actual Output

NAME              DESCRIPTION                                MODE             ALLOWALL  UID
role_protect_ccn  This role have access to protect CCN data  RoleMode.MANUAL  False     1

API Endpoint

POST /pim/roles

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles" \
-d '{
"name": "role_protect_ccn",
"description": "This role have access to protect CCN data",
"mode": "MANUAL",
"allowAll": false
}'

API Actual Output

{"name":"role_protect_ccn","description":"This role have access to protect CCN data","mode":"MANUAL","uid":"1","allowAll":false}

2.5 - Assign Member Source to Role

Assign member source to the role.

What you are doing

You are attaching a specific user or group from a member source to a role. This creates the who belongs in this role binding. If you run synchronization, it retrieves the current membership from the source into the role.

Why it matters

This is the step that turns a role from a named container into an identity-backed access control object. If you do not assign a source user or group to the role and synchronize it, then no real identities are associated with that role. This means that your policy rules may exist, but nobody in your organization will actually have the access that those rules intend to grant.

CLI Code

pim create roles members 1 --member "exampleuser1,1,USER"

CLI Actual Output

## Name          Source  Type             Uid
exampleuser1  1       MemberType.USER  1  

API Endpoint

POST /pim/roles/{SOURCE_UID}/test

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles/1/members" \
-d '[
{
"name": "exampleuser1",
"source": "1",
"type": "USER"
}
]'

API Actual Output

[{"type":"USER","name":"exampleuser1","syncid":"exampleuser1","uid":"1","source":"1"}]

2.5.1 - Synchronize Member Source

Synchronize the member source.

Connect and synchronize the users with your member source.

CLI Code

pim invoke roles sync 1

CLI Actual Output

Successfully synchronized members for role with UID '1'.  

API Endpoint

POST /pim/roles/{SOURCE_UID}/sync

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles/1/sync"

API Actual Output

The API does not return any output as response.

2.6 - Create Policy Shell

Create a policy shell.

What you are doing

Creating the policy that will hold the access rules. For example, Data Element, Role, and Rules.

Why it matters

The policy is the object that ties together the pieces and becomes deployable.

Tips

Multiple Roles, multiple data elements, and their corresponding rules can be added to a single policy. Consider structuring your policy around specific areas of focus, as in this example, the treatment of a Credit Card Number across the entirety of your enterprise.

CLI Code

pim create policies policy --name "ccn-policy" --description "Protect CCN with tokenization"

CLI Actual Output

NAME        DESCRIPTION                    ACCESS                                                      UID
ccn-policy  Protect CCN with tokenization  {'protect': False, 'reProtect': False, 'unProtect': False}  1  

API Endpoint

POST /pim/policies

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/policies" \
-d '{
"name": "ccn-policy",
"description": "Protect CCN with tokenization",
"template": {
"access": {
"protect": false,
"reProtect": false,
"unProtect": false
}
}
}'

API Actual Output

{"name":"ccn-policy","description":"Protect CCN with tokenization","uid":"1","template":{"access":{"protect":false,"reProtect":false,"unProtect":false}}}

2.7 - Define Rule with Data Element and Role

Define that includes a data element and role.

What you are doing

Creating the policy rule that binds:

  • A role: Who.
  • A data element: What.
  • Permitted operations: Protect, Reprotect, or Unprotect.

Why it matters

This binding is what makes the policy enforceable. Without rules, the policy exists but grants no access.

Tips

This rule grants the specified role permission to protect the CCN data element, while disallowing reprotect and unprotect.

CLI Code

pim create policies rules 1 --rule "1,1,1,NULL_VALUE,true,false,true"

CLI Actual Output

## Role  Dataelement  Mask  Noaccessoperation  Access
1     1            1     NULL_VALUE         {'protect': True, 'reProtect': False, 'unProtect': True}  

API Endpoint

POST /pim/policies/{POLICY_UID}/rules

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/policies/1/rules" \
-d '{
"role": "1",
"dataElement": "1",
"mask": "1",
"noAccessOperation": "NULL_VALUE",
"permission": {
"access": {
"protect": true,
"reProtect": false,
"unProtect": true
}
}
}'

API Actual Output

{"role":"1","mask":"1","dataElement":"1","permission":{"access":{"protect":true,"reProtect":false,"unProtect":true}}}

2.8 - Create Datastore

Create a datastore.

What you are doing

Creating the datastore target where a policy will be deployed.

Why it matters

A policy is not active for protectors until it is deployed to a datastore.

CLI Code

pim create datastores datastore --name "ds_protect_ccn" --description "Datastore to demonstrate CCN protection" --default

CLI Actual Output

## Name            Description                              Default  Uid
ds_protect_ccn  Datastore to demonstrate CCN protection  True     1  

API Endpoint

POST /pim/datastores

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/datastores" \
-d '{
"name": "ds_protect_ccn",
"description": "Datastore to demonstrate CCN protection",
"default": true
}'

API Actual Output

{"description":"Datastore to demonstrate CCN protection","name":"ds_protect_ccn","default":true,"uid":"1"}

2.9 - Deploy Policy to a Datastore

Deploy a policy to a datastore.

What you are doing

Deploying the policy to the datastore so protectors that target that datastore can load the policy.

Why it matters

Until the policy is deployed, the policy is not available to runtime protectors.

Tips

  • You may want to deploy the multiple policies to a single datastores. If so, include them by repeating the –-policies parameter.
  • You can also add a single policy to multiple datastores by creating a loop.

CLI Code

pim invoke datastores deploy 1 --policies 1

CLI Actual Output

Successfully deployed to datastore '1':
Policies: 1  

API Endpoint

POST /pim/datastores/{DATASTORE_UID}/deploy

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/datastores/1/deploy" \
-d '{
"policies": ["1"],
"applications": []
}'

API Actual Output

API does not return any output as response

2.10 - Confirm Deployment

Confirm the policy deployment.

What you are doing

Confirming which policies are deployed to which datastores.

Why it matters

Verifying deployment confirms the policy is active, correctly mapped, and enforceable.

CLI Code

pim get deploy

CLI Actual output

## Uid  Policies  Applications
1    ['1']     []  

API Endpoint

POST /pim/deploy

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-X GET "https://${GW_HOST}/pty/v2/pim/deploy"

API Actual Output

{"dataStores":[{"uid":"1","policies":["1"],"applications":[]}]}

3 - Create a policy to protect Date of Birth (DOB)

Workflow example to protect DOB.

Goal

Create one policy that protects Date of Birth (DOB) using Datetime data element, with:

  • At least one role.
  • At least one member source feeding that role.
  • Deployed to at least one datastore.

This example provides a walkthrough of the complete workflow to create a policy to protect a Date of Birth (DOB). DOB is a common piece of sensitive personal data, and organizations typically protect it using datetime tokenization. This tokenization preserves the YYYY‑MM‑DD structure while preventing direct exposure of the original value. In this example, a single role is used whose members are obtained from an LDAP-based Member Source. The role is granted permission to protect (tokenize) DOB values.

For this walkthrough, a dedicated DOB data element is created using a date‑specific tokenizer, ensuring that the output maintains a valid date format for downstream systems. The role and data element are combined into a single policy. The policy is then deployed to a datastore so applications working with DOB information can enforce the protection rules at runtime.

Assumptions

To execute any CLI or API command in this example, the following assumptions have been made:

  • You are operating on a new AI Team Edition setup.
    • Set up the AI Team Edition by installing the Protegrity Provisioned Cluster. For more information about installing the PPC, refer to the section Installing PPC.
  • You are connected to the Policy Manager container.
    • Connect to the Policy Manager container by deploying the Protegrity Policy Manager. For more information about deploying the Protegrity Policy Manager, refer to the section Installing Policy Workbench.

CLI Examples

To execute any CLI command in this example, the following additional assumption has been made:

API Examples

To execute any API command in this example, the following additional assumption has been made:

  • You have access to the Protegrity Policy Management REST APIs.

3.1 - Initialize Policy Management

Initialize the policy management.

This step initializes the Policy Information Management system. This step needs to be executed only once.

CLI Code

pim invoke init

CLI Actual Output

✅ PIM successfully initialized (bootstrapped).  

API Endpoint

POST /pim/init

Get Gateway host address

export GW_HOST="$(kubectl get gateway pty-main -n api-gateway -o jsonpath='{.status.addresses[0].value}')"

Generate JWT token

TOKEN=$(curl -k -s  https://$GW_HOST/api/v1/auth/login/token \
## -X Post \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'loginname=workbench' \
-d 'password=Admin123!' \
-D - -o /dev/null 2>&1 | grep -i 'pty_access_jwt_token:' | sed 's/pty_access_jwt_token: //' | tr -d '\r') && echo "${TOKEN:0:10}"

API Code

curl -k -H "Authorization: Bearer ${TOKEN}" -X POST "https://{$GW_HOST}/pty/v2/pim/init" -H "accept: application/json"

API Actual Output

The API does not return any output as response.

3.2 - Prepare Data Element

Prepare data element.

What you are doing

Creating a DOB data element that defines what is protected (Date of Birth) and how it is protected using date tokenization in YYYY-MM-DD format.

Why it matters

Data elements are the protection building blocks that policies grant access to.

CLI Code

pim create dataelements token date-time --name "de_dob_token" --description "Tokenize Date of Birth" --tokenizer "SLT_8_DATETIME"

CLI Actual Output

UID  NAME          DESCRIPTION             TOKENIZER       TOKENIZETIME  DISTINGUISHABLEDATE  DATEINCLEAR
1    de_dob_token  Tokenize Date of Birth  SLT_8_DATETIME  False         False                TokenElementDateInClear.NONE  

API Endpoint

POST /pim/dataelements

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/dataelements" \
-d '{
"name": "de_dob_token",
"description": "Tokenize Date of Birth",
"dateTimeToken": {
"tokenizer": "SLT_8_DATETIME",
"tokenizeTime": false,
"distinguishableDate": false,
"dateInClear": "NONE"
}
}'

API Actual Output

{"uid":"1","name":"de_dob_token","description":"Tokenize Date of Birth","dateTimeToken":{"tokenizer":"SLT_8_DATETIME","tokenizeTime":false,"distinguishableDate":false,"dateInClear":"NONE"}}

3.3 - Create Member Source

Create member source.

What you are doing

You are creating a Member Source, which is a source that informs Protegrity where user identities are stored. For example, Active Directory, LDAP, Azure AD or a text file. This configuration stores the connection and the lookup context that Protegrity needs to discover users and groups that are then mapped to Roles.

Why it matters

Policies do not grant access to individual users directly. Access is granted through roles, and roles are populated from external identity systems via member sources. Without a source, you cannot reliably attach real enterprise groups or users to roles, synchronize memberships, or enforce policy access the same way your organization manages identity.

CLI Code

pim create sources file --name test-file --user-file exampleusers.txt --group-file examplegroups.txt

CLI Actual output

NAME       DESCRIPTION  TYPE             USERFILE          GROUPFILE          TIMEOUT  UID
test-file               SourceType.FILE  exampleusers.txt  examplegroups.txt  120      1  

API Endpoint

POST /pim/sources

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/sources" \
-d '{
"name": "test-file",
"type": "FILE",
"connection": {
"userFile": "exampleusers.txt",
"groupFile": "examplegroups.txt"
}
}'

API Actual output

{"name":"test-file","description":"","type":"FILE","connection":{"userFile":"exampleusers.txt","groupFile":"examplegroups.txt"},"uid":"1"}

3.3.1 - Test the Member Source

Test whether the member source has been successfully created.

CLI Code

pim invoke sources test 1

CLI Actual Output

+----------------+--------+---------+
|      type      | passed | message |
+----------------+--------+---------+
|   connection   |  True  |         |
| authentication |  True  |         |
|     groups     |  True  |         |
|     users      |  True  |         |
+----------------+--------+---------+  

API Endpoint

POST /pim/sources/{SOURCE_UID}/test

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/sources/1/test"

API Actual Output

{"connection":{"passed":true,"message":""},"authentication":{"passed":true,"message":""},"groups":{"passed":true,"message":""},"users":{"passed":true,"message":""}}

3.4 - Create Role

Create a role.

What you are doing

Creating the role that represents who can perform operations against the DOB data element.

Why it matters

Permissions are granted to roles, and roles map to real users or groups via member sources.

Tips

  • If you keep the --allow-all option in the command, then set ALLOWALL to True.
  • Consider which user needs what level of access and create a role for each set of users.

CLI Code

pim create roles role --name "dob_protect_role" --description "Role having access to protect DOB" --mode "MANUAL"

CLI Actual Output

NAME              DESCRIPTION                        MODE             ALLOWALL  UID
dob_protect_role  Role having access to protect DOB  RoleMode.MANUAL  False     1  

API Endpoint

POST /pim/roles

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles" \
-d '{
"name": "dob_protect_role",
"description": "Role having access to protect DOB",
"mode": "MANUAL",
"allowAll": false
}'

API Actual Output

{"name":"dob_protect_role","description":"Role having access to protect DOB","mode":"MANUAL","uid":"1","allowAll":false}

3.5 - Assign Member Source to Role

Assign member source to a role.

What you are doing

You are attaching a specific user or group from a member source to a role. This creates the who belongs in this role binding. If you run synchronization, it retrieves the current membership from the source into the role.

Why it matters

This is the step that turns a role from a named container into an identity-backed access control object. If you do not assign a source user or group to the role and synchronize it, then no real identities are associated with that role. This means that your policy rules may exist, but nobody in your organization will actually have the access that those rules intend to grant.

CLI Code

pim create roles members 1 --member "exampleuser1,1,USER"

CLI Actual Output

## Name          Source  Type             Uid
exampleuser1  1       MemberType.USER  1  

API Endpoint

POST /pim/roles/{SOURCE_UID}/test

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles/1/members" \
-d '[
{
"name": "exampleuser1",
"source": "1",
"type": "USER"
}
]'

API Actual Output

[{"type":"USER","name":"exampleuser1","syncid":"exampleuser1","uid":"1","source":"1"}]

3.5.1 - Synchronize Member Source

Synchronize the member source.

Connect and synchronize the users with your member source.

CLI Code

pim invoke roles sync 1

CLI Actual Output

Successfully synchronized members for role with UID '1'.  

API Endpoint

POST /pim/roles/{SOURCE_UID}/sync

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/roles/1/sync"

API Actual Output

The API does not return any output as response.

3.6 - Create Policy Shell

Create a policy shell.

What you are doing

Creating the policy that will hold the access rules. For example, Data Element, Role, and Rules.

Why it matters

The policy is the object that ties together the pieces and becomes deployable.

Tips

Multiple roles, multiple data elements, and their corresponding rules can be added to a single policy. Consider structuring your policy around specific areas of focus. For example, how the DOB is used across the entire enterprise.

CLI Code

pim create policies policy --name "dob-policy" --description "Protect DOB with tokenization"

CLI Actual Output

NAME        DESCRIPTION                    ACCESS                                                      UID
dob-policy  Protect DOB with tokenization  {'protect': False, 'reProtect': False, 'unProtect': False}  1  

API Endpoint

POST /pim/policies

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/policies" \
-d '{
"name": "dob-policy",
"description": "Protect DOB with tokenization",
"template": {
"access": {
"protect": false,
"reProtect": false,
"unProtect": false
}
}
}'

API Actual Output

{"name":"dob-policy","description":"Protect DOB with tokenization","uid":"1","template":{"access":{"protect":false,"reProtect":false,"unProtect":false}}}

3.7 - Define Rule with Data Element and Role

Define a rule that includes the data element and role.

What you are doing

Creating the policy rule that binds:

  • A role: Who.
  • A data element: What.
  • Permitted operations: Protect, Reprotect, or Unprotect.

Why it matters

This binding is what makes the policy enforceable. Without rules, the policy exists but grants no access.

Tips

This rule grants the specified role permission to protect and unprotect to the DOB data element, while disallowing reprotect.

CLI Code

# Format:
# "roleUid,dataElementUid,,NOACCESSOPERATION,protect,reProtect,unProtect"

pim create policies rules 1 --rule "1,1,,NULL_VALUE,true,false,true"

CLI Actual Output

## Role  Dataelement  Mask  Noaccessoperation  Access
1     1            0     NULL_VALUE         {'protect': True, 'reProtect': False, 'unProtect': True}  

API Endpoint

POST /pim/policies/{POLICY_UID}/rules

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/policies/1/rules" \
-d '{
"role": "1",
"dataElement": "1",
"mask": "0",
"noAccessOperation": "NULL_VALUE",
"permission": {
"access": {
"protect": true,
"reProtect": false,
"unProtect": true
}
}
}'

API Actual Output

{"role":"1","mask":"0","dataElement":"1","permission":{"access":{"protect":true,"reProtect":false,"unProtect":true}}}

3.8 - Create Datastore

Create a datastore.

What you are doing

Creating the datastore target where a policy will be deployed.

Why it matters

A policy is not active for protectors until it is deployed to a datastore.

CLI Code

pim create datastores datastore --name "ds_protect_dob" --description "Datastore to demonstrate DOB protection" --default

CLI Actual Output

## Name            Description                              Default  Uid
ds_protect_dob  Datastore to demonstrate DOB protection  True     1 

API Endpoint

POST /pim/datastores

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/datastores" \
-d '{
"name": "ds_protect_dob",
"description": "Datastore to demonstrate DOB protection",
"default": true
}'

API Actual Output

{"description":"Datastore to demonstrate DOB protection","name":"ds_protect_dob","default":true,"uid":"1"}

3.9 - Deploy Policy to Datastore

Deploy the policy to a datastore.

What you are doing

Deploying the policy to the datastore so protectors that target that datastore can load the policy.

Why it matters

Until the policy is deployed, the policy is not available to runtime protectors.

Tips

  • You may want to deploy the multiple policies to a single datastores. If so, include them by repeating the –-policies option in the command.
  • You can also add a single policy to multiple datastores by creating a loop.

CLI Code

pim invoke datastores deploy 1 --policies 1

CLI Actual Output

Successfully deployed to datastore '1':
Policies: 1  

API Endpoint

POST /pim/datastores/{DATASTORE_UID}/deploy

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-X POST "https://${GW_HOST}/pty/v2/pim/datastores/1/deploy" \
-d '{
"policies": ["1"],
"applications": []
}'

API Actual Output

The API does not return any output as response.

3.10 - Confirm Deployment

Confirm the policy deployment.

What you are doing

Confirm that the policies have been deployed to the respective datastores.

Why it matters

Verifying deployment confirms the policy is active, correctly mapped, and enforceable.

CLI Code

pim get deploy

CLI Actual Output

## Uid  Policies  Applications
1    ['1']     []  

API Endpoint

POST /pim/deploy

API Code

curl -k \
-H "Authorization: Bearer ${TOKEN}" \
-H "accept: application/json" \
-X GET "https://${GW_HOST}/pty/v2/pim/deploy"

API Actual Output

{"dataStores":[{"uid":"1","policies":["1"],"applications":[]}]}

4 - Full Script Examples

Full scripts to create and deploy a data protection policy.

The following section contains the end-to-end full scripts that can be used to create and protect sample data using the Policy Management REST APIs.

4.1 - Full Script to Protect CCN using Policy Management REST APIs

Full script for protecting CCN data.

The following code snippet contains the contents of the deploy-ccn-policy.sh shell script. This script enables the creation and deployment of a policy to protect CCN data using the Policy Management REST APIs.

#!/usr/bin/env bash
###############################################################################
# Script Name  : ccn_policy.sh
# Description  : End-to-end automation script for creating and deploying a
#                Credit Card Number (CCN) protection policy using the
#                Protegrity Policy Information Management (PIM) REST API.
#
#
# ─────────────────────────────────────────────────────────────────────────────
# IMPORTANT NOTES
# ─────────────────────────────────────────────────────────────────────────────
#
# 1. WORKBENCH REQUIREMENT:
#    The Policy Management REST APIs will work only after you have installed
#    the Protegrity Workbench. Attempting to use these APIs before the
#    Workbench is installed will result in errors.
#
# 2. USER PERMISSIONS:
#    The user account used to authenticate against these APIs must have the
#    appropriate Protegrity role assigned:
#      - Security Officer : Required for write access (create, update, delete)
#      - Security Viewer  : Required for read-only access (get, list)
#    For more information about the roles and permissions required, refer to
#    the section "Managing Roles" in the Protegrity documentation.
#
# 3. API VERSION:
#    The Policy Management API uses version v2.
#    All endpoints in this script are prefixed with /pty/v2/pim/
#    Requests to older API versions will not be supported.
#
# ─────────────────────────────────────────────────────────────────────────────
# PREREQUISITES
# ─────────────────────────────────────────────────────────────────────────────
#   - Protegrity Workbench must be installed and running
#   - kubectl configured and connected to your Kubernetes cluster
#   - curl installed on the machine running this script
#   - Access to the Protegrity API Gateway
#   - A user account with Security Officer permissions
#
# ─────────────────────────────────────────────────────────────────────────────
# USAGE
# ─────────────────────────────────────────────────────────────────────────────
#   chmod +x deploy-ccn-policy.sh
#   ./deploy-ccn-policy.sh
#
# ─────────────────────────────────────────────────────────────────────────────
# WORKFLOW
# ─────────────────────────────────────────────────────────────────────────────
#   Step 1    - Initialize Policy Management
#   Step 2    - Prepare Data Element (CCN Token)
#   Step 2.1  - Create Mask (subsection of Prepare Data Element)
#   Step 3    - Create Member Source
#   Step 3.1  - Test Member Source Connectivity
#   Step 4    - Create Role
#   Step 5    - Assign Member Source to Role
#   Step 5.1  - Sync Role Membership
#   Step 6    - Create Policy Shell
#   Step 7    - Define Policy Rule (bind Role + Data Element + Permissions)
#   Step 8    - Create Datastore
#   Step 9    - Deploy Policy to Datastore
#   Step 10   - Confirm Deployment
#
# ─────────────────────────────────────────────────────────────────────────────
# SECURITY NOTES
# ─────────────────────────────────────────────────────────────────────────────
#   - If any API call returns HTTP 401 (Unauthorized), the script will
#     automatically attempt to re-generate the JWT token and retry the
#     request once before failing.
#   - If any API call indicates that a resource already exists, the script
#     will exit immediately with an error. Delete the conflicting resource
#     first, or update the name variables in SECTION 1 before re-running.
#
# ─────────────────────────────────────────────────────────────────────────────
# EXIT CODES
# ─────────────────────────────────────────────────────────────────────────────
#   0 - Success
#   1 - Script error (set -e will trigger on any failed command)
###############################################################################

set -euo pipefail

###############################################################################
# SECTION 1: USER-CONFIGURABLE VARIABLES
# ─────────────────────────────────────
# Modify the variables below to match your environment before running
# this script. All other values are derived automatically.
#
# NOTE: The user specified by ADMIN_USER must have the Security Officer
#       permission to perform write operations via the Policy Management API.
#       For read-only operations, the Security Viewer permission is sufficient.
#       For more information, refer to the "Managing Roles" section in the
#       Protegrity documentation.
###############################################################################

# --- Protegrity Admin Credentials ---
# WARNING: For production use, consider sourcing these values from a secrets
#          manager (e.g., HashiCorp Vault, Kubernetes Secrets, AWS SSM).
ADMIN_USER="workbench"
ADMIN_PASS="Admin123!"

# --- Data Element ---
DE_NAME="de_ccn_token"
DE_DESC="Tokenize credit card numbers, keeping last 4 chars in clear"
DE_TOKENIZER="SLT_1_6"           # Options: SLT_1_3 | SLT_2_3 | SLT_1_6 | SLT_2_6
DE_FROM_LEFT=0                    # Number of digits to keep in clear from the left
DE_FROM_RIGHT=4                   # Number of digits to keep in clear from the right

# --- Mask (subsection of Prepare Data Element) ---
MASK_NAME="clear_mask"
MASK_FROM_LEFT=0                  # Number of characters to keep in clear from the left
MASK_FROM_RIGHT=4                 # Number of characters to keep in clear from the right
MASK_CHARACTER="*"                # Character used to mask hidden digits

# --- Role ---
ROLE_NAME="role_protect_ccn"
ROLE_DESC="This role has access to protect CCN data"
ROLE_MODE="MANUAL"                # Options: MANUAL | SEMIAUTOMATIC | AUTOMATIC

# --- Member Source ---
SOURCE_NAME="test-file"
SOURCE_USER_FILE="exampleusers.txt"
SOURCE_GROUP_FILE="examplegroups.txt"

# --- Role Member ---
MEMBER_NAME="exampleuser1"
MEMBER_TYPE="USER"                # Options: USER | GROUP

# --- Policy ---
POLICY_NAME="ccn-policy"
POLICY_DESC="Protect CCN with tokenization"

# --- Policy Rule Permissions ---
RULE_PROTECT=true                 # Allow protect operation
RULE_REPROTECT=false              # Allow re-protect operation
RULE_UNPROTECT=true               # Allow unprotect operation
RULE_NO_ACCESS_OP="NULL_VALUE"    # Behavior for no-access: NULL_VALUE | EXCEPTION

# --- Datastore ---
DS_NAME="ds_protect_ccn"
DS_DESC="Datastore to demonstrate CCN protection"
DS_DEFAULT=true                   # Set as the default datastore: true | false

# --- Token Retry Settings ---
# On receiving HTTP 401 Unauthorized, the script will refresh the JWT token
# and retry the failed request. MAX_TOKEN_RETRIES controls how many refresh
# attempts are made before the script aborts.
MAX_TOKEN_RETRIES=1               # Number of times to retry generating a token on 401

###############################################################################
# SECTION 2: HELPER FUNCTIONS
# ────────────────────────────
# Internal utility functions used throughout the script.
# Do not modify unless necessary.
###############################################################################

# Prints a formatted primary section header to stdout
log() {
  printf "\n%s\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  printf "  %s\n" "$*"
  printf "%s\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# Prints a formatted subsection header to stdout (indented, lighter style)
log_sub() {
  printf "\n%s\n" "  ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
  printf "    %s\n" "$*"
  printf "%s\n" "  ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
}

# Prints an error message to stderr and exits with code 1
# Usage: die <message>
die() {
  printf "\n  [ERROR] %s\n" "$*" >&2
  exit 1
}

# Attempts to extract a UID from a JSON API response.
# Handles both string UIDs ("uid":"1") and integer UIDs ("uid":1).
# Exits with an error if extraction fails — never prompts interactively.
# Usage: extract_uid <json_response> <resource_label>
extract_uid() {
  local response="$1"
  local label="$2"
  local uid
  # Match string-quoted UIDs: "uid":"<value>"
  uid=$(echo "$response" | grep -o '"uid":"[^"]*"' | head -1 | sed 's/"uid":"//;s/"//' || true)
  # Fallback: match integer UIDs: "uid":<number>
  if [[ -z "${uid:-}" ]]; then
    uid=$(echo "$response" | grep -o '"uid":[0-9]*' | head -1 | grep -o '[0-9]*' || true)
  fi
  if [[ -z "${uid:-}" ]]; then
    die "Failed to extract UID for '${label}'. API response was: ${response}"
  fi
  echo "$uid"
}

# Generates a new JWT authentication token using the configured admin
# credentials (ADMIN_USER / ADMIN_PASS). Stores the result in the global
# TOKEN variable.
#
# NOTE: The user must have the Security Officer permission for write access
#       or the Security Viewer permission for read-only access to the
#       Policy Management API (v2). For more information, refer to
#       the "Managing Roles" section in the Protegrity documentation.
#
# Usage: generate_token
generate_token() {
  echo "  Generating JWT authentication token..."
  TOKEN=$(curl -k -s "https://${GW_HOST}/api/v1/auth/login/token" \
    -X POST \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d "loginname=${ADMIN_USER}" \
    -d "password=${ADMIN_PASS}" \
    -D - -o /dev/null 2>&1 \
    | grep -i 'pty_access_jwt_token:' \
    | sed 's/pty_access_jwt_token: //' \
    | tr -d '\r')

  if [[ -z "${TOKEN:-}" ]]; then
    die "Failed to retrieve JWT token. Please verify the following:
         - The Protegrity Workbench is installed and running.
         - The API Gateway host (${GW_HOST}) is reachable.
         - The credentials for user '${ADMIN_USER}' are correct.
         - The user '${ADMIN_USER}' has the Security Officer or Security Viewer
           permission assigned. Refer to 'Managing Roles' in the Protegrity
           documentation for more information."
  fi

  echo "  Token acquired successfully."
}

# Executes a curl API call and automatically retries with a refreshed JWT
# token if a 401 Unauthorized response is received.
#
# All Policy Management API calls in this script target the v2 API version:
#   https://<gateway>/pty/v2/pim/...
#
# On HTTP 401, the token is refreshed (up to MAX_TOKEN_RETRIES times) and
# the request is retried. This can occur when a token expires mid-run.
# On any other non-2xx response, a warning is logged but execution continues.
#
# Usage: api_call <curl_args...>
api_call() {
  local retries=0
  local http_status
  local response_body
  local tmp_file
  tmp_file=$(mktemp)

  while true; do
    # Execute the curl call, capturing body and HTTP status separately
    http_status=$(curl -k -s -o "$tmp_file" -w "%{http_code}" \
      -H "Authorization: Bearer ${TOKEN}" \
      "$@")
    response_body=$(cat "$tmp_file")

    # Handle 401 Unauthorized:
    # This typically means the JWT token has expired or is invalid.
    # The script will attempt to refresh the token and retry the request.
    # Ensure the user has the correct permissions (Security Officer / Viewer).
    # Refer to "Managing Roles" in the Protegrity documentation.
    if [[ "$http_status" == "401" ]]; then
      if [[ "$retries" -lt "$MAX_TOKEN_RETRIES" ]]; then
        echo "  [Warning] Received HTTP 401 Unauthorized." >&2
        echo "            Refreshing JWT token and retrying (attempt $((retries + 1)) of ${MAX_TOKEN_RETRIES})..." >&2
        generate_token
        retries=$((retries + 1))
        continue
      else
        rm -f "$tmp_file"
        die "Received HTTP 401 Unauthorized after ${MAX_TOKEN_RETRIES} token refresh attempt(s).
         Please verify that user '${ADMIN_USER}' has the required permissions:
           - Security Officer : for write access
           - Security Viewer  : for read-only access
         Refer to 'Managing Roles' in the Protegrity documentation."
      fi
    fi

    # Fail on "already exists" (HTTP 400/409) — resource must be removed first
    if echo "$response_body" | grep -qi "already exist"; then
      rm -f "$tmp_file"
      die "Resource already exists (HTTP ${http_status}). The script cannot continue.
         Response : ${response_body}
         Action   : Delete or rename the existing resource before re-running,
                    or update the name variables at the top of this script."
    fi

    # Log other non-2xx responses (excluding 401 already handled above)
    if [[ "$http_status" != 2* ]]; then
      echo "  [Warning] Received HTTP ${http_status}. Response: ${response_body}" >&2
    fi

    rm -f "$tmp_file"
    echo "$response_body"
    break
  done
}

###############################################################################
# SECTION 3: ENVIRONMENT SETUP
# ─────────────────────────────
# Retrieves the API Gateway host address and generates a JWT authentication
# token required for all subsequent API calls.
#
# NOTE: The Policy Management REST APIs will work only after the Protegrity
#       Workbench has been installed. All API calls target version v2:
#         https://<gateway>/pty/v2/pim/
###############################################################################

log "Environment Setup: Retrieving API Gateway Host"
export GW_HOST
GW_HOST="$(kubectl get gateway pty-main -n api-gateway \
  -o jsonpath='{.status.addresses[0].value}')"
echo "  API Gateway Host : ${GW_HOST}"
echo "  API Version      : v2  (/pty/v2/pim/)"

log "Environment Setup: Generating JWT Authentication Token"
generate_token

###############################################################################
# SECTION 4: WORKFLOW EXECUTION
# ──────────────────────────────
# Executes each step of the CCN policy creation workflow in sequence.
# UIDs returned by each step are captured and reused in subsequent steps.
#
# NOTE: All write operations (POST) require the Security Officer permission.
#       The read operation in Step 12 (GET) requires at minimum the
#       Security Viewer permission.
###############################################################################

# ─────────────────────────────────────────────────────────────────────────────
# STEP 1: Initialize Policy Management
# ─────────────────────────────────────────────────────────────────────────────
# Initializes the PIM system. This step only needs to be performed once
# per environment setup.
#
# Requirement : Protegrity Workbench must be installed before running this step.
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/init
# ─────────────────────────────────────────────────────────────────────────────
log "Step 1: Initialize Policy Management"
INIT_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/init")
if [[ -z "${INIT_RESPONSE}" ]]; then
  echo "  Status: OK (empty response — PIM already initialized or no content returned)"
else
  echo "  Response: ${INIT_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 2: Prepare Data Element
# ─────────────────────────────────────────────────────────────────────────────
# Prepares the CCN Data Element that defines what data is protected and how
# it is tokenized. The tokenizer (SLT_1_6) and clear-text settings determine
# how many digits remain visible after tokenization.
#
# This step also includes the creation of a Mask (Step 2.1) as a subsection,
# since the mask is directly associated with how the data element's unprotected
# value is presented to consumers.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/dataelements
# ─────────────────────────────────────────────────────────────────────────────
log "Step 2: Prepare Data Element — ${DE_NAME}"
DE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/dataelements" \
  -d '{
    "name": "'"${DE_NAME}"'",
    "description": "'"${DE_DESC}"'",
    "creditCardToken": {
      "tokenizer": "'"${DE_TOKENIZER}"'",
      "fromLeft": '"${DE_FROM_LEFT}"',
      "fromRight": '"${DE_FROM_RIGHT}"',
      "valueIdentification": {
        "invalidCardType": false,
        "invalidLuhnDigit": true,
        "alphabeticIndicator": false,
        "alphabeticIndicatorPosition": 1
      }
    }
  }')
echo "  Response: ${DE_RESPONSE}"
DE_UID=$(extract_uid "$DE_RESPONSE" "$DE_NAME")
echo "  Data Element UID: ${DE_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 2.1: Create Mask
# ─────────────────────────────────────────────────────────────────────────────
# Subsection of: Prepare Data Element
#
# Creates a mask that controls how data is displayed when unprotected.
# The mask is optionally applied to an unprotect operation to display only
# certain characters to the consumer of the data. Hidden characters are
# replaced with the specified mask character, while the defined number of
# characters on each side remain visible in the clear.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/masks
# ─────────────────────────────────────────────────────────────────────────────
log_sub "Step 2.1: Create Mask — ${MASK_NAME}  (Subsection of: Prepare Data Element)"
MASK_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/masks" \
  -d '{
    "name": "'"${MASK_NAME}"'",
    "masked": true,
    "fromLeft": '"${MASK_FROM_LEFT}"',
    "fromRight": '"${MASK_FROM_RIGHT}"',
    "character": "'"${MASK_CHARACTER}"'"
  }')
echo "    Response: ${MASK_RESPONSE}"
MASK_UID=$(extract_uid "$MASK_RESPONSE" "$MASK_NAME")
echo "    Mask UID: ${MASK_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 3: Create Member Source
# ─────────────────────────────────────────────────────────────────────────────
# Creates a Member Source that defines where user and group identities are
# sourced from (in this example, a flat file). Member Sources are used to
# populate roles with real enterprise identities.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/sources
# ─────────────────────────────────────────────────────────────────────────────
log "Step 4: Create Member Source — ${SOURCE_NAME}"
SOURCE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/sources" \
  -d '{
    "name": "'"${SOURCE_NAME}"'",
    "type": "FILE",
    "connection": {
      "userFile": "'"${SOURCE_USER_FILE}"'",
      "groupFile": "'"${SOURCE_GROUP_FILE}"'"
    }
  }')
echo "  Response: ${SOURCE_RESPONSE}"
SOURCE_UID=$(extract_uid "$SOURCE_RESPONSE" "$SOURCE_NAME")
echo "  Source UID: ${SOURCE_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 3.1: Test Member Source Connectivity
# ─────────────────────────────────────────────────────────────────────────────
# Validates that the Member Source is reachable and correctly configured.
# All connectivity checks (connection, authentication, groups, users) must
# pass before proceeding.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/sources/{id}/test
# ─────────────────────────────────────────────────────────────────────────────
log "Step 3.1: Test Member Source Connectivity — UID: ${SOURCE_UID}"
api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/sources/${SOURCE_UID}/test"
echo ""
# ─────────────────────────────────────────────────────────────────────────────
# STEP 4: Create Role
# ─────────────────────────────────────────────────────────────────────────────
# Creates a role that represents who is allowed to perform operations on
# the protected data. Permissions are granted to roles, which are then
# mapped to users and groups via Member Sources.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles
# ─────────────────────────────────────────────────────────────────────────────
log "Step 4: Create Role — ${ROLE_NAME}"
ROLE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles" \
  -d '{
    "name": "'"${ROLE_NAME}"'",
    "description": "'"${ROLE_DESC}"'",
    "mode": "'"${ROLE_MODE}"'",
    "allowAll": false
  }')
echo "  Response: ${ROLE_RESPONSE}"
ROLE_UID=$(extract_uid "$ROLE_RESPONSE" "$ROLE_NAME")
echo "  Role UID: ${ROLE_UID}"


# ─────────────────────────────────────────────────────────────────────────────
# STEP 5: Assign Member Source to Role
# ─────────────────────────────────────────────────────────────────────────────
# Binds a specific user or group from the Member Source to the Role.
# This establishes the identity-to-role mapping that makes the policy
# enforceable for real users.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles/{id}/members
# ─────────────────────────────────────────────────────────────────────────────
log "Step 5: Assign Member '${MEMBER_NAME}' to Role — UID: ${ROLE_UID}"
api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles/${ROLE_UID}/members" \
  -d '[
    {
      "name": "'"${MEMBER_NAME}"'",
      "source": "'"${SOURCE_UID}"'",
      "type": "'"${MEMBER_TYPE}"'"
    }
  ]'
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 5.1: Sync Role Membership
# ─────────────────────────────────────────────────────────────────────────────
# Synchronizes the role membership from the Member Source. This pulls the
# current list of users and groups into the role so that access controls
# reflect the latest state of the identity source.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles/{id}/sync
# ─────────────────────────────────────────────────────────────────────────────
log "Step 5.1: Sync Role Membership — Role UID: ${ROLE_UID}"
SYNC_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles/${ROLE_UID}/sync")
if [[ -z "${SYNC_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${SYNC_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 6: Create Policy Shell
# ─────────────────────────────────────────────────────────────────────────────
# Creates the policy container that will hold the access rules. The policy
# is the deployable object that ties together Data Elements, Roles, and Rules.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/policies
# ─────────────────────────────────────────────────────────────────────────────
log "Step 6: Create Policy Shell — ${POLICY_NAME}"
POLICY_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/policies" \
  -d '{
    "name": "'"${POLICY_NAME}"'",
    "description": "'"${POLICY_DESC}"'",
    "template": {
      "access": {
        "protect": false,
        "reProtect": false,
        "unProtect": false
      }
    }
  }')
echo "  Response: ${POLICY_RESPONSE}"
POLICY_UID=$(extract_uid "$POLICY_RESPONSE" "$POLICY_NAME")
echo "  Policy UID: ${POLICY_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 7: Define Policy Rule
# ─────────────────────────────────────────────────────────────────────────────
# Creates the rule that binds a Role (who), a Data Element (what), a Mask
# (how unprotected data is displayed), and the permitted operations
# (protect / reProtect / unProtect) into the policy.
# Without rules, the policy exists but grants no access.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/policies/{id}/rules
# ─────────────────────────────────────────────────────────────────────────────
log "Step 7: Define Policy Rule — Policy UID: ${POLICY_UID}"
RULE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/policies/${POLICY_UID}/rules" \
  -d '{
    "role": "'"${ROLE_UID}"'",
    "dataElement": "'"${DE_UID}"'",
    "mask": "'"${MASK_UID}"'",
    "noAccessOperation": "'"${RULE_NO_ACCESS_OP}"'",
    "permission": {
      "access": {
        "protect": '"${RULE_PROTECT}"',
        "reProtect": '"${RULE_REPROTECT}"',
        "unProtect": '"${RULE_UNPROTECT}"'
      }
    }
  }')
if [[ -z "${RULE_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${RULE_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 8: Create Datastore
# ─────────────────────────────────────────────────────────────────────────────
# Creates the datastore target to which the policy will be deployed.
# A policy is not active for protectors until it has been deployed to
# at least one datastore.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/datastores
# ─────────────────────────────────────────────────────────────────────────────
log "Step 8: Create Datastore — ${DS_NAME}"
DS_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/datastores" \
  -d '{
    "name": "'"${DS_NAME}"'",
    "description": "'"${DS_DESC}"'",
    "default": '"${DS_DEFAULT}"'
  }')
echo "  Response: ${DS_RESPONSE}"
DS_UID=$(extract_uid "$DS_RESPONSE" "$DS_NAME")
echo "  Datastore UID: ${DS_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 9: Deploy Policy to Datastore
# ─────────────────────────────────────────────────────────────────────────────
# Deploys the policy to the target datastore. After this step, runtime
# protectors that reference this datastore will be able to load and
# enforce the policy.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/datastores/{id}/deploy
# ─────────────────────────────────────────────────────────────────────────────
log "Step 9: Deploy Policy to Datastore — DS UID: ${DS_UID}, Policy UID: ${POLICY_UID}"
DEPLOY_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/datastores/${DS_UID}/deploy" \
  -d '{
    "policies": ["'"${POLICY_UID}"'"],
    "applications": []
  }')
if [[ -z "${DEPLOY_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${DEPLOY_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 10: Confirm Deployment
# ─────────────────────────────────────────────────────────────────────────────
# Verifies that the policy has been successfully deployed to the datastore.
# Confirms the policy is active, correctly mapped, and enforceable.
#
# Permission  : Security Viewer (read-only) or Security Officer
# API Version : v2  — GET /pty/v2/pim/deploy
# ─────────────────────────────────────────────────────────────────────────────
log "Step 10: Confirm Deployment"
api_call \
  -H "accept: application/json" \
  -X GET "https://${GW_HOST}/pty/v2/pim/deploy"
echo ""

###############################################################################
# SECTION 5: SUMMARY
# ───────────────────
# Displays a summary of all created resources and their UIDs.
###############################################################################
log "Workflow Complete ✅"
printf "\n%-20s %-30s %-10s\n" "Resource"             "Name"                          "UID"
printf "%-20s %-30s %-10s\n"   "────────────────────" "──────────────────────────────" "──────────"
printf "%-20s %-30s %-10s\n"   "Data Element"  "${DE_NAME}"      "${DE_UID}"
printf "%-20s %-30s %-10s\n"   "  └─ Mask"     "${MASK_NAME}"    "${MASK_UID}"
printf "%-20s %-30s %-10s\n"   "Role"          "${ROLE_NAME}"    "${ROLE_UID}"
printf "%-20s %-30s %-10s\n"   "Member Source" "${SOURCE_NAME}"  "${SOURCE_UID}"
printf "%-20s %-30s %-10s\n"   "Policy"        "${POLICY_NAME}"  "${POLICY_UID}"
printf "%-20s %-30s %-10s\n"   "Datastore"     "${DS_NAME}"      "${DS_UID}"
printf "\n"

4.2 - Full Script to Protect DOB using Policy Management REST APIs

Full script for protecting DOB data.

The following code snippet contains the contents of the deploy-dob-policy.sh shell script. This script enables the creation and deployment of a policy to protect DOB data using the Policy Management REST APIs.

#!/usr/bin/env bash
###############################################################################
# Script Name  : dob_policy.sh
# Description  : End-to-end automation script for creating and deploying a
#                Date of Birth (DOB) protection policy using the
#                Protegrity Policy Information Management (PIM) REST API.
#
#
# ─────────────────────────────────────────────────────────────────────────────
# IMPORTANT NOTES
# ─────────────────────────────────────────────────────────────────────────────
#
# 1. WORKBENCH REQUIREMENT:
#    The Policy Management REST APIs will work only after you have installed
#    the Protegrity Workbench. Attempting to use these APIs before the
#    Workbench is installed will result in errors.
#
# 2. USER PERMISSIONS:
#    The user account used to authenticate against these APIs must have the
#    appropriate Protegrity role assigned:
#      - Security Officer : Required for write access (create, update, delete)
#      - Security Viewer  : Required for read-only access (get, list)
#    For more information about the roles and permissions required, refer to
#    the section "Managing Roles" in the Protegrity documentation.
#
# 3. API VERSION:
#    The Policy Management API uses version v2.
#    All endpoints in this script are prefixed with /pty/v2/pim/
#    Requests to older API versions will not be supported.
#
# ─────────────────────────────────────────────────────────────────────────────
# PREREQUISITES
# ─────────────────────────────────────────────────────────────────────────────
#   - Protegrity Workbench must be installed and running
#   - kubectl configured and connected to your Kubernetes cluster
#   - curl installed on the machine running this script
#   - Access to the Protegrity API Gateway
#   - A user account with Security Officer permissions
#
# ─────────────────────────────────────────────────────────────────────────────
# USAGE
# ─────────────────────────────────────────────────────────────────────────────
#   chmod +x deploy-dob-policy.sh
#   ./deploy-dob-policy.sh
#
# ─────────────────────────────────────────────────────────────────────────────
# WORKFLOW
# ─────────────────────────────────────────────────────────────────────────────
#   Step 1    - Initialize Policy Management
#   Step 2    - Prepare Data Element (DOB DateTime Token)
#   Step 3    - Create Member Source
#   Step 3.1  - Test Member Source Connectivity
#   Step 4    - Create Role
#   Step 5    - Assign Member Source to Role
#   Step 5.1  - Sync Role Membership
#   Step 6    - Create Policy Shell
#   Step 7    - Define Policy Rule (bind Role + Data Element + Permissions)
#   Step 8    - Create Datastore
#   Step 9    - Deploy Policy to Datastore
#   Step 10   - Confirm Deployment
#
# ─────────────────────────────────────────────────────────────────────────────
# SECURITY NOTES
# ─────────────────────────────────────────────────────────────────────────────
#   - If any API call returns HTTP 401 (Unauthorized), the script will
#     automatically attempt to re-generate the JWT token and retry the
#     request once before failing.
#   - If any API call indicates that a resource already exists, the script
#     will exit immediately with an error. Delete the conflicting resource
#     first, or update the name variables in SECTION 1 before re-running.
#
# ─────────────────────────────────────────────────────────────────────────────
# EXIT CODES
# ─────────────────────────────────────────────────────────────────────────────
#   0 - Success
#   1 - Script error (set -e will trigger on any failed command)
###############################################################################

set -euo pipefail

###############################################################################
# SECTION 1: USER-CONFIGURABLE VARIABLES
# ─────────────────────────────────────
# Modify the variables below to match your environment before running
# this script. All other values are derived automatically.
#
# NOTE: The user specified by ADMIN_USER must have the Security Officer
#       permission to perform write operations via the Policy Management API.
#       For read-only operations, the Security Viewer permission is sufficient.
#       For more information, refer to the "Managing Roles" section in the
#       Protegrity documentation.
###############################################################################

# --- Protegrity Admin Credentials ---
# WARNING: For production use, consider sourcing these values from a secrets
#          manager (e.g., HashiCorp Vault, Kubernetes Secrets, AWS SSM).
ADMIN_USER="workbench"
ADMIN_PASS="Admin123!"

# --- Data Element ---
DE_NAME="de_dob_token"
DE_DESC="Tokenize Date of Birth"
DE_TOKENIZER="SLT_8_DATETIME"     # DateTime tokenizer for date/time fields

# --- Role ---
ROLE_NAME="dob_protect_role"
ROLE_DESC="Role having access to protect DOB"
ROLE_MODE="MANUAL"                # Options: MANUAL | SEMIAUTOMATIC | AUTOMATIC

# --- Member Source ---
SOURCE_NAME="test-file"
SOURCE_USER_FILE="exampleusers.txt"
SOURCE_GROUP_FILE="examplegroups.txt"

# --- Role Member ---
MEMBER_NAME="exampleuser1"
MEMBER_TYPE="USER"                # Options: USER | GROUP

# --- Policy ---
POLICY_NAME="dob-policy"
POLICY_DESC="Protect Date of Birth with tokenization"

# --- Policy Rule Permissions ---
RULE_PROTECT=true                 # Allow protect operation
RULE_REPROTECT=false              # Allow re-protect operation
RULE_UNPROTECT=true               # Allow unprotect operation
RULE_NO_ACCESS_OP="NULL_VALUE"    # Behavior for no-access: NULL_VALUE | EXCEPTION

# --- Datastore ---
DS_NAME="ds_protect_dob"
DS_DESC="Datastore to demonstrate DOB protection"
DS_DEFAULT=true                   # Set as the default datastore: true | false

# --- Token Retry Settings ---
# On receiving HTTP 401 Unauthorized, the script will refresh the JWT token
# and retry the failed request. MAX_TOKEN_RETRIES controls how many refresh
# attempts are made before the script aborts.
MAX_TOKEN_RETRIES=1               # Number of times to retry generating a token on 401

###############################################################################
# SECTION 2: HELPER FUNCTIONS
# ────────────────────────────
# Internal utility functions used throughout the script.
# Do not modify unless necessary.
###############################################################################

# Prints a formatted primary section header to stdout
log() {
  printf "\n%s\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
  printf "  %s\n" "$*"
  printf "%s\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# Prints a formatted subsection header to stdout (indented, lighter style)
log_sub() {
  printf "\n%s\n" "  ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
  printf "    %s\n" "$*"
  printf "%s\n" "  ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄"
}

# Prints an error message to stderr and exits with code 1
# Usage: die <message>
die() {
  printf "\n  [ERROR] %s\n" "$*" >&2
  exit 1
}

# Attempts to extract a UID from a JSON API response.
# Handles both string UIDs ("uid":"1") and integer UIDs ("uid":1).
# Exits with an error if extraction fails — never prompts interactively.
# Usage: extract_uid <json_response> <resource_label>
extract_uid() {
  local response="$1"
  local label="$2"
  local uid
  # Match string-quoted UIDs: "uid":"<value>"
  uid=$(echo "$response" | grep -o '"uid":"[^"]*"' | head -1 | sed 's/"uid":"//;s/"//' || true)
  # Fallback: match integer UIDs: "uid":<number>
  if [[ -z "${uid:-}" ]]; then
    uid=$(echo "$response" | grep -o '"uid":[0-9]*' | head -1 | grep -o '[0-9]*' || true)
  fi
  if [[ -z "${uid:-}" ]]; then
    die "Failed to extract UID for '${label}'. API response was: ${response}"
  fi
  echo "$uid"
}

# Generates a new JWT authentication token using the configured admin
# credentials (ADMIN_USER / ADMIN_PASS). Stores the result in the global
# TOKEN variable.
#
# NOTE: The user must have the Security Officer permission for write access
#       or the Security Viewer permission for read-only access to the
#       Policy Management API (v2). For more information, refer to
#       the "Managing Roles" section in the Protegrity documentation.
#
# Usage: generate_token
generate_token() {
  echo "  Generating JWT authentication token..."
  TOKEN=$(curl -k -s "https://${GW_HOST}/api/v1/auth/login/token" \
    -X POST \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d "loginname=${ADMIN_USER}" \
    -d "password=${ADMIN_PASS}" \
    -D - -o /dev/null 2>&1 \
    | grep -i 'pty_access_jwt_token:' \
    | sed 's/pty_access_jwt_token: //' \
    | tr -d '\r')

  if [[ -z "${TOKEN:-}" ]]; then
    die "Failed to retrieve JWT token. Please verify the following:
         - The Protegrity Workbench is installed and running.
         - The API Gateway host (${GW_HOST}) is reachable.
         - The credentials for user '${ADMIN_USER}' are correct.
         - The user '${ADMIN_USER}' has the Security Officer or Security Viewer
           permission assigned. Refer to 'Managing Roles' in the Protegrity
           documentation for more information."
  fi

  echo "  Token acquired successfully."
}

# Executes a curl API call and automatically retries with a refreshed JWT
# token if a 401 Unauthorized response is received.
#
# All Policy Management API calls in this script target the v2 API version:
#   https://<gateway>/pty/v2/pim/...
#
# On HTTP 401, the token is refreshed (up to MAX_TOKEN_RETRIES times) and
# the request is retried. This can occur when a token expires mid-run.
# On any other non-2xx response, a warning is logged but execution continues.
#
# Usage: api_call <curl_args...>
api_call() {
  local retries=0
  local http_status
  local response_body
  local tmp_file
  tmp_file=$(mktemp)

  while true; do
    # Execute the curl call, capturing body and HTTP status separately
    http_status=$(curl -k -s -o "$tmp_file" -w "%{http_code}" \
      -H "Authorization: Bearer ${TOKEN}" \
      "$@")
    response_body=$(cat "$tmp_file")

    # Handle 401 Unauthorized:
    # This typically means the JWT token has expired or is invalid.
    # The script will attempt to refresh the token and retry the request.
    # Ensure the user has the correct permissions (Security Officer / Viewer).
    # Refer to "Managing Roles" in the Protegrity documentation.
    if [[ "$http_status" == "401" ]]; then
      if [[ "$retries" -lt "$MAX_TOKEN_RETRIES" ]]; then
        echo "  [Warning] Received HTTP 401 Unauthorized." >&2
        echo "            Refreshing JWT token and retrying (attempt $((retries + 1)) of ${MAX_TOKEN_RETRIES})..." >&2
        generate_token
        retries=$((retries + 1))
        continue
      else
        rm -f "$tmp_file"
        die "Received HTTP 401 Unauthorized after ${MAX_TOKEN_RETRIES} token refresh attempt(s).
         Please verify that user '${ADMIN_USER}' has the required permissions:
           - Security Officer : for write access
           - Security Viewer  : for read-only access
         Refer to 'Managing Roles' in the Protegrity documentation."
      fi
    fi

    # Fail on "already exists" (HTTP 400/409) — resource must be removed first
    if echo "$response_body" | grep -qi "already exist"; then
      rm -f "$tmp_file"
      die "Resource already exists (HTTP ${http_status}). The script cannot continue.
         Response : ${response_body}
         Action   : Delete or rename the existing resource before re-running,
                    or update the name variables at the top of this script."
    fi

    # Log other non-2xx responses (excluding 401 already handled above)
    if [[ "$http_status" != 2* ]]; then
      echo "  [Warning] Received HTTP ${http_status}. Response: ${response_body}" >&2
    fi

    rm -f "$tmp_file"
    echo "$response_body"
    break
  done
}

###############################################################################
# SECTION 3: ENVIRONMENT SETUP
# ─────────────────────────────
# Retrieves the API Gateway host address and generates a JWT authentication
# token required for all subsequent API calls.
#
# NOTE: The Policy Management REST APIs will work only after the Protegrity
#       Workbench has been installed. All API calls target version v2:
#         https://<gateway>/pty/v2/pim/
###############################################################################

log "Environment Setup: Retrieving API Gateway Host"
export GW_HOST
GW_HOST="$(kubectl get gateway pty-main -n api-gateway \
  -o jsonpath='{.status.addresses[0].value}')"
echo "  API Gateway Host : ${GW_HOST}"
echo "  API Version      : v2  (/pty/v2/pim/)"

log "Environment Setup: Generating JWT Authentication Token"
generate_token

###############################################################################
# SECTION 4: WORKFLOW EXECUTION
# ──────────────────────────────
# Executes each step of the DOB policy creation workflow in sequence.
# UIDs returned by each step are captured and reused in subsequent steps.
#
# NOTE: All write operations (POST) require the Security Officer permission.
#       The read operation in Step 12 (GET) requires at minimum the
#       Security Viewer permission.
###############################################################################

# ─────────────────────────────────────────────────────────────────────────────
# STEP 1: Initialize Policy Management
# ─────────────────────────────────────────────────────────────────────────────
# Initializes the PIM system. This step only needs to be performed once
# per environment setup.
#
# Requirement : Protegrity Workbench must be installed before running this step.
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/init
# ─────────────────────────────────────────────────────────────────────────────
log "Step 1: Initialize Policy Management"
INIT_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/init")
if [[ -z "${INIT_RESPONSE}" ]]; then
  echo "  Status: OK (empty response — PIM already initialized or no content returned)"
else
  echo "  Response: ${INIT_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 2: Prepare Data Element
# ─────────────────────────────────────────────────────────────────────────────
# Prepares the DOB DateTime Data Element that defines what data is protected
# and how it is tokenized. The SLT_8_DATETIME tokenizer handles date/time
# field tokenization.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/dataelements
# ─────────────────────────────────────────────────────────────────────────────
log "Step 2: Prepare Data Element — ${DE_NAME}"
DE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/dataelements" \
  -d '{
    "name": "'"${DE_NAME}"'",
    "description": "'"${DE_DESC}"'",
    "dateTimeToken": {
      "tokenizer": "'"${DE_TOKENIZER}"'"
    }
  }')
echo "  Response: ${DE_RESPONSE}"
DE_UID=$(extract_uid "$DE_RESPONSE" "$DE_NAME")
echo "  Data Element UID: ${DE_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 3: Create Member Source
# ─────────────────────────────────────────────────────────────────────────────
# Creates a Member Source that defines where user and group identities are
# sourced from (in this example, a flat file). Member Sources are used to
# populate roles with real enterprise identities.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/sources
# ─────────────────────────────────────────────────────────────────────────────
log "Step 3: Create Member Source — ${SOURCE_NAME}"
SOURCE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/sources" \
  -d '{
    "name": "'"${SOURCE_NAME}"'",
    "type": "FILE",
    "connection": {
      "userFile": "'"${SOURCE_USER_FILE}"'",
      "groupFile": "'"${SOURCE_GROUP_FILE}"'"
    }
  }')
echo "  Response: ${SOURCE_RESPONSE}"
SOURCE_UID=$(extract_uid "$SOURCE_RESPONSE" "$SOURCE_NAME")
echo "  Source UID: ${SOURCE_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 3.1: Test Member Source Connectivity
# ─────────────────────────────────────────────────────────────────────────────
# Validates that the Member Source is reachable and correctly configured.
# All connectivity checks (connection, authentication, groups, users) must
# pass before proceeding.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/sources/{id}/test
# ─────────────────────────────────────────────────────────────────────────────
log "Step 3.1: Test Member Source Connectivity — UID: ${SOURCE_UID}"
api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/sources/${SOURCE_UID}/test"
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 4: Create Role
# ─────────────────────────────────────────────────────────────────────────────
# Creates a role that represents who is allowed to perform operations on
# the protected data. Permissions are granted to roles, which are then
# mapped to users and groups via Member Sources.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles
# ─────────────────────────────────────────────────────────────────────────────
log "Step 4: Create Role — ${ROLE_NAME}"
ROLE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles" \
  -d '{
    "name": "'"${ROLE_NAME}"'",
    "description": "'"${ROLE_DESC}"'",
    "mode": "'"${ROLE_MODE}"'",
    "allowAll": false
  }')
echo "  Response: ${ROLE_RESPONSE}"
ROLE_UID=$(extract_uid "$ROLE_RESPONSE" "$ROLE_NAME")
echo "  Role UID: ${ROLE_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 5: Assign Member Source to Role
# ─────────────────────────────────────────────────────────────────────────────
# Binds a specific user or group from the Member Source to the Role.
# This establishes the identity-to-role mapping that makes the policy
# enforceable for real users.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles/{id}/members
# ─────────────────────────────────────────────────────────────────────────────
log "Step 5: Assign Member '${MEMBER_NAME}' to Role — UID: ${ROLE_UID}"
api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles/${ROLE_UID}/members" \
  -d '[
    {
      "name": "'"${MEMBER_NAME}"'",
      "source": "'"${SOURCE_UID}"'",
      "type": "'"${MEMBER_TYPE}"'"
    }
  ]'
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 5.1: Sync Role Membership
# ─────────────────────────────────────────────────────────────────────────────
# Synchronizes the role membership from the Member Source. This pulls the
# current list of users and groups into the role so that access controls
# reflect the latest state of the identity source.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/roles/{id}/sync
# ─────────────────────────────────────────────────────────────────────────────
log "Step 5.1: Sync Role Membership — Role UID: ${ROLE_UID}"
SYNC_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/roles/${ROLE_UID}/sync")
if [[ -z "${SYNC_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${SYNC_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 6: Create Policy Shell
# ─────────────────────────────────────────────────────────────────────────────
# Creates the policy container that will hold the access rules. The policy
# is the deployable object that ties together Data Elements, Roles, and Rules.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/policies
# ─────────────────────────────────────────────────────────────────────────────
log "Step 6: Create Policy Shell — ${POLICY_NAME}"
POLICY_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/policies" \
  -d '{
    "name": "'"${POLICY_NAME}"'",
    "description": "'"${POLICY_DESC}"'",
    "template": {
      "access": {
        "protect": false,
        "reProtect": false,
        "unProtect": false
      }
    }
  }')
echo "  Response: ${POLICY_RESPONSE}"
POLICY_UID=$(extract_uid "$POLICY_RESPONSE" "$POLICY_NAME")
echo "  Policy UID: ${POLICY_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 7: Define Policy Rule
# ─────────────────────────────────────────────────────────────────────────────
# Creates the rule that binds a Role (who), a Data Element (what), and
# the permitted operations (protect / reProtect / unProtect) into the
# policy. Without rules, the policy exists but grants no access.
# Note: No mask is applied for DateTime data elements.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/policies/{id}/rules
# ─────────────────────────────────────────────────────────────────────────────
log "Step 7: Define Policy Rule — Policy UID: ${POLICY_UID}"
RULE_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/policies/${POLICY_UID}/rules" \
  -d '{
    "role": "'"${ROLE_UID}"'",
    "dataElement": "'"${DE_UID}"'",
    "noAccessOperation": "'"${RULE_NO_ACCESS_OP}"'",
    "permission": {
      "access": {
        "protect": '"${RULE_PROTECT}"',
        "reProtect": '"${RULE_REPROTECT}"',
        "unProtect": '"${RULE_UNPROTECT}"'
      }
    }
  }')
if [[ -z "${RULE_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${RULE_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 8: Create Datastore
# ─────────────────────────────────────────────────────────────────────────────
# Creates the datastore target to which the policy will be deployed.
# A policy is not active for protectors until it has been deployed to
# at least one datastore.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/datastores
# ─────────────────────────────────────────────────────────────────────────────
log "Step 8: Create Datastore — ${DS_NAME}"
DS_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/datastores" \
  -d '{
    "name": "'"${DS_NAME}"'",
    "description": "'"${DS_DESC}"'",
    "default": '"${DS_DEFAULT}"'
  }')
echo "  Response: ${DS_RESPONSE}"
DS_UID=$(extract_uid "$DS_RESPONSE" "$DS_NAME")
echo "  Datastore UID: ${DS_UID}"

# ─────────────────────────────────────────────────────────────────────────────
# STEP 9: Deploy Policy to Datastore
# ─────────────────────────────────────────────────────────────────────────────
# Deploys the policy to the target datastore. After this step, runtime
# protectors that reference this datastore will be able to load and
# enforce the policy.
#
# Permission  : Security Officer
# API Version : v2  — POST /pty/v2/pim/datastores/{id}/deploy
# ─────────────────────────────────────────────────────────────────────────────
log "Step 9: Deploy Policy to Datastore — DS UID: ${DS_UID}, Policy UID: ${POLICY_UID}"
DEPLOY_RESPONSE=$(api_call \
  -H "accept: application/json" \
  -H "Content-Type: application/json" \
  -X POST "https://${GW_HOST}/pty/v2/pim/datastores/${DS_UID}/deploy" \
  -d '{
    "policies": ["'"${POLICY_UID}"'"],
    "applications": []
  }')
if [[ -z "${DEPLOY_RESPONSE}" ]]; then
  echo "  Status: OK"
else
  echo "  Response: ${DEPLOY_RESPONSE}"
fi
echo ""

# ─────────────────────────────────────────────────────────────────────────────
# STEP 10: Confirm Deployment
# ─────────────────────────────────────────────────────────────────────────────
# Verifies that the policy has been successfully deployed to the datastore.
# Confirms the policy is active, correctly mapped, and enforceable.
#
# Permission  : Security Viewer (read-only) or Security Officer
# API Version : v2  — GET /pty/v2/pim/deploy
# ─────────────────────────────────────────────────────────────────────────────
log "Step 10: Confirm Deployment"
api_call \
  -H "accept: application/json" \
  -X GET "https://${GW_HOST}/pty/v2/pim/deploy"
echo ""

###############################################################################
# SECTION 5: SUMMARY
# ───────────────────
# Displays a summary of all created resources and their UIDs.
###############################################################################
log "Workflow Complete ✅"
printf "\n%-20s %-30s %-10s\n" "Resource"             "Name"                          "UID"
printf "%-20s %-30s %-10s\n"   "────────────────────" "──────────────────────────────" "──────────"
printf "%-20s %-30s %-10s\n"   "Data Element"  "${DE_NAME}"      "${DE_UID}"
printf "%-20s %-30s %-10s\n"   "Role"          "${ROLE_NAME}"    "${ROLE_UID}"
printf "%-20s %-30s %-10s\n"   "Member Source" "${SOURCE_NAME}"  "${SOURCE_UID}"
printf "%-20s %-30s %-10s\n"   "Policy"        "${POLICY_NAME}"  "${POLICY_UID}"
printf "%-20s %-30s %-10s\n"   "Datastore"     "${DS_NAME}"      "${DS_UID}"
printf "\n"