Skip to content

Commit e565b16

Browse files
committed
add true ACLOperation/iRODSAccess canonicalization for api calls and comparison
1 parent 5b1c514 commit e565b16

4 files changed

Lines changed: 39 additions & 29 deletions

File tree

README.md

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2142,39 +2142,18 @@ indeed are subclassed from them as well, allowing intermixed sequences to be
21422142
sorted; however, care should be taken to normalize the objects before comparisons
21432143
or for related uses of the 'in' operator:
21442144
```py
2145-
normalize = lambda acl: acl.copy(decanonicalize=True, implied_zone='tempZone')
2145+
normalize = lambda acl: acl.copy(decanonicalize=-1, implied_zone='tempZone')
21462146
acls = sorted(
21472147
[
21482148
iRODSAccess('read object', '/tempZone/home/alice', 'bob', 'tempZone'),
21492149
ACLOperation('write', 'rods'),
21502150
ACLOperation('read', 'bob'),
2151-
],
2151+
],
21522152
key=normalize
21532153
)
21542154
print(normalize(acls[0]) == normalize(acls[1]))
21552155
```
21562156

2157-
Thus, for example:
2158-
```py
2159-
ACLOperation('read','public') in sess.acls.get(object)
2160-
```
2161-
is a valid operation, so that an application that tends to cache object
2162-
permissions client-side might use such checks in optimizing atomic ACL
2163-
requests against the inclusion of any redundant ACL operations.
2164-
2165-
For purposes of sorting, a `__lt__` operator is also defined that would
2166-
allow sorting of lists of `iRODSAccess`, `ACLOperations`, or the two intermixed:
2167-
```py
2168-
perms_list=[
2169-
ACLOperation('read', 'bob'),
2170-
iRODSAccess('read', '/tempZone/home/alice', 'alice')
2171-
]
2172-
print(sorted(perms_list))
2173-
```
2174-
2175-
and, as always, a sort key may be used for custom sorting; for example,
2176-
the following sorts the objects simply by ascending numerical value of the access:
2177-
```py
21782157
perms = sorted(
21792158
[
21802159
ACLOperation('read', 'bob'),

irods/access.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,44 @@ def __hash__(self):
123123
return hash((self.access_name, iRODSPath(self.path), self.user_name, self.user_zone))
124124

125125
def copy(self, decanonicalize=False, implied_zone=''):
126+
"""
127+
Create a copy of the object, possibly in a normalized form.
128+
129+
Args:
130+
decanonicalize: Whether to modify to access_name field to a more human (1/True) or more standard (-1) form
131+
If the former, then one-word style is favored, ie "read" and "write". If the latter, the new access_name
132+
will be more machine-friendly for operators __lt__ (for sorting) and __eq__ (for equivalence or use with 'in').
133+
implied_zone: If a nonzero-length name, compare this against the zone_name field of the old object and force the zone_name to
134+
zero-length in the new object.
135+
136+
Returns:
137+
The new copy
138+
"""
126139
other = copy.deepcopy(self)
127140

128-
if decanonicalize:
129-
replacement_string = {
141+
access_name = self.access_name
142+
143+
if decanonicalize == 1:
144+
if (new_access_name := {
130145
"read object": "read",
131146
"read_object": "read",
132147
"modify object": "write",
133148
"modify_object": "write",
134-
}.get(self.access_name)
135-
other.access_name = replacement_string if replacement_string is not None else self.access_name
149+
}.get(access_name)) != None: access_name = new_access_name
150+
elif decanonicalize == -1:
151+
# Canonicalize, ie. change out old access_name for an unambiguous "standard" value.
152+
access_name = access_name.replace(" ","_")
153+
if (new_access_name := {
154+
"read": "read_object",
155+
"write": "modify_object",
156+
}.get(access_name)) != None: access_name = new_access_name
157+
elif decanonicalize == 0:
158+
pass
159+
else:
160+
msg = "Improper value for 'decanonicalize' parameter"
161+
raise RuntimerError(msg)
162+
163+
other.access_name = access_name
136164

137165
# Useful if we wish to force an explicitly specified local zone to an implicit zone spec in the copy, for equality testing:
138166
if '' != implied_zone == other.user_zone:
@@ -203,6 +231,9 @@ def __repr__(self):
203231
**{key: iRODSAccess.codes[_ichmod_synonym_mapping[key]] for key in _ichmod_synonym_mapping},
204232
}
205233

234+
canonical_permissions = dict(
235+
(k,v) for k,v in all_permissions.items() if ' ' not in k and k not in ('read','write')
236+
)
206237

207238
class _deprecated:
208239
class _iRODSAccess_pre_4_3_0(_iRODSAccess_base):

irods/manager/access_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ def set(self, acl, recursive=False, admin=False, **kw):
174174
zone_ = acl.user_zone
175175
if acl.access_name.endswith("inherit"):
176176
zone_ = userName_ = ""
177-
acl = acl.copy(decanonicalize=True)
177+
acl = acl.copy(decanonicalize=-1)
178178
message_body = ModAclRequest(
179179
recursiveFlag=int(recursive),
180180
accessLevel=f"{prefix}{acl.access_name}",

irods/test/access_test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ def test_atomic_acls__issue_505(self):
515515
)
516516

517517
def normalize(access):
518-
return access.copy(decanonicalize=True, implied_zone=ses.zone)
518+
return access.copy(decanonicalize=-1, implied_zone=ses.zone)
519519

520520
accesses = [normalize(acl) for acl in ses.acls.get(self.coll)]
521521

0 commit comments

Comments
 (0)