merge bookmark @
This commit is contained in:
commit
14d00a9dda
28 changed files with 327 additions and 201 deletions
|
|
@ -7,6 +7,7 @@ tags
|
||||||
twistd.log
|
twistd.log
|
||||||
twistd.pid
|
twistd.pid
|
||||||
bridge_constructor/generated
|
bridge_constructor/generated
|
||||||
|
tests/e2e/report*
|
||||||
_trial_temp/
|
_trial_temp/
|
||||||
sat.egg-info
|
sat.egg-info
|
||||||
*.un~
|
*.un~
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ v 0.8.0 « La Cecília » (NOT RELEASED YET):
|
||||||
- bulma theme becomes the default one for default site
|
- bulma theme becomes the default one for default site
|
||||||
- "tickets" have been renamed to more generic term "lists"
|
- "tickets" have been renamed to more generic term "lists"
|
||||||
- (lists) common lists can be created from templates
|
- (lists) common lists can be created from templates
|
||||||
- (photos) album can now be created or deleted from Libervia
|
- (photos) album can now be created or deleted from Libervia Web
|
||||||
- (photos/album) photos or videos can now be uploaded from Libervia
|
- (photos/album) photos or videos can now be uploaded from Libervia
|
||||||
- (photos/album) a mobile friendly slideshow can be run
|
- (photos/album) a mobile friendly slideshow can be run
|
||||||
- (photos/album) affiliations can be modified with new invitations manager
|
- (photos/album) affiliations can be modified with new invitations manager
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ USER libervia
|
||||||
|
|
||||||
RUN ./entrypoint.sh \
|
RUN ./entrypoint.sh \
|
||||||
# we create the file sharing component which will autoconnect when backend is started
|
# we create the file sharing component which will autoconnect when backend is started
|
||||||
jp profile create file_sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file_sharing -A && \
|
jp profile create file-sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file-sharing -A && \
|
||||||
libervia-backend stop
|
libervia-backend stop
|
||||||
|
|
||||||
RUN ./entrypoint.sh \
|
RUN ./entrypoint.sh \
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ email_server = libervia-backend.test
|
||||||
email_port = 8025
|
email_port = 8025
|
||||||
email_admins_list = admin@server1.test
|
email_admins_list = admin@server1.test
|
||||||
|
|
||||||
[component file_sharing]
|
[component file-sharing]
|
||||||
http_upload_public_facing_url = http://localhost:7777
|
http_upload_public_facing_url = http://localhost:7777
|
||||||
http_upload_connection_type = http
|
http_upload_connection_type = http
|
||||||
http_upload_port = 7777
|
http_upload_port = 7777
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
ARG REVISION=dev
|
ARG REVISION
|
||||||
FROM libervia/backend:${REVISION}
|
FROM libervia/backend:${REVISION:-dev}
|
||||||
|
|
||||||
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
||||||
|
|
||||||
|
|
@ -58,7 +58,7 @@ RUN \
|
||||||
|
|
||||||
RUN ./entrypoint.sh \
|
RUN ./entrypoint.sh \
|
||||||
# we create the file sharing component which will autoconnect when backend is started
|
# we create the file sharing component which will autoconnect when backend is started
|
||||||
li profile create file_sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file_sharing -A && \
|
li profile create file-sharing -j files.server1.test -p "" --xmpp-password test_e2e -C file-sharing -A && \
|
||||||
libervia-backend stop
|
libervia-backend stop
|
||||||
|
|
||||||
ENV LIBERVIA_TEST_REPORT_DIR=/reports
|
ENV LIBERVIA_TEST_REPORT_DIR=/reports
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ email_server = libervia-backend.test
|
||||||
email_port = 8025
|
email_port = 8025
|
||||||
email_admins_list = admin@server1.test
|
email_admins_list = admin@server1.test
|
||||||
|
|
||||||
[component file_sharing]
|
[component file-sharing]
|
||||||
tls_certificate = /usr/share/libervia/certificates/server1.test.pem
|
tls_certificate = /usr/share/libervia/certificates/server1.test.pem
|
||||||
tls_private_key = /usr/share/libervia/certificates/server1.test-key.pem
|
tls_private_key = /usr/share/libervia/certificates/server1.test-key.pem
|
||||||
http_upload_public_facing_url = https://libervia-backend.test:8888
|
http_upload_public_facing_url = https://libervia-backend.test:8888
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,12 @@ RUN python3 -m venv libervia_env && libervia_env/bin/pip install -U pip wheel &&
|
||||||
# local repos without it them cause troubles
|
# local repos without it them cause troubles
|
||||||
hg clone https://repos.goffi.org/urwid-satext && ~/libervia_env/bin/pip install -e urwid-satext && \
|
hg clone https://repos.goffi.org/urwid-satext && ~/libervia_env/bin/pip install -e urwid-satext && \
|
||||||
mv urwid-satext/urwid_satext.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
mv urwid-satext/urwid_satext.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
||||||
hg clone https://repos.goffi.org/sat_tmp -u "${REVISION:-tip}" && ~/libervia_env/bin/pip install -e sat_tmp && \
|
hg clone https://repos.goffi.org/sat_tmp -u "${REVISION:-@}" && ~/libervia_env/bin/pip install -e sat_tmp && \
|
||||||
mv sat_tmp/sat_tmp.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
mv sat_tmp/sat_tmp.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
||||||
hg clone https://repos.goffi.org/libervia-templates && ~/libervia_env/bin/pip install -e libervia-templates && \
|
hg clone https://repos.goffi.org/libervia-templates && ~/libervia_env/bin/pip install -e libervia-templates && \
|
||||||
mv libervia-templates/libervia_templates.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
mv libervia-templates/libervia_templates.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
||||||
hg clone https://repos.goffi.org/libervia-media && \
|
hg clone https://repos.goffi.org/libervia-media && \
|
||||||
hg clone https://repos.goffi.org/libervia-backend -u "${REVISION:-tip}" && ~/libervia_env/bin/pip install -e 'libervia-backend[SVG]' && \
|
hg clone https://repos.goffi.org/libervia-backend -u "${REVISION:-@}" && ~/libervia_env/bin/pip install -e 'libervia-backend[SVG]' && \
|
||||||
mv libervia-backend/libervia_backend.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
mv libervia-backend/libervia_backend.egg-info ~/libervia_env/lib/python3.*/site-packages && \
|
||||||
mkdir -p /home/libervia/.local/share/libervia
|
mkdir -p /home/libervia/.local/share/libervia
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
# we need to depend on backend to get IP address of the container for conf
|
# we need to depend on backend to get IP address of the container for conf
|
||||||
- backend
|
- backend
|
||||||
tmpfs: /var/lib/prosody
|
|
||||||
tmpfs: /var/log/prosody
|
tmpfs: /var/log/prosody
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
ARG REVISION=dev
|
ARG REVISION
|
||||||
FROM libervia/web:${REVISION}
|
FROM libervia/web:${REVISION:-dev}
|
||||||
|
|
||||||
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
ARG REVISION=dev
|
ARG REVISION
|
||||||
FROM libervia/web:${REVISION}
|
FROM libervia/web:${REVISION:-dev}
|
||||||
|
|
||||||
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
LABEL maintainer="Goffi <tmp_dockerfiles@goffi.org>"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ USER root
|
||||||
RUN apt-get install -y --no-install-recommends yarnpkg
|
RUN apt-get install -y --no-install-recommends yarnpkg
|
||||||
WORKDIR /home/libervia
|
WORKDIR /home/libervia
|
||||||
USER libervia
|
USER libervia
|
||||||
RUN cd /src && hg clone https://repos.goffi.org/libervia-web -u "${REVISION:-tip}" && \
|
RUN cd /src && hg clone https://repos.goffi.org/libervia-web -u "${REVISION:-@}" && \
|
||||||
~/libervia_env/bin/pip install -e libervia-web && \
|
~/libervia_env/bin/pip install -e libervia-web && \
|
||||||
mv libervia-web/libervia_web.egg-info ~/libervia_env/lib/python3.*/site-packages
|
mv libervia-web/libervia_web.egg-info ~/libervia_env/lib/python3.*/site-packages
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,3 @@ COPY --chown=root:prosody prosody.cfg.lua /etc/prosody/prosody.cfg.lua
|
||||||
COPY --chown=root:tls-cert certificates/server1.test/cert.pem /usr/share/libervia/certificates/server1.test.pem
|
COPY --chown=root:tls-cert certificates/server1.test/cert.pem /usr/share/libervia/certificates/server1.test.pem
|
||||||
COPY --chown=root:tls-cert certificates/server1.test/key.pem /usr/share/libervia/certificates/server1.test-key.pem
|
COPY --chown=root:tls-cert certificates/server1.test/key.pem /usr/share/libervia/certificates/server1.test-key.pem
|
||||||
|
|
||||||
# we add exec to handle properly signals, this is missing upstream
|
|
||||||
# FIXME: to be removed when new images are generated with
|
|
||||||
# https://github.com/prosody/prosody-docker/pull/65
|
|
||||||
RUN sed -i "s/^runuser -u prosody/exec \0/" /entrypoint.sh
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
usermod -u "$(stat -c %u /var/lib/prosody/.)" prosody
|
|
||||||
|
|
||||||
if [[ "$1" != "prosody" ]]; then
|
|
||||||
exec prosodyctl "$@"
|
|
||||||
exit 0;
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$LOCAL" -a "$PASSWORD" -a "$DOMAIN" ] ; then
|
|
||||||
prosodyctl register "$LOCAL" "$DOMAIN" "$PASSWORD"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec runuser -u prosody -- "$@"
|
|
||||||
|
|
@ -6,8 +6,8 @@ services:
|
||||||
depends_on:
|
depends_on:
|
||||||
# we need to depend on backend to get IP address of the container for conf
|
# we need to depend on backend to get IP address of the container for conf
|
||||||
- backend
|
- backend
|
||||||
tmpfs: /var/lib/prosody
|
tmpfs:
|
||||||
tmpfs: /var/log/prosody
|
- /var/log/prosody
|
||||||
networks:
|
networks:
|
||||||
default:
|
default:
|
||||||
aliases:
|
aliases:
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,18 @@
|
||||||
attrs==21.2.0
|
attrs==21.2.0
|
||||||
Automat==20.2.0
|
Automat==20.2.0
|
||||||
Babel==2.9.1
|
Babel==2.9.1
|
||||||
cairocffi==1.2.0
|
certifi==2021.10.8
|
||||||
CairoSVG==2.5.2
|
cffi==1.15.0
|
||||||
certifi==2021.5.30
|
charset-normalizer==2.0.7
|
||||||
cffi==1.14.5
|
|
||||||
chardet==4.0.0
|
|
||||||
constantly==15.1.0
|
constantly==15.1.0
|
||||||
cryptography==3.4.7
|
cryptography==3.4.8
|
||||||
cssselect2==0.4.1
|
dbus-python==1.2.18
|
||||||
dbus-python==1.2.16
|
|
||||||
defusedxml==0.7.1
|
|
||||||
DoubleRatchet==0.7.0
|
DoubleRatchet==0.7.0
|
||||||
html2text==2020.1.16
|
html2text==2020.1.16
|
||||||
hyperlink==21.0.0
|
hyperlink==21.0.0
|
||||||
idna==2.10
|
idna==3.3
|
||||||
incremental==21.3.0
|
incremental==21.3.0
|
||||||
Jinja2==3.0.1
|
Jinja2==3.0.2
|
||||||
langid==1.1.6
|
langid==1.1.6
|
||||||
lxml==4.6.3
|
lxml==4.6.3
|
||||||
Markdown==3.3.4
|
Markdown==3.3.4
|
||||||
|
|
@ -24,38 +20,36 @@ MarkupSafe==2.0.1
|
||||||
miniupnpc==2.0.2
|
miniupnpc==2.0.2
|
||||||
mutagen==1.45.1
|
mutagen==1.45.1
|
||||||
netifaces==0.11.0
|
netifaces==0.11.0
|
||||||
numpy==1.20.3
|
numpy==1.21.3
|
||||||
OMEMO==0.12.0
|
OMEMO==0.12.0
|
||||||
omemo-backend-signal==0.2.6
|
omemo-backend-signal==0.2.6
|
||||||
Pillow==8.2.0
|
Pillow==8.4.0
|
||||||
progressbar2==3.53.1
|
progressbar2==3.53.3
|
||||||
protobuf==3.17.3
|
protobuf==3.19.1
|
||||||
pyasn1==0.4.8
|
pyasn1==0.4.8
|
||||||
pyasn1-modules==0.2.8
|
pyasn1-modules==0.2.8
|
||||||
pycairo==1.20.1
|
pycairo==1.20.1
|
||||||
pycparser==2.20
|
pycparser==2.20
|
||||||
pycrypto==2.6.1
|
pycrypto==2.6.1
|
||||||
Pygments==2.9.0
|
Pygments==2.10.0
|
||||||
PyGObject==3.40.1
|
PyGObject==3.40.0
|
||||||
PyNaCl==1.4.0
|
PyNaCl==1.4.0
|
||||||
pyOpenSSL==20.0.1
|
pyOpenSSL==20.0.1
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.2
|
||||||
python-potr==1.0.2
|
python-potr==1.0.2
|
||||||
python-utils==2.5.6
|
python-utils==2.5.6
|
||||||
pytz==2021.1
|
pytz==2021.3
|
||||||
pyxdg==0.27
|
pyxdg==0.27
|
||||||
PyYAML==5.4.1
|
PyYAML==5.4.1
|
||||||
requests==2.25.1
|
requests==2.26.0
|
||||||
service-identity==21.1.0
|
service-identity==21.1.0
|
||||||
setuptools-scm==6.0.1
|
|
||||||
shortuuid==1.0.1
|
shortuuid==1.0.1
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
tinycss2==1.1.0
|
|
||||||
treq==21.5.0
|
treq==21.5.0
|
||||||
Twisted==21.2.0
|
Twisted==21.2.0
|
||||||
urllib3==1.26.5
|
typing-extensions==3.10.0.2
|
||||||
|
urllib3==1.26.7
|
||||||
urwid==2.1.2
|
urwid==2.1.2
|
||||||
webencodings==0.5.1
|
|
||||||
wokkel==18.0.0
|
wokkel==18.0.0
|
||||||
X3DH==0.5.9
|
X3DH==0.5.9
|
||||||
XEdDSA==0.4.7
|
XEdDSA==0.4.7
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,11 @@ class SatXMPPEntity(core_types.SatXMPPEntity):
|
||||||
self.streamInitialized()
|
self.streamInitialized()
|
||||||
|
|
||||||
def _finish_connection(self, __):
|
def _finish_connection(self, __):
|
||||||
self.conn_deferred.callback(None)
|
if self.conn_deferred.called:
|
||||||
|
# can happen in case of forced disconnection by server
|
||||||
|
log.debug(f"{self} has already been connected")
|
||||||
|
else:
|
||||||
|
self.conn_deferred.callback(None)
|
||||||
|
|
||||||
def streamInitialized(self):
|
def streamInitialized(self):
|
||||||
"""Called after _authd"""
|
"""Called after _authd"""
|
||||||
|
|
|
||||||
|
|
@ -545,7 +545,7 @@ class FileSharing:
|
||||||
self._f.openFileWrite(
|
self._f.openFileWrite(
|
||||||
client, file_tmp_path, transfer_data, file_data, stream_object
|
client, file_tmp_path, transfer_data, file_data, stream_object
|
||||||
)
|
)
|
||||||
return False, defer.succeed(True)
|
return False, True
|
||||||
|
|
||||||
async def _retrieveFiles(
|
async def _retrieveFiles(
|
||||||
self, client, session, content_data, content_name, file_data, file_elt
|
self, client, session, content_data, content_name, file_data, file_elt
|
||||||
|
|
|
||||||
|
|
@ -428,6 +428,7 @@ class Events(object):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
timestamp, data = self._parseEventElt(event_elt)
|
timestamp, data = self._parseEventElt(event_elt)
|
||||||
|
data["interest_id"] = item["id"]
|
||||||
events.append((timestamp, data))
|
events.append((timestamp, data))
|
||||||
defer.returnValue(events)
|
defer.returnValue(events)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import shortuuid
|
import shortuuid
|
||||||
|
from typing import Optional
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from twisted.words.protocols.jabber import jid
|
from twisted.words.protocols.jabber import jid
|
||||||
from twisted.words.protocols.jabber import error
|
from twisted.words.protocols.jabber import error
|
||||||
|
|
@ -131,6 +132,97 @@ class InvitationsPlugin(object):
|
||||||
kwargs[key] = str(value)
|
kwargs[key] = str(value)
|
||||||
return defer.ensureDeferred(self.create(**kwargs))
|
return defer.ensureDeferred(self.create(**kwargs))
|
||||||
|
|
||||||
|
async def getExistingInvitation(self, email: Optional[str]) -> Optional[dict]:
|
||||||
|
"""Retrieve existing invitation with given email
|
||||||
|
|
||||||
|
@param email: check if any invitation exist with this email
|
||||||
|
@return: first found invitation, or None if nothing found
|
||||||
|
"""
|
||||||
|
# FIXME: This method is highly inefficient, it get all invitations and check them
|
||||||
|
# one by one, this is just a temporary way to avoid creating creating new accounts
|
||||||
|
# for an existing email. A better way will be available with Libervia 0.9.
|
||||||
|
# TODO: use a better way to check existing invitations
|
||||||
|
|
||||||
|
if email is None:
|
||||||
|
return None
|
||||||
|
all_invitations = await self.invitations.all()
|
||||||
|
for id_, invitation in all_invitations.items():
|
||||||
|
if invitation.get("email") == email:
|
||||||
|
invitation[KEY_ID] = id_
|
||||||
|
return invitation
|
||||||
|
|
||||||
|
async def _createAccountAndProfile(
|
||||||
|
self,
|
||||||
|
id_: str,
|
||||||
|
kwargs: dict,
|
||||||
|
extra: dict
|
||||||
|
) -> None:
|
||||||
|
"""Create XMPP account and Libervia profile for guest"""
|
||||||
|
## XMPP account creation
|
||||||
|
password = kwargs.pop('password', None)
|
||||||
|
if password is None:
|
||||||
|
password = utils.generatePassword()
|
||||||
|
assert password
|
||||||
|
# XXX: password is here saved in clear in database
|
||||||
|
# it is needed for invitation as the same password is used for profile
|
||||||
|
# and SàT need to be able to automatically open the profile with the uuid
|
||||||
|
# FIXME: we could add an extra encryption key which would be used with the
|
||||||
|
# uuid when the invitee is connecting (e.g. with URL). This key would
|
||||||
|
# not be saved and could be used to encrypt profile password.
|
||||||
|
extra[KEY_PASSWORD] = password
|
||||||
|
|
||||||
|
jid_ = kwargs.pop('jid_', None)
|
||||||
|
if not jid_:
|
||||||
|
domain = self.host.memory.getConfig(None, 'xmpp_domain')
|
||||||
|
if not domain:
|
||||||
|
# TODO: fallback to profile's domain
|
||||||
|
raise ValueError(_("You need to specify xmpp_domain in sat.conf"))
|
||||||
|
jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(),
|
||||||
|
domain=domain)
|
||||||
|
jid_ = jid.JID(jid_)
|
||||||
|
extra[KEY_JID] = jid_.full()
|
||||||
|
|
||||||
|
if jid_.user:
|
||||||
|
# we don't register account if there is no user as anonymous login is then
|
||||||
|
# used
|
||||||
|
try:
|
||||||
|
await self.host.plugins['XEP-0077'].registerNewAccount(jid_, password)
|
||||||
|
except error.StanzaError as e:
|
||||||
|
prefix = jid_.user
|
||||||
|
idx = 0
|
||||||
|
while e.condition == 'conflict':
|
||||||
|
if idx >= SUFFIX_MAX:
|
||||||
|
raise exceptions.ConflictError(_("Can't create XMPP account"))
|
||||||
|
jid_.user = prefix + '_' + str(idx)
|
||||||
|
log.info(_("requested jid already exists, trying with {}".format(
|
||||||
|
jid_.full())))
|
||||||
|
try:
|
||||||
|
await self.host.plugins['XEP-0077'].registerNewAccount(
|
||||||
|
jid_,
|
||||||
|
password
|
||||||
|
)
|
||||||
|
except error.StanzaError:
|
||||||
|
idx += 1
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if e.condition != 'conflict':
|
||||||
|
raise e
|
||||||
|
|
||||||
|
log.info(_("account {jid_} created").format(jid_=jid_.full()))
|
||||||
|
|
||||||
|
## profile creation
|
||||||
|
|
||||||
|
extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format(
|
||||||
|
uuid=id_
|
||||||
|
)
|
||||||
|
# profile creation should not fail as we generate unique name ourselves
|
||||||
|
await self.host.memory.createProfile(guest_profile, password)
|
||||||
|
await self.host.memory.startSession(password, guest_profile)
|
||||||
|
await self.host.memory.setParam("JabberID", jid_.full(), "Connection",
|
||||||
|
profile_key=guest_profile)
|
||||||
|
await self.host.memory.setParam("Password", password, "Connection",
|
||||||
|
profile_key=guest_profile)
|
||||||
|
|
||||||
async def create(self, **kwargs):
|
async def create(self, **kwargs):
|
||||||
r"""Create an invitation
|
r"""Create an invitation
|
||||||
|
|
||||||
|
|
@ -201,6 +293,13 @@ class InvitationsPlugin(object):
|
||||||
self.checkExtra(extra)
|
self.checkExtra(extra)
|
||||||
|
|
||||||
email = kwargs.pop('email', None)
|
email = kwargs.pop('email', None)
|
||||||
|
|
||||||
|
existing = await self.getExistingInvitation(email)
|
||||||
|
if existing is not None:
|
||||||
|
log.info(f"There is already an invitation for {email!r}")
|
||||||
|
extra.update(existing)
|
||||||
|
del extra[KEY_ID]
|
||||||
|
|
||||||
emails_extra = kwargs.pop('emails_extra', [])
|
emails_extra = kwargs.pop('emails_extra', [])
|
||||||
if not email and emails_extra:
|
if not email and emails_extra:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
|
@ -214,67 +313,18 @@ class InvitationsPlugin(object):
|
||||||
|
|
||||||
## uuid
|
## uuid
|
||||||
log.info(_("creating an invitation"))
|
log.info(_("creating an invitation"))
|
||||||
id_ = str(shortuuid.uuid())
|
id_ = existing[KEY_ID] if existing else str(shortuuid.uuid())
|
||||||
|
|
||||||
## XMPP account creation
|
if existing is None:
|
||||||
password = kwargs.pop('password', None)
|
await self._createAccountAndProfile(id_, kwargs, extra)
|
||||||
if password is None:
|
|
||||||
password = utils.generatePassword()
|
|
||||||
assert password
|
|
||||||
# XXX: password is here saved in clear in database
|
|
||||||
# it is needed for invitation as the same password is used for profile
|
|
||||||
# and SàT need to be able to automatically open the profile with the uuid
|
|
||||||
# FIXME: we could add an extra encryption key which would be used with the uuid
|
|
||||||
# when the invitee is connecting (e.g. with URL). This key would not be
|
|
||||||
# saved and could be used to encrypt profile password.
|
|
||||||
extra[KEY_PASSWORD] = password
|
|
||||||
|
|
||||||
jid_ = kwargs.pop('jid_', None)
|
profile = kwargs.pop('profile', None)
|
||||||
if not jid_:
|
guest_profile = extra[KEY_GUEST_PROFILE]
|
||||||
domain = self.host.memory.getConfig(None, 'xmpp_domain')
|
jid_ = jid.JID(extra[KEY_JID])
|
||||||
if not domain:
|
|
||||||
# TODO: fallback to profile's domain
|
|
||||||
raise ValueError(_("You need to specify xmpp_domain in sat.conf"))
|
|
||||||
jid_ = "invitation-{uuid}@{domain}".format(uuid=shortuuid.uuid(),
|
|
||||||
domain=domain)
|
|
||||||
jid_ = jid.JID(jid_)
|
|
||||||
if jid_.user:
|
|
||||||
# we don't register account if there is no user as anonymous login is then
|
|
||||||
# used
|
|
||||||
try:
|
|
||||||
await self.host.plugins['XEP-0077'].registerNewAccount(jid_, password)
|
|
||||||
except error.StanzaError as e:
|
|
||||||
prefix = jid_.user
|
|
||||||
idx = 0
|
|
||||||
while e.condition == 'conflict':
|
|
||||||
if idx >= SUFFIX_MAX:
|
|
||||||
raise exceptions.ConflictError(_("Can't create XMPP account"))
|
|
||||||
jid_.user = prefix + '_' + str(idx)
|
|
||||||
log.info(_("requested jid already exists, trying with {}".format(
|
|
||||||
jid_.full())))
|
|
||||||
try:
|
|
||||||
await self.host.plugins['XEP-0077'].registerNewAccount(jid_,
|
|
||||||
password)
|
|
||||||
except error.StanzaError:
|
|
||||||
idx += 1
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
if e.condition != 'conflict':
|
|
||||||
raise e
|
|
||||||
|
|
||||||
log.info(_("account {jid_} created").format(jid_=jid_.full()))
|
## identity
|
||||||
|
|
||||||
## profile creation
|
|
||||||
|
|
||||||
extra[KEY_GUEST_PROFILE] = guest_profile = INVITEE_PROFILE_TPL.format(uuid=id_)
|
|
||||||
# profile creation should not fail as we generate unique name ourselves
|
|
||||||
await self.host.memory.createProfile(guest_profile, password)
|
|
||||||
await self.host.memory.startSession(password, guest_profile)
|
|
||||||
await self.host.memory.setParam("JabberID", jid_.full(), "Connection",
|
|
||||||
profile_key=guest_profile)
|
|
||||||
await self.host.memory.setParam("Password", password, "Connection",
|
|
||||||
profile_key=guest_profile)
|
|
||||||
name = kwargs.pop('name', None)
|
name = kwargs.pop('name', None)
|
||||||
|
password = extra[KEY_PASSWORD]
|
||||||
if name is not None:
|
if name is not None:
|
||||||
extra['name'] = name
|
extra['name'] = name
|
||||||
try:
|
try:
|
||||||
|
|
@ -306,7 +356,6 @@ class InvitationsPlugin(object):
|
||||||
else:
|
else:
|
||||||
format_args['name'] = name
|
format_args['name'] = name
|
||||||
|
|
||||||
profile = kwargs.pop('profile', None)
|
|
||||||
if profile is None:
|
if profile is None:
|
||||||
format_args['profile'] = ''
|
format_args['profile'] = ''
|
||||||
else:
|
else:
|
||||||
|
|
@ -344,8 +393,6 @@ class InvitationsPlugin(object):
|
||||||
if kwargs:
|
if kwargs:
|
||||||
log.warning(_("Not all arguments have been consumed: {}").format(kwargs))
|
log.warning(_("Not all arguments have been consumed: {}").format(kwargs))
|
||||||
|
|
||||||
extra[KEY_JID] = jid_.full()
|
|
||||||
|
|
||||||
## extra data saving
|
## extra data saving
|
||||||
self.invitations[id_] = extra
|
self.invitations[id_] = extra
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,16 +17,18 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import re
|
|
||||||
from html import escape
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from sat.core.i18n import _, D_
|
from html import escape
|
||||||
from sat.core.constants import Const as C
|
import re
|
||||||
from sat.core.log import getLogger
|
from typing import Set
|
||||||
|
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from twisted.internet.threads import deferToThread
|
from twisted.internet.threads import deferToThread
|
||||||
|
|
||||||
from sat.core import exceptions
|
from sat.core import exceptions
|
||||||
|
from sat.core.constants import Const as C
|
||||||
|
from sat.core.i18n import D_, _
|
||||||
|
from sat.core.log import getLogger
|
||||||
from sat.tools import xml_tools
|
from sat.tools import xml_tools
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -115,7 +117,15 @@ VOID_ELEMENTS = (
|
||||||
"track",
|
"track",
|
||||||
"wbr")
|
"wbr")
|
||||||
|
|
||||||
SAFE_ATTRS = html.defs.safe_attrs.union(("style", "poster", "controls"))
|
SAFE_ATTRS = html.defs.safe_attrs.union({"style", "poster", "controls"}) - {"id"}
|
||||||
|
SAFE_CLASSES = {
|
||||||
|
# those classes are used for code highlighting
|
||||||
|
"bp", "c", "ch", "cm", "cp", "cpf", "cs", "dl", "err", "fm", "gd", "ge", "get", "gh",
|
||||||
|
"gi", "go", "gp", "gr", "gs", "gt", "gu", "highlight", "hll", "il", "k", "kc", "kd",
|
||||||
|
"kn", "kp", "kr", "kt", "m", "mb", "mf", "mh", "mi", "mo", "na", "nb", "nc", "nd",
|
||||||
|
"ne", "nf", "ni", "nl", "nn", "no", "nt", "nv", "o", "ow", "s", "sa", "sb", "sc",
|
||||||
|
"sd", "se", "sh", "si", "sr", "ss", "sx", "vc", "vg", "vi", "vm", "w", "write",
|
||||||
|
}
|
||||||
STYLES_VALUES_REGEX = (
|
STYLES_VALUES_REGEX = (
|
||||||
r"^("
|
r"^("
|
||||||
+ "|".join(
|
+ "|".join(
|
||||||
|
|
@ -237,7 +247,8 @@ class TextSyntaxes(object):
|
||||||
except ImportError:
|
except ImportError:
|
||||||
log.warning("markdown or html2text not found, can't use Markdown syntax")
|
log.warning("markdown or html2text not found, can't use Markdown syntax")
|
||||||
log.info(
|
log.info(
|
||||||
"You can download/install them from https://pythonhosted.org/Markdown/ and https://github.com/Alir3z4/html2text/"
|
"You can download/install them from https://pythonhosted.org/Markdown/ "
|
||||||
|
"and https://github.com/Alir3z4/html2text/"
|
||||||
)
|
)
|
||||||
host.bridge.addMethod(
|
host.bridge.addMethod(
|
||||||
"syntaxConvert",
|
"syntaxConvert",
|
||||||
|
|
@ -288,14 +299,14 @@ class TextSyntaxes(object):
|
||||||
)
|
)
|
||||||
return failure
|
return failure
|
||||||
|
|
||||||
def cleanStyle(self, styles):
|
def cleanStyle(self, styles_raw: str) -> str:
|
||||||
""""Clean unsafe CSS styles
|
""""Clean unsafe CSS styles
|
||||||
|
|
||||||
Remove styles not in the whitelist, or where the value doesn't match the regex
|
Remove styles not in the whitelist, or where the value doesn't match the regex
|
||||||
@param styles_raw(unicode): CSS styles
|
@param styles_raw: CSS styles
|
||||||
@return (unicode): cleaned styles
|
@return: cleaned styles
|
||||||
"""
|
"""
|
||||||
styles = styles.split(";")
|
styles: List[str] = styles_raw.split(";")
|
||||||
cleaned_styles = []
|
cleaned_styles = []
|
||||||
for style in styles:
|
for style in styles:
|
||||||
try:
|
try:
|
||||||
|
|
@ -315,6 +326,14 @@ class TextSyntaxes(object):
|
||||||
["%s: %s" % (key_, value_) for key_, value_ in cleaned_styles]
|
["%s: %s" % (key_, value_) for key_, value_ in cleaned_styles]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def cleanClasses(self, classes_raw: str) -> str:
|
||||||
|
"""Remove any non whitelisted class
|
||||||
|
|
||||||
|
@param classes_raw: classes set on an element
|
||||||
|
@return: remaining classes (can be empty string)
|
||||||
|
"""
|
||||||
|
return " ".join(SAFE_CLASSES.intersection(classes_raw.split()))
|
||||||
|
|
||||||
def cleanXHTML(self, xhtml):
|
def cleanXHTML(self, xhtml):
|
||||||
"""Clean XHTML text by removing potentially dangerous/malicious parts
|
"""Clean XHTML text by removing potentially dangerous/malicious parts
|
||||||
|
|
||||||
|
|
@ -341,6 +360,8 @@ class TextSyntaxes(object):
|
||||||
xhtml_elt = cleaner.clean_html(xhtml_elt)
|
xhtml_elt = cleaner.clean_html(xhtml_elt)
|
||||||
for elt in xhtml_elt.xpath("//*[@style]"):
|
for elt in xhtml_elt.xpath("//*[@style]"):
|
||||||
elt.set("style", self.cleanStyle(elt.get("style")))
|
elt.set("style", self.cleanStyle(elt.get("style")))
|
||||||
|
for elt in xhtml_elt.xpath("//*[@class]"):
|
||||||
|
elt.set("class", self.cleanClasses(elt.get("class")))
|
||||||
# we remove self-closing elements for non-void elements
|
# we remove self-closing elements for non-void elements
|
||||||
for element in xhtml_elt.iter(tag=etree.Element):
|
for element in xhtml_elt.iter(tag=etree.Element):
|
||||||
if not element.text:
|
if not element.text:
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue