The RolesApi method for list_roles does not currently return the same information I see returned from the associated v2025 API endpoint /roles.
The membership criteria specifically is missing information. For example, our ALL Managers role has a criteria that checks if the isManager identity attribute = true, and I can see that criteria returned via the /roles API as:
{
"operation": "EQUALS",
"key": {
"type": "IDENTITY",
"property": "attribute.isManager",
"sourceId": null
},
"values": [
"true"
],
"stringValue": null,
"children": null
}
The list_roles method in python does not have the word “true” anywhere in the returned information for this role, though it does show a reference to the isManager attribute just without any values assigned to match it.
I suspect this may be related to the Role configuration changes that happened a decent while ago where we could put multiple values under one statement rather than having to make a bunch of ORs (see: Update! Enhancement: Improved Automated Role Assignment)
How can we get this fixed? We use this information to keep our role documentation up to date.
Below is the methedology I’m using in my python code. I’m not using the SDK:
import requests
Define the API endpoint and headers
url = "https://{tenant}.api.identitynow.com/v3/search"
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
Define the payload
payload = {
"indices": ["roles"],
"query": { "query": "*" },
"includeNested": True,
"queryResultFilter": {
"includes": ["id", "name", "membership", "parentId"]
}
}
Execute the POST request
response = requests.post(url, headers=headers, json=payload)
response.raise_for_status()
roles_data = response.json()
for role in roles_data:
role_name = role.get("name")
membership = role.get("membership")
criteria_block = membership.get("criteria") if membership else None
criteria_summary = summarize_criteria(criteria_block)
print(f"Role: {role_name} -> {criteria_summary}")
Yeah i’m thinking just avoiding the SDK altogether and interacting with the API directly is going to be better here. Not really sure what I gain from using the SDK, thanks for the code
What does your summarize_criteria method do? I’m assuming it formats it in a more human-readable way? Do you mind sharing?
It returns it as a string.
I then take it into a jinja2 template and display it with htmlx
def summarize_criteria(group: dict) -> str:
"""Human-readable summary, e.g. 'department IN [Sales, HR]'."""
if not group:
return "None"
operation = group.get("operation")
children = group.get("children")
key = group.get("key")
if not children and key:
prop = key.get("property", "").replace("attribute.", "")
multi = group.get("values")
if multi:
return f"{prop} IN [{', '.join(multi)}]"
val = group.get("stringValue")
op_sym = {"EQUALS": "==", "NOT_EQUALS": "!=", "CONTAINS": "contains",
"STARTS_WITH": "starts_with", "IS_NULL": "is null",
"IS_NOT_NULL": "is not null"}.get(operation, operation)
return f"{prop} {op_sym} '{val}'" if val is not None else f"{prop} {op_sym}"
if children:
subs = [s for s in (summarize_criteria(c) for c in children) if s != "None"]
return f"({f' {operation} '.join(subs)})" if subs else "None"
return "None"