Repository navigation
Expand file tree
/
Copy pathcreate_entitlements.py
More file actions
126 lines (107 loc) · 5.37 KB
/
Copy pathcreate_entitlements.py
File metadata and controls
126 lines (107 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
"""Grant the logged in user the Roles for the OGCR dynamic entities.
Grants the entity DEFINITION Roles, the Domain API Roles, the Roles for managing the Role Groups
and adding users to them (see role_groups.py), plus the RECORD Roles for every
`Entity: <name>` in the parsed entities file (produced by
`parse_minimum_fields.py --save`), all at the bank id of the entities' space
(OBP_ENTITY_SPACE_ID, or SYS for system level; see obp_space.py). An entity's record Roles
only exist on OBP once the entity does, so entities not on OBP yet are skipped.
Roles the user already holds (at the same bank id) are not requested again; only
the missing ones are granted. Exits 1 if any grant failed.
Usage:
python3 create_entitlements.py [path/to/entities_output.txt]
"""
import sys
from delete_dynamic_entities import DEFAULT_INPUT, parse_entity_names
from get_and_delete_dynamic_entities import get_all_system_dynamic_entities
from obp_client import token, obp_host, session, authorization, require_user_mode
from obp_space import DOMAIN_API_ROLE_NAMES, ROLE_BANK_ID, describe
from role_groups import GROUP_ADMIN_ROLES
def add_entitlement_to_user(token, user_id, role_name, bank_id=""):
"""Grant `role_name` at `bank_id` to the user; returns OBP's JSON response."""
# v7.0.0: the older versions reject the SYS bank id with OBP-30001 Bank not found.
response = session.post(f"{obp_host}/obp/v7.0.0/users/{user_id}/entitlements",
headers={"Authorization": authorization(token), "Content-Type": "application/json"},
json={"bank_id": bank_id, "role_name": role_name})
if not response.ok:
print(response, response.text)
return response.json()
require_user_mode("create_entitlements.py")
entities_file = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_INPUT
entity_names = parse_entity_names(entities_file)
print(f"Read {len(entity_names)} entities from {entities_file}")
# Grant the Roles to whoever is logged in.
response = session.get(f"{obp_host}/obp/v6.0.0/users/current",
headers={"Authorization": authorization(token)})
response.raise_for_status()
user_id = response.json()["user_id"]
held = {(e.get("role_name"), e.get("bank_id", ""))
for e in response.json().get("entitlements", {}).get("list", [])}
print(f"Granting Roles for the entities at {describe()} to user_id {user_id}, at bank id {ROLE_BANK_ID}")
# The Roles that gate an entity's DEFINITION. The dynamic entity management endpoints require these
# (they replaced the old CanXSystemLevelDynamicEntity names) and, like the Record Roles below, they
# are granted at the bank id of the space.
meta_role_names = [
"CanCreateDynamicEntityDefinition",
"CanDeleteDynamicEntityDefinition",
"CanGetDynamicEntityDefinitions",
"CanUpdateDynamicEntityDefinition"
]
# The Roles that gate an entity's RECORDS. They were renamed on 2026-09-24: the "_System" variant is
# gone, because the Role no longer says which space it applies to -- the bank id of the grant does.
# For system level entities that bank id is the literal SYS, not the empty string.
role_names = [
"CanCreateDynamicEntityRecord_",
"CanDeleteDynamicEntityRecord_",
"CanGetDynamicEntityRecord_",
"CanUpdateDynamicEntityRecord_"
]
# The Roles wanted before the entities can be looked at: definition Roles, Domain API Roles, group Roles.
wanted = [(m, ROLE_BANK_ID) for m in meta_role_names]
# The Roles domain_api.py needs, also at the bank id of the space.
wanted += [(d, ROLE_BANK_ID) for d in DOMAIN_API_ROLE_NAMES]
# The Roles create_role_groups.py and add_users_to_groups.py need (steps 4 and 5 of
# recreate_dynamic_entities.sh). Some are system level, so each carries its own bank id.
wanted += GROUP_ADMIN_ROLES
failed = []
granted_count = 0
def grant_missing(pairs):
"""Grant each (role_name, bank_id) the user doesn't hold yet; failures are collected in `failed`."""
global granted_count
for role_name, bank_id in pairs:
if (role_name, bank_id) in held:
continue
where = f" @ {bank_id}" if bank_id else ""
try:
result = add_entitlement_to_user(
token=token,
user_id=user_id,
role_name=role_name,
bank_id=bank_id)
except Exception as e: # e.g. connection error, or a response that is not JSON
print(f" {e}")
result = None
if isinstance(result, dict) and result.get("entitlement_id"):
print(f" ✓ granted {role_name}{where}")
held.add((role_name, bank_id))
granted_count += 1
else:
failed.append(f"{role_name}{where}")
print(f" ✗ could not grant {role_name}{where}")
# These first: listing the entities below needs CanGetDynamicEntityDefinitions, which a new user
# (e.g. on a fresh LOCAL) only gets here.
grant_missing(wanted)
existing = get_all_system_dynamic_entities(token=token) # raises if OBP cannot list them
existing_names = {e.get("entity_name") for e in existing.get("dynamic_entities", [])}
skipped = [name for name in entity_names if name not in existing_names]
for name in skipped:
print(f"skipping {name}: entity not at {describe()} on OBP yet")
record_roles = [(r + name, ROLE_BANK_ID) for name in entity_names if name not in skipped for r in role_names]
grant_missing(record_roles)
total = len(wanted) + len(record_roles)
print(f"{total - granted_count - len(failed)} of {total} Roles already held; granted {granted_count}")
if skipped:
print(f"Skipped {len(skipped)} of {len(entity_names)} entities not on OBP yet; "
"create them, then run this again")
if failed:
print(f"✗ {len(failed)} Role(s) could not be granted: {', '.join(failed)}")
sys.exit(1)