123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138 |
- # -*- 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 six import iteritems, string_types
- from twisted.internet import defer
- from synapse.api.errors import SynapseError
- from synapse.api.urls import ConsentURIBuilder
- from synapse.config import ConfigError
- from synapse.types import get_localpart_from_id
- 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
- self._send_to_guests = hs.config.user_consent_server_notice_to_guests
- 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.",
- )
- self._consent_uri_builder = ConsentURIBuilder(hs.config)
- @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["is_guest"] and not self._send_to_guests:
- # don't send to guests
- return
- 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:
- consent_uri = self._consent_uri_builder.build_user_consent_uri(
- get_localpart_from_id(user_id),
- )
- content = copy_with_str_subst(
- self._server_notice_content, {
- 'consent_uri': consent_uri,
- },
- )
- yield self._server_notices_manager.send_notice(
- user_id, 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)
- def copy_with_str_subst(x, substitutions):
- """Deep-copy a structure, carrying out string substitions on any strings
- Args:
- x (object): structure to be copied
- substitutions (object): substitutions to be made - passed into the
- string '%' operator
- Returns:
- copy of x
- """
- if isinstance(x, string_types):
- return x % substitutions
- if isinstance(x, dict):
- return {
- k: copy_with_str_subst(v, substitutions) for (k, v) in iteritems(x)
- }
- if isinstance(x, (list, tuple)):
- return [copy_with_str_subst(y) for y in x]
- # assume it's uninterested and can be shallow-copied.
- return x
|