mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2024-10-01 11:49:51 -04:00
Merge pull request #3236 from matrix-org/rav/consent_notice
Send users a server notice about consent
This commit is contained in:
commit
8aeb529262
@ -18,25 +18,49 @@ from ._base import Config
|
|||||||
DEFAULT_CONFIG = """\
|
DEFAULT_CONFIG = """\
|
||||||
# User Consent configuration
|
# User Consent configuration
|
||||||
#
|
#
|
||||||
# uncomment and configure if enabling the 'consent' resource under 'listeners'.
|
# Parts of this section are required if enabling the 'consent' resource under
|
||||||
|
# 'listeners', in particular 'template_dir' and 'version'.
|
||||||
#
|
#
|
||||||
# 'template_dir' gives the location of the templates for the HTML forms.
|
# 'template_dir' gives the location of the templates for the HTML forms.
|
||||||
# This directory should contain one subdirectory per language (eg, 'en', 'fr'),
|
# This directory should contain one subdirectory per language (eg, 'en', 'fr'),
|
||||||
# and each language directory should contain the policy document (named as
|
# and each language directory should contain the policy document (named as
|
||||||
# '<version>.html') and a success page (success.html).
|
# '<version>.html') and a success page (success.html).
|
||||||
#
|
#
|
||||||
# 'default_version' gives the version of the policy document to serve up if
|
# 'version' specifies the 'current' version of the policy document. It defines
|
||||||
# there is no 'v' parameter.
|
# the version to be served by the consent resource if there is no 'v'
|
||||||
|
# parameter.
|
||||||
|
#
|
||||||
|
# 'server_notice_content', if enabled, will send a user a "Server Notice"
|
||||||
|
# asking them to consent to the privacy policy. The 'server_notices' section
|
||||||
|
# must also be configured for this to work.
|
||||||
#
|
#
|
||||||
# user_consent:
|
# user_consent:
|
||||||
# template_dir: res/templates/privacy
|
# template_dir: res/templates/privacy
|
||||||
# default_version: 1.0
|
# version: 1.0
|
||||||
|
# server_notice_content:
|
||||||
|
# msgtype: m.text
|
||||||
|
# body: |
|
||||||
|
# Pls do consent kthx
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class ConsentConfig(Config):
|
class ConsentConfig(Config):
|
||||||
|
def __init__(self):
|
||||||
|
super(ConsentConfig, self).__init__()
|
||||||
|
|
||||||
|
self.user_consent_version = None
|
||||||
|
self.user_consent_template_dir = None
|
||||||
|
self.user_consent_server_notice_content = None
|
||||||
|
|
||||||
def read_config(self, config):
|
def read_config(self, config):
|
||||||
self.consent_config = config.get("user_consent")
|
consent_config = config.get("user_consent")
|
||||||
|
if consent_config is None:
|
||||||
|
return
|
||||||
|
self.user_consent_version = str(consent_config["version"])
|
||||||
|
self.user_consent_template_dir = consent_config["template_dir"]
|
||||||
|
self.user_consent_server_notice_content = consent_config.get(
|
||||||
|
"server_notice_content",
|
||||||
|
)
|
||||||
|
|
||||||
def default_config(self, **kwargs):
|
def default_config(self, **kwargs):
|
||||||
return DEFAULT_CONFIG
|
return DEFAULT_CONFIG
|
||||||
|
@ -48,6 +48,7 @@ class EventStreamHandler(BaseHandler):
|
|||||||
|
|
||||||
self.notifier = hs.get_notifier()
|
self.notifier = hs.get_notifier()
|
||||||
self.state = hs.get_state_handler()
|
self.state = hs.get_state_handler()
|
||||||
|
self._server_notices_sender = hs.get_server_notices_sender()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
@log_function
|
@log_function
|
||||||
@ -58,6 +59,10 @@ class EventStreamHandler(BaseHandler):
|
|||||||
|
|
||||||
If `only_keys` is not None, events from keys will be sent down.
|
If `only_keys` is not None, events from keys will be sent down.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# send any outstanding server notices to the user.
|
||||||
|
yield self._server_notices_sender.on_user_syncing(auth_user_id)
|
||||||
|
|
||||||
auth_user = UserID.from_string(auth_user_id)
|
auth_user = UserID.from_string(auth_user_id)
|
||||||
presence_handler = self.hs.get_presence_handler()
|
presence_handler = self.hs.get_presence_handler()
|
||||||
|
|
||||||
|
@ -87,6 +87,11 @@ assert LAST_ACTIVE_GRANULARITY < IDLE_TIMER
|
|||||||
class PresenceHandler(object):
|
class PresenceHandler(object):
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer):
|
||||||
|
"""
|
||||||
self.is_mine = hs.is_mine
|
self.is_mine = hs.is_mine
|
||||||
self.is_mine_id = hs.is_mine_id
|
self.is_mine_id = hs.is_mine_id
|
||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
@ -94,7 +99,6 @@ class PresenceHandler(object):
|
|||||||
self.wheel_timer = WheelTimer()
|
self.wheel_timer = WheelTimer()
|
||||||
self.notifier = hs.get_notifier()
|
self.notifier = hs.get_notifier()
|
||||||
self.federation = hs.get_federation_sender()
|
self.federation = hs.get_federation_sender()
|
||||||
|
|
||||||
self.state = hs.get_state_handler()
|
self.state = hs.get_state_handler()
|
||||||
|
|
||||||
federation_registry = hs.get_federation_registry()
|
federation_registry = hs.get_federation_registry()
|
||||||
|
@ -69,6 +69,7 @@ class ReplicationStreamer(object):
|
|||||||
self.presence_handler = hs.get_presence_handler()
|
self.presence_handler = hs.get_presence_handler()
|
||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
self.notifier = hs.get_notifier()
|
self.notifier = hs.get_notifier()
|
||||||
|
self._server_notices_sender = hs.get_server_notices_sender()
|
||||||
|
|
||||||
# Current connections.
|
# Current connections.
|
||||||
self.connections = []
|
self.connections = []
|
||||||
@ -253,6 +254,7 @@ class ReplicationStreamer(object):
|
|||||||
yield self.store.insert_client_ip(
|
yield self.store.insert_client_ip(
|
||||||
user_id, access_token, ip, user_agent, device_id, last_seen,
|
user_id, access_token, ip, user_agent, device_id, last_seen,
|
||||||
)
|
)
|
||||||
|
yield self._server_notices_sender.on_user_ip(user_id)
|
||||||
|
|
||||||
def send_sync_to_all_connections(self, data):
|
def send_sync_to_all_connections(self, data):
|
||||||
"""Sends a SYNC command to all clients.
|
"""Sends a SYNC command to all clients.
|
||||||
|
@ -85,6 +85,7 @@ class SyncRestServlet(RestServlet):
|
|||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
self.filtering = hs.get_filtering()
|
self.filtering = hs.get_filtering()
|
||||||
self.presence_handler = hs.get_presence_handler()
|
self.presence_handler = hs.get_presence_handler()
|
||||||
|
self._server_notices_sender = hs.get_server_notices_sender()
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def on_GET(self, request):
|
def on_GET(self, request):
|
||||||
@ -149,6 +150,9 @@ class SyncRestServlet(RestServlet):
|
|||||||
else:
|
else:
|
||||||
since_token = None
|
since_token = None
|
||||||
|
|
||||||
|
# send any outstanding server notices to the user.
|
||||||
|
yield self._server_notices_sender.on_user_syncing(user.to_string())
|
||||||
|
|
||||||
affect_presence = set_presence != PresenceState.OFFLINE
|
affect_presence = set_presence != PresenceState.OFFLINE
|
||||||
|
|
||||||
if affect_presence:
|
if affect_presence:
|
||||||
|
@ -95,8 +95,8 @@ class ConsentResource(Resource):
|
|||||||
# this is required by the request_handler wrapper
|
# this is required by the request_handler wrapper
|
||||||
self.clock = hs.get_clock()
|
self.clock = hs.get_clock()
|
||||||
|
|
||||||
consent_config = hs.config.consent_config
|
self._default_consent_verison = hs.config.user_consent_version
|
||||||
if consent_config is None:
|
if self._default_consent_verison is None:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"Consent resource is enabled but user_consent section is "
|
"Consent resource is enabled but user_consent section is "
|
||||||
"missing in config file.",
|
"missing in config file.",
|
||||||
@ -104,7 +104,7 @@ class ConsentResource(Resource):
|
|||||||
|
|
||||||
# daemonize changes the cwd to /, so make the path absolute now.
|
# daemonize changes the cwd to /, so make the path absolute now.
|
||||||
consent_template_directory = path.abspath(
|
consent_template_directory = path.abspath(
|
||||||
consent_config["template_dir"],
|
hs.config.user_consent_template_dir,
|
||||||
)
|
)
|
||||||
if not path.isdir(consent_template_directory):
|
if not path.isdir(consent_template_directory):
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
@ -116,8 +116,6 @@ class ConsentResource(Resource):
|
|||||||
loader = jinja2.FileSystemLoader(consent_template_directory)
|
loader = jinja2.FileSystemLoader(consent_template_directory)
|
||||||
self._jinja_env = jinja2.Environment(loader=loader)
|
self._jinja_env = jinja2.Environment(loader=loader)
|
||||||
|
|
||||||
self._default_consent_verison = consent_config["default_version"]
|
|
||||||
|
|
||||||
if hs.config.form_secret is None:
|
if hs.config.form_secret is None:
|
||||||
raise ConfigError(
|
raise ConfigError(
|
||||||
"Consent resource is enabled but form_secret is not set in "
|
"Consent resource is enabled but form_secret is not set in "
|
||||||
|
@ -73,6 +73,10 @@ from synapse.rest.media.v1.media_repository import (
|
|||||||
MediaRepositoryResource,
|
MediaRepositoryResource,
|
||||||
)
|
)
|
||||||
from synapse.server_notices.server_notices_manager import ServerNoticesManager
|
from synapse.server_notices.server_notices_manager import ServerNoticesManager
|
||||||
|
from synapse.server_notices.server_notices_sender import ServerNoticesSender
|
||||||
|
from synapse.server_notices.worker_server_notices_sender import (
|
||||||
|
WorkerServerNoticesSender,
|
||||||
|
)
|
||||||
from synapse.state import StateHandler, StateResolutionHandler
|
from synapse.state import StateHandler, StateResolutionHandler
|
||||||
from synapse.storage import DataStore
|
from synapse.storage import DataStore
|
||||||
from synapse.streams.events import EventSources
|
from synapse.streams.events import EventSources
|
||||||
@ -158,6 +162,7 @@ class HomeServer(object):
|
|||||||
'room_member_handler',
|
'room_member_handler',
|
||||||
'federation_registry',
|
'federation_registry',
|
||||||
'server_notices_manager',
|
'server_notices_manager',
|
||||||
|
'server_notices_sender',
|
||||||
]
|
]
|
||||||
|
|
||||||
def __init__(self, hostname, **kwargs):
|
def __init__(self, hostname, **kwargs):
|
||||||
@ -401,8 +406,15 @@ class HomeServer(object):
|
|||||||
return FederationHandlerRegistry()
|
return FederationHandlerRegistry()
|
||||||
|
|
||||||
def build_server_notices_manager(self):
|
def build_server_notices_manager(self):
|
||||||
|
if self.config.worker_app:
|
||||||
|
raise Exception("Workers cannot send server notices")
|
||||||
return ServerNoticesManager(self)
|
return ServerNoticesManager(self)
|
||||||
|
|
||||||
|
def build_server_notices_sender(self):
|
||||||
|
if self.config.worker_app:
|
||||||
|
return WorkerServerNoticesSender(self)
|
||||||
|
return ServerNoticesSender(self)
|
||||||
|
|
||||||
def remove_pusher(self, app_id, push_key, user_id):
|
def remove_pusher(self, app_id, push_key, user_id):
|
||||||
return self.get_pusherpool().remove_pusher(app_id, push_key, user_id)
|
return self.get_pusherpool().remove_pusher(app_id, push_key, user_id)
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import synapse.handlers.e2e_keys
|
|||||||
import synapse.handlers.set_password
|
import synapse.handlers.set_password
|
||||||
import synapse.rest.media.v1.media_repository
|
import synapse.rest.media.v1.media_repository
|
||||||
import synapse.server_notices.server_notices_manager
|
import synapse.server_notices.server_notices_manager
|
||||||
|
import synapse.server_notices.server_notices_sender
|
||||||
import synapse.state
|
import synapse.state
|
||||||
import synapse.storage
|
import synapse.storage
|
||||||
|
|
||||||
@ -69,3 +70,6 @@ class HomeServer(object):
|
|||||||
|
|
||||||
def get_server_notices_manager(self) -> synapse.server_notices.server_notices_manager.ServerNoticesManager:
|
def get_server_notices_manager(self) -> synapse.server_notices.server_notices_manager.ServerNoticesManager:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_server_notices_sender(self) -> synapse.server_notices.server_notices_sender.ServerNoticesSender:
|
||||||
|
pass
|
||||||
|
95
synapse/server_notices/consent_server_notices.py
Normal file
95
synapse/server_notices/consent_server_notices.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018 New Vector Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
from synapse.api.errors import SynapseError
|
||||||
|
from synapse.config import ConfigError
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class ConsentServerNotices(object):
|
||||||
|
"""Keeps track of whether we need to send users server_notices about
|
||||||
|
privacy policy consent, and sends one if we do.
|
||||||
|
"""
|
||||||
|
def __init__(self, hs):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer):
|
||||||
|
"""
|
||||||
|
self._server_notices_manager = hs.get_server_notices_manager()
|
||||||
|
self._store = hs.get_datastore()
|
||||||
|
|
||||||
|
self._users_in_progress = set()
|
||||||
|
|
||||||
|
self._current_consent_version = hs.config.user_consent_version
|
||||||
|
self._server_notice_content = hs.config.user_consent_server_notice_content
|
||||||
|
|
||||||
|
if self._server_notice_content is not None:
|
||||||
|
if not self._server_notices_manager.is_enabled():
|
||||||
|
raise ConfigError(
|
||||||
|
"user_consent configuration requires server notices, but "
|
||||||
|
"server notices are not enabled.",
|
||||||
|
)
|
||||||
|
if 'body' not in self._server_notice_content:
|
||||||
|
raise ConfigError(
|
||||||
|
"user_consent server_notice_consent must contain a 'body' "
|
||||||
|
"key.",
|
||||||
|
)
|
||||||
|
|
||||||
|
@defer.inlineCallbacks
|
||||||
|
def maybe_send_server_notice_to_user(self, user_id):
|
||||||
|
"""Check if we need to send a notice to this user, and does so if so
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): user to check
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred
|
||||||
|
"""
|
||||||
|
if self._server_notice_content is None:
|
||||||
|
# not enabled
|
||||||
|
return
|
||||||
|
|
||||||
|
# make sure we don't send two messages to the same user at once
|
||||||
|
if user_id in self._users_in_progress:
|
||||||
|
return
|
||||||
|
self._users_in_progress.add(user_id)
|
||||||
|
try:
|
||||||
|
u = yield self._store.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
if u["consent_version"] == self._current_consent_version:
|
||||||
|
# user has already consented
|
||||||
|
return
|
||||||
|
|
||||||
|
if u["consent_server_notice_sent"] == self._current_consent_version:
|
||||||
|
# we've already sent a notice to the user
|
||||||
|
return
|
||||||
|
|
||||||
|
# need to send a message
|
||||||
|
try:
|
||||||
|
yield self._server_notices_manager.send_notice(
|
||||||
|
user_id, self._server_notice_content,
|
||||||
|
)
|
||||||
|
yield self._store.user_set_consent_server_notice_sent(
|
||||||
|
user_id, self._current_consent_version,
|
||||||
|
)
|
||||||
|
except SynapseError as e:
|
||||||
|
logger.error("Error sending server notice about user consent: %s", e)
|
||||||
|
finally:
|
||||||
|
self._users_in_progress.remove(user_id)
|
58
synapse/server_notices/server_notices_sender.py
Normal file
58
synapse/server_notices/server_notices_sender.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018 New Vector Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from synapse.server_notices.consent_server_notices import ConsentServerNotices
|
||||||
|
|
||||||
|
|
||||||
|
class ServerNoticesSender(object):
|
||||||
|
"""A centralised place which sends server notices automatically when
|
||||||
|
Certain Events take place
|
||||||
|
"""
|
||||||
|
def __init__(self, hs):
|
||||||
|
"""
|
||||||
|
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer):
|
||||||
|
"""
|
||||||
|
# todo: it would be nice to make this more dynamic
|
||||||
|
self._consent_server_notices = ConsentServerNotices(hs)
|
||||||
|
|
||||||
|
def on_user_syncing(self, user_id):
|
||||||
|
"""Called when the user performs a sync operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): mxid of user who synced
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred
|
||||||
|
"""
|
||||||
|
return self._consent_server_notices.maybe_send_server_notice_to_user(
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
def on_user_ip(self, user_id):
|
||||||
|
"""Called on the master when a worker process saw a client request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): mxid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred
|
||||||
|
"""
|
||||||
|
# The synchrotrons use a stubbed version of ServerNoticesSender, so
|
||||||
|
# we check for notices to send to the user in on_user_ip as well as
|
||||||
|
# in on_user_syncing
|
||||||
|
return self._consent_server_notices.maybe_send_server_notice_to_user(
|
||||||
|
user_id,
|
||||||
|
)
|
46
synapse/server_notices/worker_server_notices_sender.py
Normal file
46
synapse/server_notices/worker_server_notices_sender.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018 New Vector Ltd
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from twisted.internet import defer
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerServerNoticesSender(object):
|
||||||
|
"""Stub impl of ServerNoticesSender which does nothing"""
|
||||||
|
def __init__(self, hs):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
hs (synapse.server.HomeServer):
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_user_syncing(self, user_id):
|
||||||
|
"""Called when the user performs a sync operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): mxid of user who synced
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred
|
||||||
|
"""
|
||||||
|
return defer.succeed()
|
||||||
|
|
||||||
|
def on_user_ip(self, user_id):
|
||||||
|
"""Called on the master when a worker process saw a client request.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): mxid
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Deferred
|
||||||
|
"""
|
||||||
|
raise AssertionError("on_user_ip unexpectedly called on worker")
|
@ -33,7 +33,10 @@ class RegistrationWorkerStore(SQLBaseStore):
|
|||||||
keyvalues={
|
keyvalues={
|
||||||
"name": user_id,
|
"name": user_id,
|
||||||
},
|
},
|
||||||
retcols=["name", "password_hash", "is_guest"],
|
retcols=[
|
||||||
|
"name", "password_hash", "is_guest",
|
||||||
|
"consent_version", "consent_server_notice_sent",
|
||||||
|
],
|
||||||
allow_none=True,
|
allow_none=True,
|
||||||
desc="get_user_by_id",
|
desc="get_user_by_id",
|
||||||
)
|
)
|
||||||
@ -297,12 +300,41 @@ class RegistrationStore(RegistrationWorkerStore,
|
|||||||
Raises:
|
Raises:
|
||||||
StoreError(404) if user not found
|
StoreError(404) if user not found
|
||||||
"""
|
"""
|
||||||
return self._simple_update_one(
|
def f(txn):
|
||||||
|
self._simple_update_one_txn(
|
||||||
|
txn,
|
||||||
table='users',
|
table='users',
|
||||||
keyvalues={'name': user_id, },
|
keyvalues={'name': user_id, },
|
||||||
updatevalues={'consent_version': consent_version, },
|
updatevalues={'consent_version': consent_version, },
|
||||||
desc="user_set_consent_version"
|
|
||||||
)
|
)
|
||||||
|
self._invalidate_cache_and_stream(
|
||||||
|
txn, self.get_user_by_id, (user_id,)
|
||||||
|
)
|
||||||
|
return self.runInteraction("user_set_consent_version", f)
|
||||||
|
|
||||||
|
def user_set_consent_server_notice_sent(self, user_id, consent_version):
|
||||||
|
"""Updates the user table to record that we have sent the user a server
|
||||||
|
notice about privacy policy consent
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id (str): full mxid of the user to update
|
||||||
|
consent_version (str): version of the policy we have notified the
|
||||||
|
user about
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
StoreError(404) if user not found
|
||||||
|
"""
|
||||||
|
def f(txn):
|
||||||
|
self._simple_update_one_txn(
|
||||||
|
txn,
|
||||||
|
table='users',
|
||||||
|
keyvalues={'name': user_id, },
|
||||||
|
updatevalues={'consent_server_notice_sent': consent_version, },
|
||||||
|
)
|
||||||
|
self._invalidate_cache_and_stream(
|
||||||
|
txn, self.get_user_by_id, (user_id,)
|
||||||
|
)
|
||||||
|
return self.runInteraction("user_set_consent_server_notice_sent", f)
|
||||||
|
|
||||||
def user_delete_access_tokens(self, user_id, except_token_id=None,
|
def user_delete_access_tokens(self, user_id, except_token_id=None,
|
||||||
device_id=None):
|
device_id=None):
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
/* Copyright 2018 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* record whether we have sent a server notice about consenting to the
|
||||||
|
* privacy policy. Specifically records the version of the policy we sent
|
||||||
|
* a message about.
|
||||||
|
*/
|
||||||
|
ALTER TABLE users ADD COLUMN consent_server_notice_sent TEXT;
|
@ -42,9 +42,14 @@ class RegistrationStoreTestCase(unittest.TestCase):
|
|||||||
yield self.store.register(self.user_id, self.tokens[0], self.pwhash)
|
yield self.store.register(self.user_id, self.tokens[0], self.pwhash)
|
||||||
|
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
|
{
|
||||||
# TODO(paul): Surely this field should be 'user_id', not 'name'
|
# TODO(paul): Surely this field should be 'user_id', not 'name'
|
||||||
# Additionally surely it shouldn't come in a 1-element list
|
"name": self.user_id,
|
||||||
{"name": self.user_id, "password_hash": self.pwhash, "is_guest": 0},
|
"password_hash": self.pwhash,
|
||||||
|
"is_guest": 0,
|
||||||
|
"consent_version": None,
|
||||||
|
"consent_server_notice_sent": None,
|
||||||
|
},
|
||||||
(yield self.store.get_user_by_id(self.user_id))
|
(yield self.store.get_user_by_id(self.user_id))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ def setup_test_homeserver(name="test", datastore=None, config=None, **kargs):
|
|||||||
config.federation_rc_concurrent = 10
|
config.federation_rc_concurrent = 10
|
||||||
config.filter_timeline_limit = 5000
|
config.filter_timeline_limit = 5000
|
||||||
config.user_directory_search_all_users = False
|
config.user_directory_search_all_users = False
|
||||||
|
config.user_consent_server_notice_content = None
|
||||||
|
|
||||||
# disable user directory updates, because they get done in the
|
# disable user directory updates, because they get done in the
|
||||||
# background, which upsets the test runner.
|
# background, which upsets the test runner.
|
||||||
|
Loading…
Reference in New Issue
Block a user