mirror of https://github.com/watcha-fr/synapse
parent
d4d12daed9
commit
b8ca494ee9
@ -0,0 +1,120 @@ |
||||
# -*- coding: utf-8 -*- |
||||
# Copyright 2017 Vector Creations 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 |
||||
|
||||
from synapse.api.errors import SynapseError |
||||
from synapse.types import get_domain_from_id |
||||
from synapse.util.logcontext import preserve_fn |
||||
|
||||
from signedjson.sign import sign_json |
||||
|
||||
|
||||
DEFAULT_ATTESTATION_LENGTH_MS = 3 * 24 * 60 * 60 * 1000 |
||||
MIN_ATTESTATION_LENGTH_MS = 1 * 60 * 60 * 1000 |
||||
UPDATE_ATTESTATION_TIME_MS = 1 * 24 * 60 * 60 * 1000 |
||||
|
||||
|
||||
class GroupAttestationSigning(object): |
||||
def __init__(self, hs): |
||||
self.keyring = hs.get_keyring() |
||||
self.clock = hs.get_clock() |
||||
self.server_name = hs.hostname |
||||
self.signing_key = hs.config.signing_key[0] |
||||
|
||||
@defer.inlineCallbacks |
||||
def verify_attestation(self, attestation, group_id, user_id, server_name=None): |
||||
if not server_name: |
||||
if get_domain_from_id(group_id) == self.server_name: |
||||
server_name = get_domain_from_id(user_id) |
||||
else: |
||||
server_name = get_domain_from_id(group_id) |
||||
|
||||
if user_id != attestation["user_id"]: |
||||
raise SynapseError(400, "Attestation has incorrect user_id") |
||||
|
||||
if group_id != attestation["group_id"]: |
||||
raise SynapseError(400, "Attestation has incorrect group_id") |
||||
|
||||
valid_until_ms = attestation["valid_until_ms"] |
||||
if valid_until_ms - self.clock.time_msec() < MIN_ATTESTATION_LENGTH_MS: |
||||
raise SynapseError(400, "Attestation not valid for long enough") |
||||
|
||||
yield self.keyring.verify_json_for_server(server_name, attestation) |
||||
|
||||
def create_attestation(self, group_id, user_id): |
||||
return sign_json({ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
"valid_until_ms": self.clock.time_msec() + DEFAULT_ATTESTATION_LENGTH_MS, |
||||
}, self.server_name, self.signing_key) |
||||
|
||||
|
||||
class GroupAttestionRenewer(object): |
||||
def __init__(self, hs): |
||||
self.clock = hs.get_clock() |
||||
self.store = hs.get_datastore() |
||||
self.assestations = hs.get_groups_attestation_signing() |
||||
self.transport_client = hs.get_federation_transport_client() |
||||
|
||||
self._renew_attestations_loop = self.clock.looping_call( |
||||
self._renew_attestations, 30 * 60 * 1000, |
||||
) |
||||
|
||||
@defer.inlineCallbacks |
||||
def on_renew_attestation(self, group_id, user_id, content): |
||||
attestation = content["attestation"] |
||||
|
||||
yield self.attestations.verify_attestation( |
||||
attestation, |
||||
user_id=user_id, |
||||
group_id=group_id, |
||||
) |
||||
|
||||
yield self.store.update_remote_attestion(group_id, user_id, attestation) |
||||
|
||||
defer.returnValue({}) |
||||
|
||||
@defer.inlineCallbacks |
||||
def _renew_attestations(self): |
||||
now = self.clock.time_msec() |
||||
|
||||
rows = yield self.store.get_attestations_need_renewals( |
||||
now + UPDATE_ATTESTATION_TIME_MS |
||||
) |
||||
|
||||
@defer.inlineCallbacks |
||||
def _renew_attestation(self, group_id, user_id): |
||||
attestation = self.attestations.create_attestation(group_id, user_id) |
||||
|
||||
if self.hs.is_mine_id(group_id): |
||||
destination = get_domain_from_id(user_id) |
||||
else: |
||||
destination = get_domain_from_id(group_id) |
||||
|
||||
yield self.transport_client.renew_group_attestation( |
||||
destination, group_id, user_id, |
||||
content={"attestation": attestation}, |
||||
) |
||||
|
||||
yield self.store.update_attestation_renewal( |
||||
group_id, user_id, attestation |
||||
) |
||||
|
||||
for row in rows: |
||||
group_id = row["group_id"] |
||||
user_id = row["user_id"] |
||||
|
||||
preserve_fn(_renew_attestation)(group_id, user_id) |
@ -0,0 +1,382 @@ |
||||
# -*- coding: utf-8 -*- |
||||
# Copyright 2017 Vector Creations 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 |
||||
|
||||
from synapse.api.errors import SynapseError |
||||
from synapse.types import UserID, get_domain_from_id |
||||
|
||||
|
||||
import functools |
||||
import logging |
||||
|
||||
logger = logging.getLogger(__name__) |
||||
|
||||
|
||||
# TODO: Allow users to "knock" or simpkly join depending on rules |
||||
# TODO: Federation admin APIs |
||||
# TODO: is_priveged flag to users and is_public to users and rooms |
||||
# TODO: Audit log for admins (profile updates, membership changes, users who tried |
||||
# to join but were rejected, etc) |
||||
# TODO: Flairs |
||||
|
||||
|
||||
UPDATE_ATTESTATION_TIME_MS = 1 * 24 * 60 * 60 * 1000 |
||||
|
||||
|
||||
def check_group_is_ours(and_exists=False): |
||||
def g(func): |
||||
@functools.wraps(func) |
||||
@defer.inlineCallbacks |
||||
def h(self, group_id, *args, **kwargs): |
||||
if not self.is_mine_id(group_id): |
||||
raise SynapseError(400, "Group not on this server") |
||||
if and_exists: |
||||
group = yield self.store.get_group(group_id) |
||||
if not group: |
||||
raise SynapseError(404, "Unknown group") |
||||
|
||||
res = yield func(self, group_id, *args, **kwargs) |
||||
defer.returnValue(res) |
||||
|
||||
return h |
||||
return g |
||||
|
||||
|
||||
class GroupsServerHandler(object): |
||||
def __init__(self, hs): |
||||
self.hs = hs |
||||
self.store = hs.get_datastore() |
||||
self.room_list_handler = hs.get_room_list_handler() |
||||
self.auth = hs.get_auth() |
||||
self.clock = hs.get_clock() |
||||
self.keyring = hs.get_keyring() |
||||
self.is_mine_id = hs.is_mine_id |
||||
self.signing_key = hs.config.signing_key[0] |
||||
self.server_name = hs.hostname |
||||
self.attestations = hs.get_groups_attestation_signing() |
||||
self.transport_client = hs.get_federation_transport_client() |
||||
|
||||
# Ensure attestations get renewed |
||||
hs.get_groups_attestation_renewer() |
||||
|
||||
@check_group_is_ours() |
||||
@defer.inlineCallbacks |
||||
def get_group_profile(self, group_id, requester_user_id): |
||||
group_description = yield self.store.get_group(group_id) |
||||
|
||||
if group_description: |
||||
defer.returnValue(group_description) |
||||
else: |
||||
raise SynapseError(404, "Unknown group") |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def get_users_in_group(self, group_id, requester_user_id): |
||||
is_user_in_group = yield self.store.is_user_in_group(requester_user_id, group_id) |
||||
|
||||
user_results = yield self.store.get_users_in_group( |
||||
group_id, include_private=is_user_in_group, |
||||
) |
||||
|
||||
chunk = [] |
||||
for user_result in user_results: |
||||
g_user_id = user_result["user_id"] |
||||
is_public = user_result["is_public"] |
||||
|
||||
entry = {"user_id": g_user_id} |
||||
|
||||
# TODO: Get profile information |
||||
|
||||
if not is_public: |
||||
entry["is_public"] = False |
||||
|
||||
if not self.is_mine_id(requester_user_id): |
||||
attestation = yield self.store.get_remote_attestation(group_id, g_user_id) |
||||
if not attestation: |
||||
continue |
||||
|
||||
entry["attestation"] = attestation |
||||
else: |
||||
entry["attestation"] = self.attestations.create_attestation( |
||||
group_id, g_user_id, |
||||
) |
||||
|
||||
chunk.append(entry) |
||||
|
||||
# TODO: If admin add lists of users whose attestations have timed out |
||||
|
||||
defer.returnValue({ |
||||
"chunk": chunk, |
||||
"total_user_count_estimate": len(user_results), |
||||
}) |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def get_rooms_in_group(self, group_id, requester_user_id): |
||||
is_user_in_group = yield self.store.is_user_in_group(requester_user_id, group_id) |
||||
|
||||
room_results = yield self.store.get_rooms_in_group( |
||||
group_id, include_private=is_user_in_group, |
||||
) |
||||
|
||||
chunk = [] |
||||
for room_result in room_results: |
||||
room_id = room_result["room_id"] |
||||
is_public = room_result["is_public"] |
||||
|
||||
joined_users = yield self.store.get_users_in_room(room_id) |
||||
entry = yield self.room_list_handler.generate_room_entry( |
||||
room_id, len(joined_users), |
||||
with_alias=False, allow_private=True, |
||||
) |
||||
|
||||
if not entry: |
||||
continue |
||||
|
||||
if not is_public: |
||||
entry["is_public"] = False |
||||
|
||||
chunk.append(entry) |
||||
|
||||
chunk.sort(key=lambda e: -e["num_joined_members"]) |
||||
|
||||
defer.returnValue({ |
||||
"chunk": chunk, |
||||
"total_room_count_estimate": len(room_results), |
||||
}) |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def add_room(self, group_id, requester_user_id, room_id, content): |
||||
is_admin = yield self.store.is_user_admin_in_group(group_id, requester_user_id) |
||||
if not is_admin: |
||||
raise SynapseError(403, "User is not admin in group") |
||||
|
||||
# TODO: Check if room has already been added |
||||
|
||||
visibility = content.get("visibility") |
||||
if visibility: |
||||
vis_type = visibility["type"] |
||||
if vis_type not in ("public", "private"): |
||||
raise SynapseError( |
||||
400, "Synapse only supports 'public'/'private' visibility" |
||||
) |
||||
is_public = vis_type == "public" |
||||
else: |
||||
is_public = True |
||||
|
||||
yield self.store.add_room_to_group(group_id, room_id, is_public=is_public) |
||||
|
||||
defer.returnValue({}) |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def invite_to_group(self, group_id, user_id, requester_user_id, content): |
||||
is_admin = yield self.store.is_user_admin_in_group( |
||||
group_id, requester_user_id |
||||
) |
||||
if not is_admin: |
||||
raise SynapseError(403, "User is not admin in group") |
||||
|
||||
# TODO: Check if user knocked |
||||
# TODO: Check if user is already invited |
||||
|
||||
group = yield self.store.get_group(group_id) |
||||
content = { |
||||
"profile": { |
||||
"name": group["name"], |
||||
"avatar_url": group["avatar_url"], |
||||
}, |
||||
"inviter": requester_user_id, |
||||
} |
||||
|
||||
if self.hs.is_mine_id(user_id): |
||||
raise NotImplementedError() |
||||
else: |
||||
local_attestation = self.attestations.create_attestation(group_id, user_id) |
||||
content.update({ |
||||
"attestation": local_attestation, |
||||
}) |
||||
|
||||
res = yield self.transport_client.invite_to_group_notification( |
||||
get_domain_from_id(user_id), group_id, user_id, content |
||||
) |
||||
|
||||
if res["state"] == "join": |
||||
if not self.hs.is_mine_id(user_id): |
||||
remote_attestation = res["attestation"] |
||||
|
||||
yield self.attestations.verify_attestation( |
||||
remote_attestation, |
||||
user_id=user_id, |
||||
group_id=group_id, |
||||
) |
||||
else: |
||||
remote_attestation = None |
||||
|
||||
yield self.store.add_user_to_group( |
||||
group_id, user_id, |
||||
is_admin=False, |
||||
is_public=False, # TODO |
||||
local_attestation=local_attestation, |
||||
remote_attestation=remote_attestation, |
||||
) |
||||
elif res["state"] == "invite": |
||||
yield self.store.add_group_invite( |
||||
group_id, user_id, |
||||
) |
||||
defer.returnValue({ |
||||
"state": "invite" |
||||
}) |
||||
elif res["state"] == "reject": |
||||
defer.returnValue({ |
||||
"state": "reject" |
||||
}) |
||||
else: |
||||
raise SynapseError(502, "Unknown state returned by HS") |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def accept_invite(self, group_id, user_id, content): |
||||
if not self.store.is_user_invited_to_local_group(group_id, user_id): |
||||
raise SynapseError(403, "User not invited to group") |
||||
|
||||
if not self.hs.is_mine_id(user_id): |
||||
remote_attestation = content["attestation"] |
||||
|
||||
yield self.attestations.verify_attestation( |
||||
remote_attestation, |
||||
user_id=user_id, |
||||
group_id=group_id, |
||||
) |
||||
else: |
||||
remote_attestation = None |
||||
|
||||
local_attestation = self.attestations.create_attestation(group_id, user_id) |
||||
|
||||
visibility = content.get("visibility") |
||||
if visibility: |
||||
vis_type = visibility["type"] |
||||
if vis_type not in ("public", "private"): |
||||
raise SynapseError( |
||||
400, "Synapse only supports 'public'/'private' visibility" |
||||
) |
||||
is_public = vis_type == "public" |
||||
else: |
||||
is_public = True |
||||
|
||||
yield self.store.add_user_to_group( |
||||
group_id, user_id, |
||||
is_admin=False, |
||||
is_public=is_public, |
||||
local_attestation=local_attestation, |
||||
remote_attestation=remote_attestation, |
||||
) |
||||
|
||||
defer.returnValue({ |
||||
"state": "join", |
||||
"attestation": local_attestation, |
||||
}) |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def knock(self, group_id, user_id, content): |
||||
pass |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def accept_knock(self, group_id, user_id, content): |
||||
pass |
||||
|
||||
@check_group_is_ours(and_exists=True) |
||||
@defer.inlineCallbacks |
||||
def remove_user_from_group(self, group_id, user_id, requester_user_id, content): |
||||
is_kick = False |
||||
if requester_user_id != user_id: |
||||
is_admin = yield self.store.is_user_admin_in_group( |
||||
group_id, requester_user_id |
||||
) |
||||
if not is_admin: |
||||
raise SynapseError(403, "User is not admin in group") |
||||
|
||||
is_kick = True |
||||
|
||||
yield self.store.remove_user_to_group( |
||||
group_id, user_id, |
||||
) |
||||
|
||||
if is_kick: |
||||
if self.hs.is_mine_id(user_id): |
||||
raise NotImplementedError() |
||||
else: |
||||
yield self.transport_client.remove_user_from_group_notification( |
||||
get_domain_from_id(user_id), group_id, user_id, {} |
||||
) |
||||
|
||||
defer.returnValue({}) |
||||
|
||||
@check_group_is_ours() |
||||
@defer.inlineCallbacks |
||||
def create_group(self, group_id, user_id, content): |
||||
logger.info("Attempting to create group with ID: %r", group_id) |
||||
group = yield self.store.get_group(group_id) |
||||
if group: |
||||
raise SynapseError(400, "Group already exists") |
||||
|
||||
is_admin = yield self.auth.is_server_admin(UserID.from_string(user_id)) |
||||
if not is_admin and not group_id.startswith("+u/"): |
||||
raise SynapseError(403, "Group ID must start with '+u/' or be a server admin") |
||||
|
||||
profile = content.get("profile", {}) |
||||
name = profile.get("name") |
||||
avatar_url = profile.get("avatar_url") |
||||
short_description = profile.get("short_description") |
||||
long_description = profile.get("long_description") |
||||
|
||||
yield self.store.create_group( |
||||
group_id, |
||||
user_id, |
||||
name=name, |
||||
avatar_url=avatar_url, |
||||
short_description=short_description, |
||||
long_description=long_description, |
||||
) |
||||
|
||||
if not self.hs.is_mine_id(user_id): |
||||
remote_attestation = content["attestation"] |
||||
|
||||
yield self.attestations.verify_attestation( |
||||
remote_attestation, |
||||
user_id=user_id, |
||||
group_id=group_id, |
||||
) |
||||
|
||||
local_attestation = self.attestations.create_attestation(group_id, user_id) |
||||
else: |
||||
local_attestation = None |
||||
remote_attestation = None |
||||
|
||||
yield self.store.add_user_to_group( |
||||
group_id, user_id, |
||||
is_admin=True, |
||||
is_public=True, # TODO |
||||
local_attestation=local_attestation, |
||||
remote_attestation=remote_attestation, |
||||
) |
||||
|
||||
defer.returnValue({ |
||||
"group_id": group_id, |
||||
}) |
@ -0,0 +1,280 @@ |
||||
# -*- coding: utf-8 -*- |
||||
# Copyright 2017 Vector Creations 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 |
||||
|
||||
from ._base import SQLBaseStore |
||||
|
||||
import ujson as json |
||||
|
||||
|
||||
class GroupServerStore(SQLBaseStore): |
||||
def get_group(self, group_id): |
||||
return self._simple_select_one( |
||||
table="groups", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
}, |
||||
retcols=("name", "short_description", "long_description", "avatar_url",), |
||||
allow_none=True, |
||||
desc="is_user_in_group", |
||||
) |
||||
|
||||
def get_users_in_group(self, group_id, include_private=False): |
||||
# TODO: Pagination |
||||
|
||||
keyvalues = { |
||||
"group_id": group_id, |
||||
} |
||||
if not include_private: |
||||
keyvalues["is_public"] = True |
||||
|
||||
return self._simple_select_list( |
||||
table="group_users", |
||||
keyvalues=keyvalues, |
||||
retcols=("user_id", "is_public",), |
||||
desc="get_users_in_group", |
||||
) |
||||
|
||||
def get_rooms_in_group(self, group_id, include_private=False): |
||||
# TODO: Pagination |
||||
|
||||
keyvalues = { |
||||
"group_id": group_id, |
||||
} |
||||
if not include_private: |
||||
keyvalues["is_public"] = True |
||||
|
||||
return self._simple_select_list( |
||||
table="group_rooms", |
||||
keyvalues=keyvalues, |
||||
retcols=("room_id", "is_public",), |
||||
desc="get_rooms_in_group", |
||||
) |
||||
|
||||
def is_user_in_group(self, user_id, group_id): |
||||
return self._simple_select_one_onecol( |
||||
table="group_users", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
retcol="user_id", |
||||
allow_none=True, |
||||
desc="is_user_in_group", |
||||
).addCallback(lambda r: bool(r)) |
||||
|
||||
def is_user_admin_in_group(self, group_id, user_id): |
||||
return self._simple_select_one_onecol( |
||||
table="group_users", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
retcol="is_admin", |
||||
allow_none=True, |
||||
desc="is_user_adim_in_group", |
||||
) |
||||
|
||||
def add_group_invite(self, group_id, user_id): |
||||
return self._simple_insert( |
||||
table="group_invites", |
||||
values={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
desc="add_group_invite", |
||||
) |
||||
|
||||
def is_user_invited_to_local_group(self, group_id, user_id): |
||||
return self._simple_select_one_onecol( |
||||
table="group_invites", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
retcol="user_id", |
||||
desc="is_user_invited_to_local_group", |
||||
allow_none=True, |
||||
) |
||||
|
||||
def add_user_to_group(self, group_id, user_id, is_admin=False, is_public=True, |
||||
local_attestation=None, remote_attestation=None): |
||||
def _add_user_to_group_txn(txn): |
||||
self._simple_insert_txn( |
||||
txn, |
||||
table="group_users", |
||||
values={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
"is_admin": is_admin, |
||||
"is_public": is_public, |
||||
}, |
||||
) |
||||
|
||||
self._simple_delete_txn( |
||||
txn, |
||||
table="group_invites", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
) |
||||
|
||||
if local_attestation: |
||||
self._simple_insert_txn( |
||||
txn, |
||||
table="group_attestations_renewals", |
||||
values={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
"valid_until_ms": local_attestation["valid_until_ms"], |
||||
}, |
||||
) |
||||
if remote_attestation: |
||||
self._simple_insert_txn( |
||||
txn, |
||||
table="group_attestations_remote", |
||||
values={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
"valid_until_ms": remote_attestation["valid_until_ms"], |
||||
"attestation": json.dumps(remote_attestation), |
||||
}, |
||||
) |
||||
|
||||
return self.runInteraction( |
||||
"add_user_to_group", _add_user_to_group_txn |
||||
) |
||||
|
||||
def remove_user_to_group(self, group_id, user_id): |
||||
def _remove_user_to_group_txn(txn): |
||||
self._simple_delete_txn( |
||||
txn, |
||||
table="group_users", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
) |
||||
self._simple_delete_txn( |
||||
txn, |
||||
table="group_invites", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
) |
||||
self._simple_delete_txn( |
||||
txn, |
||||
table="group_attestations_renewals", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
) |
||||
self._simple_delete_txn( |
||||
txn, |
||||
table="group_attestations_remote", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
) |
||||
return self.runInteraction("remove_user_to_group", _remove_user_to_group_txn) |
||||
|
||||
def add_room_to_group(self, group_id, room_id, is_public): |
||||
return self._simple_insert( |
||||
table="group_rooms", |
||||
values={ |
||||
"group_id": group_id, |
||||
"room_id": room_id, |
||||
"is_public": is_public, |
||||
}, |
||||
desc="add_room_to_group", |
||||
) |
||||
|
||||
@defer.inlineCallbacks |
||||
def create_group(self, group_id, user_id, name, avatar_url, short_description, |
||||
long_description,): |
||||
yield self._simple_insert( |
||||
table="groups", |
||||
values={ |
||||
"group_id": group_id, |
||||
"name": name, |
||||
"avatar_url": avatar_url, |
||||
"short_description": short_description, |
||||
"long_description": long_description, |
||||
}, |
||||
desc="create_group", |
||||
) |
||||
|
||||
def get_attestations_need_renewals(self, valid_until_ms): |
||||
def _get_attestations_need_renewals_txn(txn): |
||||
sql = """ |
||||
SELECT group_id, user_id FROM group_attestations_renewals |
||||
WHERE valid_until_ms <= ? |
||||
""" |
||||
txn.execute(sql, (valid_until_ms,)) |
||||
return self.cursor_to_dict(txn) |
||||
return self.runInteraction( |
||||
"get_attestations_need_renewals", _get_attestations_need_renewals_txn |
||||
) |
||||
|
||||
def update_attestation_renewal(self, group_id, user_id, attestation): |
||||
return self._simple_update_one( |
||||
table="group_attestations_renewals", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
updatevalues={ |
||||
"valid_until_ms": attestation["valid_until_ms"], |
||||
}, |
||||
desc="update_attestation_renewal", |
||||
) |
||||
|
||||
def update_remote_attestion(self, group_id, user_id, attestation): |
||||
return self._simple_update_one( |
||||
table="group_attestations_remote", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
updatevalues={ |
||||
"valid_until_ms": attestation["valid_until_ms"], |
||||
"attestation": json.dumps(attestation) |
||||
}, |
||||
desc="update_remote_attestion", |
||||
) |
||||
|
||||
@defer.inlineCallbacks |
||||
def get_remote_attestation(self, group_id, user_id): |
||||
row = yield self._simple_select_one( |
||||
table="group_attestations_remote", |
||||
keyvalues={ |
||||
"group_id": group_id, |
||||
"user_id": user_id, |
||||
}, |
||||
retcols=("valid_until_ms", "attestation"), |
||||
desc="get_remote_attestation", |
||||
allow_none=True, |
||||
) |
||||
|
||||
now = int(self._clock.time_msec()) |
||||
if row and now < row["valid_until_ms"]: |
||||
defer.returnValue(json.loads(row["attestation"])) |
||||
|
||||
defer.returnValue(None) |
@ -0,0 +1,77 @@ |
||||
/* Copyright 2017 Vector Creations 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. |
||||
*/ |
||||
|
||||
CREATE TABLE groups ( |
||||
group_id TEXT NOT NULL, |
||||
name TEXT, |
||||
avatar_url TEXT, |
||||
short_description TEXT, |
||||
long_description TEXT |
||||
); |
||||
|
||||
CREATE UNIQUE INDEX groups_idx ON groups(group_id); |
||||
|
||||
|
||||
CREATE TABLE group_users ( |
||||
group_id TEXT NOT NULL, |
||||
user_id TEXT NOT NULL, |
||||
is_admin BOOLEAN NOT NULL, |
||||
is_public BOOLEAN NOT NULL |
||||
); |
||||
|
||||
|
||||
CREATE INDEX groups_users_g_idx ON group_users(group_id, user_id); |
||||
CREATE INDEX groups_users_u_idx ON group_users(user_id); |
||||
|
||||
|
||||
CREATE TABLE group_invites ( |
||||
group_id TEXT NOT NULL, |
||||
user_id TEXT NOT NULL |
||||
); |
||||
|
||||
CREATE INDEX groups_invites_g_idx ON group_invites(group_id, user_id); |
||||
CREATE INDEX groups_invites_u_idx ON group_invites(user_id); |
||||
|
||||
|
||||
CREATE TABLE group_rooms ( |
||||
group_id TEXT NOT NULL, |
||||
room_id TEXT NOT NULL, |
||||
is_public BOOLEAN NOT NULL |
||||
); |
||||
|
||||
CREATE INDEX groups_rooms_g_idx ON group_rooms(group_id, room_id); |
||||
CREATE INDEX groups_rooms_r_idx ON group_rooms(room_id); |
||||
|
||||
|
||||
CREATE TABLE group_attestations_renewals ( |
||||
group_id TEXT NOT NULL, |
||||
user_id TEXT NOT NULL, |
||||
valid_until_ms BIGINT NOT NULL |
||||
); |
||||
|
||||
CREATE INDEX group_attestations_renewals_g_idx ON group_attestations_renewals(group_id, user_id); |
||||
CREATE INDEX group_attestations_renewals_u_idx ON group_attestations_renewals(user_id); |
||||
CREATE INDEX group_attestations_renewals_v_idx ON group_attestations_renewals(valid_until_ms); |
||||
|
||||
CREATE TABLE group_attestations_remote ( |
||||
group_id TEXT NOT NULL, |
||||
user_id TEXT NOT NULL, |
||||
valid_until_ms BIGINT NOT NULL, |
||||
attestation TEXT NOT NULL |
||||
); |
||||
|
||||
CREATE INDEX group_attestations_remote_g_idx ON group_attestations_remote(group_id, user_id); |
||||
CREATE INDEX group_attestations_remote_u_idx ON group_attestations_remote(user_id); |
||||
CREATE INDEX group_attestations_remote_v_idx ON group_attestations_remote(valid_until_ms); |
Loading…
Reference in new issue