This section provides workflows on how to protect the following sample data:
- Credit Card Number (CCN)
- Date of Birth (DOB)
This is the multi-page printable view of this section. Click here to print.
This section provides workflows on how to protect the following sample data:
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:
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.
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.
To execute any CLI or API command in this example, the following assumptions have been made:
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:
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.
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.
Initialize the Policy Management environment so it can store keys, policies, and configuration data required for all subsequent steps.
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.
To set up the foundational Policy Management environment so that all future API commands operate against a valid and initialized repository.
None.
Initialization is the first action performed before any policy‑related configuration can occur.
No inputs are required.
The initialization command runs with system defaults and prepares the environment automatically.
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.
None.
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.
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.
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.
None.
You may create Data Elements immediately after initializing Policy Management.
Typical inputs may include:
Sometimes you might want to create a mask or use a special alphabet in your policy.
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.
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.de_email is created to enforce consistent masking of email addresses, such as replacing the user portion with asterisks while preserving the domain.de_<datatype>_<method>.Create a Member Source that defines the external system from which user and group identities will be imported for use in roles and policies.
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.
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.
None.
Member Sources can be created at any time, though they are typically defined before assigning them to roles.
Inputs vary depending on the type of Member Source, but commonly include:
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.
ms_hr_directory and ms_test_users.Create a Role to represent a group of users or service accounts that will receive specific permissions in a policy.
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.
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.
None.
Roles can be created at any time, although they become active only after a Member Source is assigned in the next step.
Typical inputs when creating a Role include:
These inputs help clearly define the role’s identity and intended usage in policy rules.
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.
r_cc_protect is created for payment‑processing applications responsible for protecting credit card numbers using tokenization before storage.r_customer_support_masked is created for agents who may view masked customer data but cannot unprotect or view clear‑text values.r_<domain>_<capability>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.
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.
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.
Typical inputs for this step include:
You may also optionally run a synchronization operation after assignment so that the Role reflects current membership from the source immediately.
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.
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.pci_analysts or hr_analysts. Note that some of the examples will not use groups.Create an empty Policy Shell that acts as the container for roles, data elements, rules, and deployment configuration.
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.
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.
Typical inputs for this step include:
At this stage, no data elements, roles, or permissions are defined.Only the policy container itself is defined.
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.
policy_credit_card intended to govern how credit card numbers are tokenized and which users can unprotect them.policy_support_data is created to organize rules that provide masked data to customer service roles while restricting access to full values.Define a rule that specifies how a Role may interact with a Data Element by assigning permissions such as protect, unprotect, mask, or view.
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.
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.
Typical inputs for this step include:
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.
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.r_support_masked role, allowing customer support teams to view masked data but not access clear text or perform protection operations.r_<domain>_protect and r_<domain>_viewmasked.Create a Datastore entry that represents the application, service, or infrastructure component where the policy will be deployed and enforced.
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.
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.
Typical Datastore inputs include:
Actual inputs depend on the type of enforcement point being registered.
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.
None.
ds_payments_prod and ds_analytics_dev.Deploy the completed policy to a Datastore so that its rules are actively enforced during real data access operations.
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.
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.
Typical deployment inputs include:
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.
Not applicable.
Verify that the policy has been successfully deployed to the intended Datastore by retrieving deployment information.
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.
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.
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.
No inputs are required. The confirmation command runs without arguments.
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.
Not applicable.
Create a policy that protects Credit Card Number (CCN) using CCN data element, with:
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:
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.
To execute any CLI or API command in this example, the following assumptions have been made:
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:
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.
Creating the data element that defines:
Data elements are the protection building blocks that will be granted the permissions by the policy.
Tips
--from-left 0 --from-right 0.--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}}}
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.
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":"*"}
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.
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"}
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":""}}
Creating the role that represents who can perform operations against the data element.
Permissions are granted to roles and roles map to users or groups, ideally from member sources.
Tips
--allow-all option in the command, then set ALLOWALL to True.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}
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.
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"}]
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.
Creating the policy that will hold the access rules. For example, Data Element, Role, and Rules.
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}}}
Creating the policy rule that binds:
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}}}
Creating the datastore target where a policy will be deployed.
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"}
Deploying the policy to the datastore so protectors that target that datastore can load the policy.
Until the policy is deployed, the policy is not available to runtime protectors.
Tips
–-policies parameter.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
Confirming which policies are deployed to which datastores.
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":[]}]}
Create one policy that protects Date of Birth (DOB) using Datetime data element, with:
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.
To execute any CLI or API command in this example, the following assumptions have been made:
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:
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.
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.
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"}}
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.
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"}
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":""}}
Creating the role that represents who can perform operations against the DOB data element.
Permissions are granted to roles, and roles map to real users or groups via member sources.
Tips
--allow-all option in the command, then set ALLOWALL to True.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}
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.
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"}]
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.
Creating the policy that will hold the access rules. For example, Data Element, Role, and Rules.
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}}}
Creating the policy rule that binds:
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}}}
Creating the datastore target where a policy will be deployed.
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"}
Deploying the policy to the datastore so protectors that target that datastore can load the policy.
Until the policy is deployed, the policy is not available to runtime protectors.
Tips
–-policies option in the command.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.
Confirm that the policies have been deployed to the respective datastores.
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":[]}]}
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.
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"
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"