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 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
from os.path import basename, dirname
from irods.manager import Manager
from irods.api_number import api_number
from irods.message import ModAclRequest, iRODSMessage
from irods.data_object import iRODSDataObject, irods_dirname, irods_basename
from irods.collection import iRODSCollection
from irods.models import (
DataObject,
Collection,
User,
CollectionUser,
DataAccess,
CollectionAccess,
)
from irods.access import iRODSAccess
from irods.column import In
from irods.user import iRODSUser
import logging
import warnings
logger = logging.getLogger(__name__)
def users_by_ids(session, ids=()):
try:
ids = list(iter(ids))
except TypeError:
if type(ids) in (str, int):
ids = int(ids)
else:
raise
cond = (
()
if not ids
else (
(In(User.id, list(map(int, ids))),)
if len(ids) > 1
else (User.id == int(ids[0]),)
)
)
return [
iRODSUser(session.users, i)
for i in session.query(User.id, User.name, User.type, User.zone).filter(*cond)
]
class AccessManager(Manager):
def get(self, target, report_raw_acls=True, **kw):
if report_raw_acls:
return self.__get_raw(
target, **kw
) # prefer a behavior consistent with 'ils -A`
# different query whether target is an object or a collection
if type(target) == iRODSDataObject:
access_type = DataAccess
user_type = User
conditions = [
Collection.name == dirname(target.path),
DataObject.name == basename(target.path),
]
elif type(target) == iRODSCollection:
access_type = CollectionAccess
user_type = CollectionUser
conditions = [Collection.name == target.path]
else:
raise TypeError
results = (
self.sess.query(user_type.name, user_type.zone, access_type.name)
.filter(*conditions)
.all()
)
def get_usertype(row):
return self.sess.users.get(row[user_type.name], row[user_type.zone]).type
return [
iRODSAccess(
access_name=row[access_type.name],
user_name=row[user_type.name],
user_type=get_usertype(row),
path=target.path,
user_zone=row[user_type.zone],
)
for row in results
]
def coll_access_query(self, path):
return self.sess.query(Collection, CollectionAccess).filter(
Collection.name == path
)
def data_access_query(self, path):
cn = irods_dirname(path)
dn = irods_basename(path)
return self.sess.query(DataObject, DataAccess).filter(
Collection.name == cn, DataObject.name == dn
)
def __get_raw(self, target, **kw):
### sample usage: ###
#
# user_id_list = [] # simply to store the user id's from the discovered ACL's
# session.acls.get( data_or_coll_target, acl_users = user_id_list,
# acl_users_transform = lambda u: u.id)
#
# -> returns list of iRODSAccess objects mapping one-to-one with ACL's stored in the catalog
users_out = kw.pop("acl_users", None)
T = kw.pop("acl_users_transform", lambda value: value)
# different choice of query based on whether target is an object or a collection
if isinstance(target, iRODSDataObject):
access_column = DataAccess
query_func = self.data_access_query
elif isinstance(target, iRODSCollection):
access_column = CollectionAccess
query_func = self.coll_access_query
else:
raise TypeError
# TODO: remove the filtering through extant_ids on resolution of irods/irods#6921.
# (depending on the nature of the fix we may make it conditional, based on the server --
# if for example in upcoming iRODS 4.2.12 and >=4.3.1 outdated userIDs in R_OBJT_ACCESS
# are guaranteed to be systematically and atomically purged.
extant_ids = set(u[User.id] for u in self.sess.query(User))
rows = [
r for r in query_func(target.path) if r[access_column.user_id] in extant_ids
]
userids = set(r[access_column.user_id] for r in rows)
user_lookup = {j.id: j for j in users_by_ids(self.sess, userids)}
if isinstance(users_out, dict):
users_out.update(user_lookup)
elif isinstance(users_out, list):
users_out += [T(v) for v in user_lookup.values()]
elif isinstance(users_out, set):
users_out |= set(T(v) for v in user_lookup.values())
elif users_out is None:
pass
else:
raise TypeError
# Instantiate as set before converting to a list, in order to remove duplicate iRODSAccess
# objects. [#557]
acls = list(
{
iRODSAccess(
r[access_column.name],
target.path,
user_lookup[r[access_column.user_id]].name,
user_lookup[r[access_column.user_id]].zone,
user_lookup[r[access_column.user_id]].type,
)
for r in rows
}
)
return acls
def set(self, acl, recursive=False, admin=False, **kw):
prefix = "admin:" if admin else ""
userName_ = acl.user_name
zone_ = acl.user_zone
if acl.access_name.endswith("inherit"):
zone_ = userName_ = ""
acl = acl.copy(decanonicalize=True)
message_body = ModAclRequest(
recursiveFlag=int(recursive),
accessLevel=f"{prefix}{acl.access_name}",
userName=userName_,
zone=zone_,
path=acl.path,
)
request = iRODSMessage(
"RODS_API_REQ",
msg=message_body,
int_info=api_number["MOD_ACCESS_CONTROL_AN"],
)
with self.sess.pool.get_connection() as conn:
conn.send(request)
response = conn.recv()
logger.debug(response.int_info)
|