Skip to content

Commit 50925c9

Browse files
Created COManage scripts method library
Created a file to hold methods that are, or will likely be, used by multiple scripts in the osg-comanage-rpoject-usermap repo. Refactored existing scripts to use the new library.
1 parent 60139cb commit 50925c9

5 files changed

Lines changed: 289 additions & 554 deletions

File tree

comanage_scripts_utils.py

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import re
5+
import sys
6+
import json
7+
import urllib.error
8+
import urllib.request
9+
from ldap3 import Server, Connection, ALL, ALL_ATTRIBUTES, SAFE_SYNC
10+
11+
MIN_TIMEOUT = 5
12+
MAX_TIMEOUT = 625
13+
TIMEOUTMULTIPLE = 5
14+
15+
GET = "GET"
16+
PUT = "PUT"
17+
POST = "POST"
18+
DELETE = "DELETE"
19+
20+
21+
def getpw(user, passfd, passfile):
22+
if ":" in user:
23+
user, pw = user.split(":", 1)
24+
elif passfd is not None:
25+
pw = os.fdopen(passfd).readline().rstrip("\n")
26+
elif passfile is not None:
27+
pw = open(passfile).readline().rstrip("\n")
28+
elif "PASS" in os.environ:
29+
pw = os.environ["PASS"]
30+
else:
31+
raise PermissionError
32+
#when script needs to say PASS required, raise a permission error
33+
#usage("PASS required")
34+
return user, pw
35+
36+
37+
def mkauthstr(user, passwd):
38+
from base64 import encodebytes
39+
raw_authstr = "%s:%s" % (user, passwd)
40+
return encodebytes(raw_authstr.encode()).decode().replace("\n", "")
41+
42+
43+
def mkrequest(method, target, data, endpoint, authstr, **kw):
44+
url = os.path.join(endpoint, target)
45+
if kw:
46+
url += "?" + "&".join("{}={}".format(k,v) for k,v in kw.items())
47+
req = urllib.request.Request(url, json.dumps(data).encode("utf-8"))
48+
req.add_header("Authorization", "Basic %s" % authstr)
49+
req.add_header("Content-Type", "application/json")
50+
req.get_method = lambda: method
51+
return req
52+
53+
54+
def call_api(target, endpoint, authstr, **kw):
55+
return call_api2(GET, target, endpoint, authstr, **kw)
56+
57+
58+
def call_api2(method, target, endpoint, authstr, **kw):
59+
return call_api3(method, target, data=None, endpoint=endpoint, authstr=authstr, **kw)
60+
61+
62+
def call_api3(method, target, data, endpoint, authstr, **kw):
63+
req = mkrequest(method, target, data, endpoint, authstr, **kw)
64+
trying = True
65+
currentTimeout = MIN_TIMEOUT
66+
while trying:
67+
try:
68+
resp = urllib.request.urlopen(req, timeout=currentTimeout)
69+
payload = resp.read()
70+
trying = False
71+
except urllib.error.URLError as exception:
72+
if currentTimeout < MAX_TIMEOUT:
73+
currentTimeout *= TIMEOUTMULTIPLE
74+
else:
75+
sys.exit(
76+
f"Exception raised after maximum retrys and/or timeout {MAX_TIMEOUT} seconds reached. "
77+
+ f"Exception reason: {exception.reason}.\n Request: {req.full_url}"
78+
)
79+
80+
return json.loads(payload) if payload else None
81+
82+
83+
def get_osg_co_groups(osg_co_id, endpoint, authstr):
84+
return call_api("co_groups.json", endpoint, authstr, coid=osg_co_id)
85+
86+
87+
def get_co_group_identifiers(gid, endpoint, authstr):
88+
return call_api("identifiers.json", endpoint, authstr, cogroupid=gid)
89+
90+
91+
def get_co_group_members(gid, endpoint, authstr):
92+
return call_api("co_group_members.json", endpoint, authstr, cogroupid=gid)
93+
94+
95+
def get_co_person_identifiers(pid, endpoint, authstr):
96+
return call_api("identifiers.json", endpoint, authstr, copersonid=pid)
97+
98+
99+
def get_co_group(gid, endpoint, authstr):
100+
resp_data = call_api("co_groups/%s.json" % gid, endpoint, authstr)
101+
grouplist = get_datalist(resp_data, "CoGroups")
102+
if not grouplist:
103+
raise RuntimeError("No such CO Group Id: %s" % gid)
104+
return grouplist[0]
105+
106+
107+
def get_identifier(id_, endpoint, authstr):
108+
resp_data = call_api("identifiers/%s.json" % id_, endpoint, authstr)
109+
idfs = get_datalist(resp_data, "Identifiers")
110+
if not idfs:
111+
raise RuntimeError("No such Identifier Id: %s" % id_)
112+
return idfs[0]
113+
114+
115+
def get_unix_cluster_groups(ucid, endpoint, authstr):
116+
return call_api("unix_cluster/unix_cluster_groups.json", endpoint, authstr, unix_cluster_id=ucid)
117+
118+
119+
def get_unix_cluster_groups_ids(ucid, endpoint, authstr):
120+
unix_cluster_groups = get_unix_cluster_groups(ucid, endpoint, authstr)
121+
return set(group["CoGroupId"] for group in unix_cluster_groups["UnixClusterGroups"])
122+
123+
124+
def delete_identifier(id_, endpoint, authstr):
125+
return call_api2(DELETE, "identifiers/%s.json" % id_, endpoint, authstr)
126+
127+
128+
def get_datalist(data, listname):
129+
return data[listname] if data else []
130+
131+
132+
def get_ldap_groups(ldap_server, ldap_user, ldap_authtok):
133+
ldap_group_osggids = set()
134+
server = Server(ldap_server, get_info=ALL)
135+
connection = Connection(server, ldap_user, ldap_authtok, client_strategy=SAFE_SYNC, auto_bind=True)
136+
_, _, response, _ = connection.search("ou=groups,o=OSG,o=CO,dc=cilogon,dc=org", "(cn=*)", attributes=ALL_ATTRIBUTES)
137+
for group in response:
138+
ldap_group_osggids.add(group["attributes"]["gidNumber"])
139+
return ldap_group_osggids
140+
141+
142+
def identifier_from_list(id_list, id_type):
143+
id_type_list = [id["Type"] for id in id_list]
144+
try:
145+
id_index = id_type_list.index(id_type)
146+
return id_list[id_index]["Identifier"]
147+
except ValueError:
148+
return None
149+
150+
151+
def identifier_matches(id_list, id_type, regex_string):
152+
pattern = re.compile(regex_string)
153+
value = identifier_from_list(id_list, id_type)
154+
return (value is not None) & (pattern.match(value) is not None)
155+
156+
157+
def rename_co_group(gid, group, newname, endpoint, authstr):
158+
# minimal edit CoGroup Request includes Name+CoId+Status+Version
159+
new_group_info = {
160+
"Name" : newname,
161+
"CoId" : group["CoId"],
162+
"Status" : group["Status"],
163+
"Version" : group["Version"]
164+
}
165+
data = {
166+
"CoGroups" : [new_group_info],
167+
"RequestType" : "CoGroups",
168+
"Version" : "1.0"
169+
}
170+
return call_api3(PUT, "co_groups/%s.json" % gid, data, endpoint, authstr)
171+
172+
173+
def add_identifier_to_group(gid, type, identifier_value, endpoint, authstr):
174+
new_identifier_info = {
175+
"Version": "1.0",
176+
"Type": type,
177+
"Identifier": identifier_value,
178+
"Login": False,
179+
"Person": {"Type": "Group", "Id": str(gid)},
180+
"Status": "Active",
181+
}
182+
data = {
183+
"RequestType": "Identifiers",
184+
"Version": "1.0",
185+
"Identifiers": [new_identifier_info],
186+
}
187+
return call_api3(POST, "identifiers.json", data, endpoint, authstr)
188+
189+
190+
def add_unix_cluster_group(gid, ucid, endpoint, authstr):
191+
data = {
192+
"RequestType": "UnixClusterGroups",
193+
"Version": "1.0",
194+
"UnixClusterGroups": [{"Version": "1.0", "UnixClusterId": ucid, "CoGroupId": gid}],
195+
}
196+
return call_api3(POST, "unix_cluster/unix_cluster_groups.json", data, endpoint, authstr)
197+
198+
199+
def provision_group(gid, provision_target, endpoint, authstr):
200+
path = f"co_provisioning_targets/provision/{provision_target}/cogroupid:{gid}.json"
201+
data = {
202+
"RequestType" : "CoGroupProvisioning",
203+
"Version" : "1.0",
204+
"Synchronous" : True
205+
}
206+
return call_api3(POST, path, data, endpoint, authstr)
207+
208+
def provision_group_members(gid, prov_id, endpoint, authstr):
209+
data = {
210+
"RequestType" : "CoPersonProvisioning",
211+
"Version" : "1.0",
212+
"Synchronous" : True
213+
}
214+
responses = {}
215+
for member in get_co_group_members(gid, endpoint, authstr)["CoGroupMembers"]:
216+
if member["Person"]["Type"] == "CO":
217+
pid = member["Person"]["Id"]
218+
path = f"co_provisioning_targets/provision/{prov_id}/copersonid:{pid}.json"
219+
responses[pid] = call_api3(POST, path, data, endpoint, authstr)
220+
return responses

0 commit comments

Comments
 (0)