Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Add timestamp to user's consent #13741

Merged
merged 4 commits into from
Sep 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/13741.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document the timestamp when a user accepts the consent, if [consent tracking](https://matrix-org.github.io/synapse/latest/consent_tracking.html) is used.
2 changes: 2 additions & 0 deletions docs/admin_api/user_admin_api.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ It returns a JSON body like the following:
"appservice_id": null,
"consent_server_notice_sent": null,
"consent_version": null,
"consent_ts": null,
"external_ids": [
{
"auth_provider": "<provider1>",
Expand Down Expand Up @@ -364,6 +365,7 @@ The following actions are **NOT** performed. The list may be incomplete.
- Remove the user's creation (registration) timestamp
- [Remove rate limit overrides](#override-ratelimiting-for-users)
- Remove from monthly active users
- Remove user's consent information (consent version and timestamp)

## Reset password

Expand Down
1 change: 1 addition & 0 deletions synapse/handlers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ async def get_user(self, user: UserID) -> Optional[JsonDict]:
"appservice_id",
"consent_server_notice_sent",
"consent_version",
"consent_ts",
"user_type",
"is_guest",
}
Expand Down
6 changes: 5 additions & 1 deletion synapse/storage/databases/main/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ async def get_user_by_id(self, user_id: str) -> Optional[Dict[str, Any]]:
"is_guest",
"admin",
"consent_version",
"consent_ts",
"consent_server_notice_sent",
"appservice_id",
"creation_ts",
Expand Down Expand Up @@ -2227,7 +2228,10 @@ def f(txn: LoggingTransaction) -> None:
txn,
table="users",
keyvalues={"name": user_id},
updatevalues={"consent_version": consent_version},
updatevalues={
"consent_version": consent_version,
"consent_ts": self._clock.time_msec(),
},
)
self._invalidate_cache_and_stream(txn, self.get_user_by_id, (user_id,))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* Copyright 2022 The Matrix.org Foundation C.I.C
*
* 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.
*/

ALTER TABLE users ADD consent_ts bigint;
1 change: 1 addition & 0 deletions tests/rest/admin/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -2580,6 +2580,7 @@ def _check_fields(self, content: JsonDict) -> None:
self.assertIn("appservice_id", content)
self.assertIn("consent_server_notice_sent", content)
self.assertIn("consent_version", content)
self.assertIn("consent_ts", content)
self.assertIn("external_ids", content)

# This key was removed intentionally. Ensure it is not accidentally re-included.
Expand Down
33 changes: 26 additions & 7 deletions tests/storage/test_registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,26 @@
# 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.test.proto_helpers import MemoryReactor

from synapse.api.constants import UserTypes
from synapse.api.errors import ThreepidValidationError
from synapse.server import HomeServer
from synapse.util import Clock

from tests.unittest import HomeserverTestCase


class RegistrationStoreTestCase(HomeserverTestCase):
def prepare(self, reactor, clock, hs):
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
self.store = hs.get_datastores().main

self.user_id = "@my-user:test"
self.tokens = ["AbCdEfGhIjKlMnOpQrStUvWxYz", "BcDeFgHiJkLmNoPqRsTuVwXyZa"]
self.pwhash = "{xx1}123456789"
self.device_id = "akgjhdjklgshg"

def test_register(self):
def test_register(self) -> None:
self.get_success(self.store.register_user(self.user_id, self.pwhash))

self.assertEqual(
Expand All @@ -38,6 +41,7 @@ def test_register(self):
"admin": 0,
"is_guest": 0,
"consent_version": None,
"consent_ts": None,
"consent_server_notice_sent": None,
"appservice_id": None,
"creation_ts": 0,
Expand All @@ -48,7 +52,20 @@ def test_register(self):
(self.get_success(self.store.get_user_by_id(self.user_id))),
)

def test_add_tokens(self):
def test_consent(self) -> None:
self.get_success(self.store.register_user(self.user_id, self.pwhash))
before_consent = self.clock.time_msec()
self.reactor.advance(5)
self.get_success(self.store.user_set_consent_version(self.user_id, "1"))
self.reactor.advance(5)

user = self.get_success(self.store.get_user_by_id(self.user_id))
assert user
self.assertEqual(user["consent_version"], "1")
self.assertGreater(user["consent_ts"], before_consent)
self.assertLess(user["consent_ts"], self.clock.time_msec())

def test_add_tokens(self) -> None:
self.get_success(self.store.register_user(self.user_id, self.pwhash))
self.get_success(
self.store.add_access_token_to_user(
Expand All @@ -58,11 +75,12 @@ def test_add_tokens(self):

result = self.get_success(self.store.get_user_by_access_token(self.tokens[1]))

assert result
self.assertEqual(result.user_id, self.user_id)
self.assertEqual(result.device_id, self.device_id)
self.assertIsNotNone(result.token_id)

def test_user_delete_access_tokens(self):
def test_user_delete_access_tokens(self) -> None:
# add some tokens
self.get_success(self.store.register_user(self.user_id, self.pwhash))
self.get_success(
Expand All @@ -87,6 +105,7 @@ def test_user_delete_access_tokens(self):

# check the one not associated with the device was not deleted
user = self.get_success(self.store.get_user_by_access_token(self.tokens[0]))
assert user
self.assertEqual(self.user_id, user.user_id)

# now delete the rest
Expand All @@ -95,11 +114,11 @@ def test_user_delete_access_tokens(self):
user = self.get_success(self.store.get_user_by_access_token(self.tokens[0]))
self.assertIsNone(user, "access token was not deleted without device_id")

def test_is_support_user(self):
def test_is_support_user(self) -> None:
TEST_USER = "@test:test"
SUPPORT_USER = "@support:test"

res = self.get_success(self.store.is_support_user(None))
res = self.get_success(self.store.is_support_user(None)) # type: ignore[arg-type]
self.assertFalse(res)
self.get_success(
self.store.register_user(user_id=TEST_USER, password_hash=None)
Expand All @@ -115,7 +134,7 @@ def test_is_support_user(self):
res = self.get_success(self.store.is_support_user(SUPPORT_USER))
self.assertTrue(res)

def test_3pid_inhibit_invalid_validation_session_error(self):
def test_3pid_inhibit_invalid_validation_session_error(self) -> None:
"""Tests that enabling the configuration option to inhibit 3PID errors on
/requestToken also inhibits validation errors caused by an unknown session ID.
"""
Expand Down