comparison sat/memory/memory.py @ 3541:888109774673

core: various changes and fixes to work with new storage and D-Bus bridge: - fixes coroutines handling in various places - fixes types which are not serialised by Tx DBus - XEP-0384: call storage methods in main thread in XEP: Python OMEMO's Promise use thread which prevent the use of AsyncIO loop. To work around that, callLater is used to launch storage method in main thread. This is a temporary workaround, as Python OMEMO should get rid of Promise implementation and threads soon.
author Goffi <goffi@goffi.org>
date Thu, 03 Jun 2021 15:21:43 +0200
parents f9a5b810f14d
children 71516731d0aa
comparison
equal deleted inserted replaced
3540:aa58451b77ba 3541:888109774673
1129 return self.params._getParamsValuesFromCategory( 1129 return self.params._getParamsValuesFromCategory(
1130 category, security_limit, app, extra_s, profile_key 1130 category, security_limit, app, extra_s, profile_key
1131 ) 1131 )
1132 1132
1133 def asyncGetStringParamA( 1133 def asyncGetStringParamA(
1134 self, name, category, attr="value", security_limit=C.NO_SECURITY_LIMIT, 1134 self, name, category, attribute="value", security_limit=C.NO_SECURITY_LIMIT,
1135 profile_key=C.PROF_KEY_NONE): 1135 profile_key=C.PROF_KEY_NONE):
1136 1136
1137 profile = self.getProfileName(profile_key) 1137 profile = self.getProfileName(profile_key)
1138 return defer.ensureDeferred(self.params.asyncGetStringParamA( 1138 return defer.ensureDeferred(self.params.asyncGetStringParamA(
1139 name, category, attr, security_limit, profile 1139 name, category, attribute, security_limit, profile
1140 )) 1140 ))
1141 1141
1142 def _getParamsUI(self, security_limit, app, extra_s, profile_key): 1142 def _getParamsUI(self, security_limit, app, extra_s, profile_key):
1143 return self.params._getParamsUI(security_limit, app, extra_s, profile_key) 1143 return self.params._getParamsUI(security_limit, app, extra_s, profile_key)
1144 1144
1168 1168
1169 def _privateDataSet(self, namespace, key, data_s, profile_key): 1169 def _privateDataSet(self, namespace, key, data_s, profile_key):
1170 client = self.host.getClient(profile_key) 1170 client = self.host.getClient(profile_key)
1171 # we accept any type 1171 # we accept any type
1172 data = data_format.deserialise(data_s, type_check=None) 1172 data = data_format.deserialise(data_s, type_check=None)
1173 return self.storage.setPrivateValue( 1173 return defer.ensureDeferred(self.storage.setPrivateValue(
1174 namespace, key, data, binary=True, profile=client.profile) 1174 namespace, key, data, binary=True, profile=client.profile))
1175 1175
1176 def _privateDataGet(self, namespace, key, profile_key): 1176 def _privateDataGet(self, namespace, key, profile_key):
1177 client = self.host.getClient(profile_key) 1177 client = self.host.getClient(profile_key)
1178 d = self.storage.getPrivates( 1178 d = defer.ensureDeferred(
1179 namespace, [key], binary=True, profile=client.profile) 1179 self.storage.getPrivates(
1180 namespace, [key], binary=True, profile=client.profile)
1181 )
1180 d.addCallback(lambda data_dict: data_format.serialise(data_dict.get(key))) 1182 d.addCallback(lambda data_dict: data_format.serialise(data_dict.get(key)))
1181 return d 1183 return d
1182 1184
1183 def _privateDataDelete(self, namespace, key, profile_key): 1185 def _privateDataDelete(self, namespace, key, profile_key):
1184 client = self.host.getClient(profile_key) 1186 client = self.host.getClient(profile_key)
1185 return self.storage.delPrivateValue( 1187 return defer.ensureDeferred(self.storage.delPrivateValue(
1186 namespace, key, binary=True, profile=client.profile) 1188 namespace, key, binary=True, profile=client.profile))
1187 1189
1188 ## Files ## 1190 ## Files ##
1189 1191
1190 def checkFilePermission( 1192 def checkFilePermission(
1191 self, 1193 self,
1249 else: 1251 else:
1250 raise exceptions.InternalError( 1252 raise exceptions.InternalError(
1251 _("unknown access type: {type}").format(type=perm_type) 1253 _("unknown access type: {type}").format(type=perm_type)
1252 ) 1254 )
1253 1255
1254 @defer.inlineCallbacks 1256 async def checkPermissionToRoot(self, client, file_data, peer_jid, perms_to_check):
1255 def checkPermissionToRoot(self, client, file_data, peer_jid, perms_to_check):
1256 """do checkFilePermission on file_data and all its parents until root""" 1257 """do checkFilePermission on file_data and all its parents until root"""
1257 current = file_data 1258 current = file_data
1258 while True: 1259 while True:
1259 self.checkFilePermission(current, peer_jid, perms_to_check) 1260 self.checkFilePermission(current, peer_jid, perms_to_check)
1260 parent = current["parent"] 1261 parent = current["parent"]
1261 if not parent: 1262 if not parent:
1262 break 1263 break
1263 files_data = yield self.getFiles( 1264 files_data = await self.getFiles(
1264 client, peer_jid=None, file_id=parent, perms_to_check=None 1265 client, peer_jid=None, file_id=parent, perms_to_check=None
1265 ) 1266 )
1266 try: 1267 try:
1267 current = files_data[0] 1268 current = files_data[0]
1268 except IndexError: 1269 except IndexError:
1269 raise exceptions.DataError("Missing parent") 1270 raise exceptions.DataError("Missing parent")
1270 1271
1271 @defer.inlineCallbacks 1272 async def _getParentDir(
1272 def _getParentDir(
1273 self, client, path, parent, namespace, owner, peer_jid, perms_to_check 1273 self, client, path, parent, namespace, owner, peer_jid, perms_to_check
1274 ): 1274 ):
1275 """Retrieve parent node from a path, or last existing directory 1275 """Retrieve parent node from a path, or last existing directory
1276 1276
1277 each directory of the path will be retrieved, until the last existing one 1277 each directory of the path will be retrieved, until the last existing one
1291 1291
1292 # we retrieve all directories from path until we get the parent container 1292 # we retrieve all directories from path until we get the parent container
1293 # non existing directories will be created 1293 # non existing directories will be created
1294 parent = "" 1294 parent = ""
1295 for idx, path_elt in enumerate(path_elts): 1295 for idx, path_elt in enumerate(path_elts):
1296 directories = yield self.storage.getFiles( 1296 directories = await self.storage.getFiles(
1297 client, 1297 client,
1298 parent=parent, 1298 parent=parent,
1299 type_=C.FILE_TYPE_DIRECTORY, 1299 type_=C.FILE_TYPE_DIRECTORY,
1300 name=path_elt, 1300 name=path_elt,
1301 namespace=namespace, 1301 namespace=namespace,
1302 owner=owner, 1302 owner=owner,
1303 ) 1303 )
1304 if not directories: 1304 if not directories:
1305 defer.returnValue((parent, path_elts[idx:])) 1305 return (parent, path_elts[idx:])
1306 # from this point, directories don't exist anymore, we have to create them 1306 # from this point, directories don't exist anymore, we have to create them
1307 elif len(directories) > 1: 1307 elif len(directories) > 1:
1308 raise exceptions.InternalError( 1308 raise exceptions.InternalError(
1309 _("Several directories found, this should not happen") 1309 _("Several directories found, this should not happen")
1310 ) 1310 )
1311 else: 1311 else:
1312 directory = directories[0] 1312 directory = directories[0]
1313 self.checkFilePermission(directory, peer_jid, perms_to_check) 1313 self.checkFilePermission(directory, peer_jid, perms_to_check)
1314 parent = directory["id"] 1314 parent = directory["id"]
1315 defer.returnValue((parent, [])) 1315 return (parent, [])
1316 1316
1317 def getFileAffiliations(self, file_data: dict) -> Dict[jid.JID, str]: 1317 def getFileAffiliations(self, file_data: dict) -> Dict[jid.JID, str]:
1318 """Convert file access to pubsub like affiliations""" 1318 """Convert file access to pubsub like affiliations"""
1319 affiliations = {} 1319 affiliations = {}
1320 access_data = file_data['access'] 1320 access_data = file_data['access']
1482 raise exceptions.InternalError( 1482 raise exceptions.InternalError(
1483 "Owner must be set for component if peer_jid is None" 1483 "Owner must be set for component if peer_jid is None"
1484 ) 1484 )
1485 return peer_jid.userhostJID() 1485 return peer_jid.userhostJID()
1486 1486
1487 @defer.inlineCallbacks 1487 async def getFiles(
1488 def getFiles(
1489 self, client, peer_jid, file_id=None, version=None, parent=None, path=None, 1488 self, client, peer_jid, file_id=None, version=None, parent=None, path=None,
1490 type_=None, file_hash=None, hash_algo=None, name=None, namespace=None, 1489 type_=None, file_hash=None, hash_algo=None, name=None, namespace=None,
1491 mime_type=None, public_id=None, owner=None, access=None, projection=None, 1490 mime_type=None, public_id=None, owner=None, access=None, projection=None,
1492 unique=False, perms_to_check=(C.ACCESS_PERM_READ,)): 1491 unique=False, perms_to_check=(C.ACCESS_PERM_READ,)):
1493 """Retrieve files with with given filters 1492 """Retrieve files with with given filters
1534 ) 1533 )
1535 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent) 1534 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent)
1536 if path is not None: 1535 if path is not None:
1537 path = str(path) 1536 path = str(path)
1538 # permission are checked by _getParentDir 1537 # permission are checked by _getParentDir
1539 parent, remaining_path_elts = yield self._getParentDir( 1538 parent, remaining_path_elts = await self._getParentDir(
1540 client, path, parent, namespace, owner, peer_jid, perms_to_check 1539 client, path, parent, namespace, owner, peer_jid, perms_to_check
1541 ) 1540 )
1542 if remaining_path_elts: 1541 if remaining_path_elts:
1543 # if we have remaining path elements, 1542 # if we have remaining path elements,
1544 # the parent directory is not found 1543 # the parent directory is not found
1545 raise failure.Failure(exceptions.NotFound()) 1544 raise failure.Failure(exceptions.NotFound())
1546 if parent and peer_jid: 1545 if parent and peer_jid:
1547 # if parent is given directly and permission check is requested, 1546 # if parent is given directly and permission check is requested,
1548 # we need to check all the parents 1547 # we need to check all the parents
1549 parent_data = yield self.storage.getFiles(client, file_id=parent) 1548 parent_data = await self.storage.getFiles(client, file_id=parent)
1550 try: 1549 try:
1551 parent_data = parent_data[0] 1550 parent_data = parent_data[0]
1552 except IndexError: 1551 except IndexError:
1553 raise exceptions.DataError("mising parent") 1552 raise exceptions.DataError("mising parent")
1554 yield self.checkPermissionToRoot( 1553 await self.checkPermissionToRoot(
1555 client, parent_data, peer_jid, perms_to_check 1554 client, parent_data, peer_jid, perms_to_check
1556 ) 1555 )
1557 1556
1558 files = yield self.storage.getFiles( 1557 files = await self.storage.getFiles(
1559 client, 1558 client,
1560 file_id=file_id, 1559 file_id=file_id,
1561 version=version, 1560 version=version,
1562 parent=parent, 1561 parent=parent,
1563 type_=type_, 1562 type_=type_,
1576 if peer_jid: 1575 if peer_jid:
1577 # if permission are checked, we must remove all file that user can't access 1576 # if permission are checked, we must remove all file that user can't access
1578 to_remove = [] 1577 to_remove = []
1579 for file_data in files: 1578 for file_data in files:
1580 try: 1579 try:
1581 self.checkFilePermission(file_data, peer_jid, perms_to_check, set_affiliation=True) 1580 self.checkFilePermission(
1581 file_data, peer_jid, perms_to_check, set_affiliation=True
1582 )
1582 except exceptions.PermissionError: 1583 except exceptions.PermissionError:
1583 to_remove.append(file_data) 1584 to_remove.append(file_data)
1584 for file_data in to_remove: 1585 for file_data in to_remove:
1585 files.remove(file_data) 1586 files.remove(file_data)
1586 defer.returnValue(files) 1587 return files
1587 1588
1588 @defer.inlineCallbacks 1589 async def setFile(
1589 def setFile(
1590 self, client, name, file_id=None, version="", parent=None, path=None, 1590 self, client, name, file_id=None, version="", parent=None, path=None,
1591 type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None, 1591 type_=C.FILE_TYPE_FILE, file_hash=None, hash_algo=None, size=None,
1592 namespace=None, mime_type=None, public_id=None, created=None, modified=None, 1592 namespace=None, mime_type=None, public_id=None, created=None, modified=None,
1593 owner=None, access=None, extra=None, peer_jid=None, 1593 owner=None, access=None, extra=None, peer_jid=None,
1594 perms_to_check=(C.ACCESS_PERM_WRITE,) 1594 perms_to_check=(C.ACCESS_PERM_WRITE,)
1666 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent) 1666 owner = self.getFilesOwner(client, owner, peer_jid, file_id, parent)
1667 1667
1668 if path is not None: 1668 if path is not None:
1669 path = str(path) 1669 path = str(path)
1670 # _getParentDir will check permissions if peer_jid is set, so we use owner 1670 # _getParentDir will check permissions if peer_jid is set, so we use owner
1671 parent, remaining_path_elts = yield self._getParentDir( 1671 parent, remaining_path_elts = await self._getParentDir(
1672 client, path, parent, namespace, owner, owner, perms_to_check 1672 client, path, parent, namespace, owner, owner, perms_to_check
1673 ) 1673 )
1674 # if remaining directories don't exist, we have to create them 1674 # if remaining directories don't exist, we have to create them
1675 for new_dir in remaining_path_elts: 1675 for new_dir in remaining_path_elts:
1676 new_dir_id = shortuuid.uuid() 1676 new_dir_id = shortuuid.uuid()
1677 yield self.storage.setFile( 1677 await self.storage.setFile(
1678 client, 1678 client,
1679 name=new_dir, 1679 name=new_dir,
1680 file_id=new_dir_id, 1680 file_id=new_dir_id,
1681 version="", 1681 version="",
1682 parent=parent, 1682 parent=parent,
1689 ) 1689 )
1690 parent = new_dir_id 1690 parent = new_dir_id
1691 elif parent is None: 1691 elif parent is None:
1692 parent = "" 1692 parent = ""
1693 1693
1694 yield self.storage.setFile( 1694 await self.storage.setFile(
1695 client, 1695 client,
1696 file_id=file_id, 1696 file_id=file_id,
1697 version=version, 1697 version=version,
1698 parent=parent, 1698 parent=parent,
1699 type_=type_, 1699 type_=type_,