plugin XEP-0329: added way to change access_model using PubSub-like configuration:

Those methods are not standard, but have been done in a PubSub-like way to prepare the
move to a PubSub based file sharing system.
This commit is contained in:
Goffi 2020-08-01 16:14:37 +02:00
commit 7dd1873adf

View file

@ -58,6 +58,8 @@ IQ_FIS_REQUEST = f'{C.IQ_GET}/query[@xmlns="{NS_FIS}"]'
# not in the standard, but needed, and it's handy to keep it here
IQ_FIS_AFFILIATION_GET = f'{C.IQ_GET}/affiliations[@xmlns="{NS_FIS_AFFILIATION}"]'
IQ_FIS_AFFILIATION_SET = f'{C.IQ_SET}/affiliations[@xmlns="{NS_FIS_AFFILIATION}"]'
IQ_FIS_CONFIGURATION_GET = f'{C.IQ_GET}/configuration[@xmlns="{NS_FIS_CONFIGURATION}"]'
IQ_FIS_CONFIGURATION_SET = f'{C.IQ_SET}/configuration[@xmlns="{NS_FIS_CONFIGURATION}"]'
SINGLE_FILES_DIR = "files"
TYPE_VIRTUAL = "virtual"
TYPE_PATH = "path"
@ -317,6 +319,22 @@ class XEP_0329(object):
method=self._affiliationsSet,
async_=True,
)
host.bridge.addMethod(
"FISConfigurationGet",
".plugin",
in_sign="ssss",
out_sign="a{ss}",
method=self._configurationGet,
async_=True,
)
host.bridge.addMethod(
"FISConfigurationSet",
".plugin",
in_sign="sssa{ss}s",
out_sign="",
method=self._configurationSet,
async_=True,
)
host.bridge.addSignal("FISSharedPathNew", ".plugin", signature="sss")
host.bridge.addSignal("FISSharedPathRemoved", ".plugin", signature="ss")
host.trigger.add("XEP-0234_fileSendingRequest", self._fileSendingRequestTrigger)
@ -856,6 +874,137 @@ class XEP_0329(object):
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
client.send(iq_result_elt)
# configuration
def _configurationGet(self, service_jid_s, namespace, path, profile):
client = self.host.getClient(profile)
service = jid.JID(service_jid_s)
d = defer.ensureDeferred(self.configurationGet(
client, service, namespace or None, path))
d.addCallback(
lambda configuration: {
str(entity): affiliation for entity, affiliation in configuration.items()
}
)
return d
async def configurationGet(
self,
client: SatXMPPEntity,
service: jid.JID,
namespace: Optional[str],
path: str
) -> Dict[str, str]:
if not path:
raise ValueError(f"invalid path: {path!r}")
iq_elt = client.IQ("get")
iq_elt['to'] = service.full()
configuration_elt = iq_elt.addElement((NS_FIS_CONFIGURATION, "configuration"))
if namespace:
configuration_elt["namespace"] = namespace
configuration_elt["path"] = path
iq_result_elt = await iq_elt.send()
try:
configuration_elt = next(iq_result_elt.elements(NS_FIS_CONFIGURATION, "configuration"))
except StopIteration:
raise exceptions.DataError(f"Invalid result to configuration request: {iq_result_elt.toXml()}")
form = data_form.findForm(configuration_elt, NS_FIS_CONFIGURATION)
configuration = {f.var: f.value for f in form.fields.values()}
return configuration
def _configurationSet(self, service_jid_s, namespace, path, configuration, profile):
client = self.host.getClient(profile)
service = jid.JID(service_jid_s)
return defer.ensureDeferred(self.configurationSet(
client, service, namespace or None, path, configuration))
async def configurationSet(
self,
client: SatXMPPEntity,
service: jid.JID,
namespace: Optional[str],
path: str,
configuration: Dict[jid.JID, str],
):
if not path:
raise ValueError(f"invalid path: {path!r}")
iq_elt = client.IQ("set")
iq_elt['to'] = service.full()
configuration_elt = iq_elt.addElement((NS_FIS_CONFIGURATION, "configuration"))
if namespace:
configuration_elt["namespace"] = namespace
configuration_elt["path"] = path
form = data_form.Form(formType="submit", formNamespace=NS_FIS_CONFIGURATION)
form.makeFields(configuration)
configuration_elt.addChild(form.toElement())
await iq_elt.send()
def _onComponentConfigurationGet(self, iq_elt, client):
iq_elt.handled = True
defer.ensureDeferred(self.onComponentConfigurationGet(client, iq_elt))
async def onComponentConfigurationGet(self, client, iq_elt):
try:
(
from_jid, configuration_elt, path, namespace, file_data
) = await self._parseElement(client, iq_elt, "configuration", NS_FIS_CONFIGURATION)
except exceptions.CancelError:
return
except RootPathException:
client.sendError(iq_elt, 'bad-request', "Root path can't be used")
return
try:
access_type = file_data['access'][C.ACCESS_PERM_READ]['type']
except KeyError:
access_model = 'whitelist'
else:
access_model = 'open' if access_type == C.ACCESS_TYPE_PUBLIC else 'whitelist'
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
configuration_elt = iq_result_elt.addElement((NS_FIS_CONFIGURATION, 'configuration'))
form = data_form.Form(formType="form", formNamespace=NS_FIS_CONFIGURATION)
form.makeFields({'access_model': access_model})
configuration_elt.addChild(form.toElement())
client.send(iq_result_elt)
def _onComponentConfigurationSet(self, iq_elt, client):
iq_elt.handled = True
defer.ensureDeferred(self.onComponentConfigurationSet(client, iq_elt))
async def onComponentConfigurationSet(self, client, iq_elt):
try:
(
from_jid, configuration_elt, path, namespace, file_data
) = await self._parseElement(client, iq_elt, "configuration", NS_FIS_CONFIGURATION)
except exceptions.CancelError:
return
except RootPathException:
client.sendError(iq_elt, 'bad-request', "Root path can't be used")
return
from_jid_bare = from_jid.userhostJID()
is_owner = from_jid_bare == file_data.get('owner')
if not is_owner:
log.warning(
f"{from_jid} tried to modify {path} configuration while the owner is "
f"{file_data['owner']}"
)
client.sendError(iq_elt, 'forbidden')
return
form = data_form.findForm(configuration_elt, NS_FIS_CONFIGURATION)
for name, value in form.items():
if name == 'access_model':
await self.host.memory.setFileAccessModel(client, file_data, value)
else:
# TODO: send a IQ error?
log.warning(
f"Trying to set a not implemented configuration option: {name}")
iq_result_elt = xmlstream.toResponse(iq_elt, "result")
client.send(iq_result_elt)
# file methods #
def _serializeData(self, files_data):