Watcha op544 access right management when setting nextcloud url (#92)

* feat: change storing process of mapping between watcha room and Nextcloud directory

* feat: adds Nextcloud folder access right management process

* feat: adds specific error code on invalid permission on Nextcloud folder.

* feat: adds Nextcloud Integration Config class

* fix: variable spelling

* feat: improve function wording

* feat: change documentation of add_user_to_nextcloud_groups function

* test: skips Nextcloud integration test for OpenDSI demonstration

* feat: improve code name error.

* refactor: refactor watcha_nextcloud_integration.py file
code_spécifique_watcha
KevICO 4 years ago committed by c-cal
parent a32d79678e
commit ec1a57b60f
Signed by: watcha
GPG Key ID: 87DD78E7F7A1581D
  1. 1
      synapse/api/errors.py
  2. 1
      synapse/config/_base.pyi
  3. 2
      synapse/config/homeserver.py
  4. 63
      synapse/config/watcha_nextcloud_integration.py
  5. 381
      synapse/handlers/room.py
  6. 79
      synapse/rest/client/v1/room.py
  7. 54
      synapse/storage/databases/main/events.py
  8. 39
      synapse/storage/databases/main/room.py
  9. 3
      tests/rest/client/v1/test_watcha.py
  10. 6
      tests/storage/test_room.py

@ -76,6 +76,7 @@ class Codes:
INVALID_SIGNATURE = "M_INVALID_SIGNATURE"
USER_DEACTIVATED = "M_USER_DEACTIVATED"
BAD_ALIAS = "M_BAD_ALIAS"
NEXTCLOUD_FOLDER_ACCESS_FORBIDDEN = "W_NEXTCLOUD_FOLDER_ACCESS_FORBIDDEN" # watcha+
class CodeMessageException(RuntimeError):

@ -76,6 +76,7 @@ class RootConfig:
roomdirectory: room_directory.RoomDirectoryConfig
thirdpartyrules: third_party_event_rules.ThirdPartyRulesConfig
tracer: tracer.TracerConfig
nextcloudintegration: watcha_nextcloud_integration.NextcloudIntegrationConfig # watcha+ op544
config_classes: List = ...
def __init__(self) -> None: ...

@ -51,6 +51,7 @@ from .tracer import TracerConfig
from .user_directory import UserDirectoryConfig
from .voip import VoipConfig
from .workers import WorkerConfig
from .watcha_nextcloud_integration import NextcloudIntegrationConfig # watcha+ op544
class HomeServerConfig(RootConfig):
@ -93,4 +94,5 @@ class HomeServerConfig(RootConfig):
WorkerConfig,
RedisConfig,
FederationConfig,
NextcloudIntegrationConfig # watcha+ op544
]

@ -0,0 +1,63 @@
from ._base import Config
DEFAULT_CONFIG = """
# Nextcloud Integration configuration
#
# 'keycloak_serveur' corresponds to the Keycloak server URL
# use to handle authentification process.
#
# 'keycloak_realm' is the name of your Keycloak realm
# (https://www.keycloak.org/docs/latest/getting_started/#creating-a-realm-and-a-user)
#
# 'nextcloud_server' corresponds to the Nextcloud server URL.
#
# 'nextcloud_shared_secret' is the secret used to allow Synapse
# to logged as a user and to call Nextcloud APIs.
#
# 'service_account_name' is the name of the account service use to
# handle operations between keycloak, Synapse and Nextcloud.
#
# 'service_account_password' is the password or maybe more
# a shared secret for the service account.
#
#nextcloud_integration:
# keycloak_serveur: "https://example.com/auth"
# keycloak_realm: "example.com"
# nextcloud_server: "https://example.com/nextcloud"
# nextcloud_shared_secret: "YOUR_SHARED_SECRET"
# service_account_name: "example_account_service"
# service_account_password: "YOUR_SHARED_SECRET"
"""
class NextcloudIntegrationConfig(Config):
section = "nextcloudintegration"
def __init__(self, *args):
super(NextcloudIntegrationConfig, self).__init__(*args)
self.keycloak_serveur = None
self.keycloak_realm = None
self.nextcloud_server = None
self.nextcloud_shared_secret = None
self.service_account_name = None
self.service_account_password = None
def read_config(self, config, **kwargs):
nextcloud_integration_config = config.get("nextcloud_integration", {})
self.keycloak_serveur = nextcloud_integration_config.get("keycloak_serveur")
self.keycloak_realm = nextcloud_integration_config.get("keycloak_realm")
self.nextcloud_server = nextcloud_integration_config.get("nextcloud_server")
self.nextcloud_shared_secret = nextcloud_integration_config.get(
"nextcloud_shared_secret"
)
self.service_account_name = nextcloud_integration_config.get(
"service_account_name"
)
self.service_account_password = nextcloud_integration_config.get(
"service_account_password"
)
def generate_config_section(self, config_dir_path, server_name, **kwargs):
return DEFAULT_CONFIG

@ -23,8 +23,13 @@ import math
import random
import string
from collections import OrderedDict
from pathlib import Path # watcha+ op488
# watcha+
from pathlib import Path
from requests import get, post, delete, auth, HTTPError
from requests.auth import HTTPBasicAuth
# +watcha
from typing import TYPE_CHECKING, Any, Awaitable, Dict, List, Optional, Tuple
from urllib.parse import parse_qs, urlparse # watcha+ op486
from synapse.api.constants import (
EventTypes,
@ -52,6 +57,7 @@ from synapse.types import (
UserID,
create_requester,
)
from synapse.types import get_localpart_from_id # watcha+ op544
from synapse.util import stringutils
from synapse.util.async_helpers import Linearizer
from synapse.util.caches.response_cache import ResponseCache
@ -1411,6 +1417,373 @@ class WatchaRoomHandler(BaseHandler):
self.store = hs.get_datastore()
self.event_creation_handler = hs.get_event_creation_handler()
# Nextcloud Integration config :
self.keycloak_server = hs.config.keycloak_serveur
self.keycloak_realm = hs.config.keycloak_realm
self.nextcloud_shared_secret = hs.config.nextcloud_shared_secret
self.nextcloud_server = hs.config.nextcloud_server
self.service_account_name = hs.config.service_account_name
self.service_account_password = hs.config.service_account_password
self.keycloak_access_token = ""
async def delete_room_mapping_with_nextcloud_directory(self, room_id, requester_id):
""" Delete a mapping between a room and an Nextcloud folder.
Args :
room_id: the id of the room.
requester_id: the user_id of the requester.
"""
directory_path = await self.store.get_nextcloud_directory_path_from_roomID(
room_id
)
try:
self.keycloak_access_token = await self.get_keycloak_access_token()
except HTTPError:
raise SynapseError(
400,
"Unable to retrieve the Keycloak access token of realm {}".format(
self.keycloak_realm
),
)
try:
nextcloud_username = await self.get_nextcloud_username(
get_localpart_from_id(requester_id)
)
except HTTPError:
raise SynapseError(
400,
"Unable to retrieve the corresponding Nextcloud username of user {}.".format(
requester_id
),
)
try:
all_shares = await self.get_sharing_of_nextcloud_directory(
nextcloud_username, directory_path
)
except HTTPError as e:
if e.response.status_code == 404:
raise SynapseError(
400,
"The user {} doesn't have the access right to the folder {}.".format(
requester_id, directory_path
),
Codes.NEXTCLOUD_FOLDER_ACCESS_FORBIDDEN,
)
raise SynapseError(
400,
"Unable to get shares on the folder {}.".format(
directory_path
),
)
group_share_id = ""
for share in all_shares:
if share["share_with"] == room_id:
group_share_id = share["id"]
break
if not group_share_id:
raise SynapseError(
400,
"Unable to retrieve share id between Watcha room {} and Nextcloud directory {}".format(
room_id, directory_path
),
)
try:
await self.delete_existing_nextcloud_share(nextcloud_username, group_share_id)
except HTTPError:
raise SynapseError(
400,
"Unable to delete the share on the nextcloud folder {} for the nextcloud group {}.".format(
room_id, directory_path
),
)
await self.store.deleted_room_mapping_with_nextcloud_directory(room_id)
async def add_room_mapping_with_nextcloud_directory(
self, room_id, requester_id, nextcloud_URL
):
""" Add a mapping between a room and an Nextcloud folder.
Args :
room_id: the id of the room which must be linked with the Nextcloud folder.
requester_id: the user_id of the requester.
nextcloud_URL: an URL pointing on the Nextcloud folder to link with the room.
"""
nextcloud_URL_query = parse_qs(urlparse(nextcloud_URL).query)
if "dir" not in nextcloud_URL_query:
raise SynapseError(400, "The url doesn't point to a valid directory path.")
nextcloud_directory_path = nextcloud_URL_query["dir"][0]
try:
self.keycloak_access_token = await self.get_keycloak_access_token()
except HTTPError:
raise SynapseError(
400,
"Unable to retrieve the Keycloak access token of realm {}".format(
self.keycloak_realm
),
)
try:
nextcloud_username = await self.get_nextcloud_username(
get_localpart_from_id(requester_id)
)
except HTTPError:
raise SynapseError(
400,
"Unable to retrieve the corresponding Nextcloud username of user {}.".format(
requester_id
),
)
try:
await self.get_sharing_of_nextcloud_directory(
nextcloud_username, nextcloud_directory_path
)
except HTTPError as e:
if e.response.status_code == 404:
raise SynapseError(
400,
"The user {} doesn't have the access right to the folder {}.".format(
requester_id, nextcloud_directory_path
),
Codes.NEXTCLOUD_FOLDER_ACCESS_FORBIDDEN,
)
raise SynapseError(
400,
"Unable to get shares on the folder {}.".format(
nextcloud_directory_path
),
)
try:
group_exists = await self.nextcloud_room_group_exists(room_id)
except HTTPError:
raise SynapseError(
400,
"Unable to retrieve to know if the nextcloud group {} exists or not.".format(
room_id
),
)
if not group_exists:
try:
await self.create_nextcloud_group(room_id)
except HTTPError:
raise SynapseError(
400, "Unable to create the Nextcloud group {}.".format(room_id)
)
try:
await self.create_new_nextcloud_share(
nextcloud_username, nextcloud_directory_path, room_id
)
except HTTPError:
raise SynapseError(
400,
"Unable to create a share for the nextcloud group {} on the nextcloud folder {}.".format(
room_id, nextcloud_directory_path
),
)
await self.store.set_room_mapping_with_nextcloud_directory(
room_id, nextcloud_directory_path
)
async def get_nextcloud_username(self, user_localpart):
""" Get the corresponding Nextcloud username of the synapse user from Keycloak.
Args :
user_localpart: the synapse user localpart.
Returns:
The Nextcloud username.
"""
request = get(
"{}/admin/realms/{}/users".format(self.keycloak_server, self.keycloak_realm),
headers={"Authorization": "Bearer {}".format(self.keycloak_access_token)},
params={"username": user_localpart},
)
request.raise_for_status()
return request.json()[0]["id"]
async def get_keycloak_access_token(self):
""" Get the realm Keycloak access token in order to use Keycloak Admin API.
Returns:
The realm Keycloak access token.
"""
request = post(
"{}/realms/{}/protocol/openid-connect/token".format(
self.keycloak_server, self.keycloak_realm
),
data={
"client_id": "admin-cli",
"username": self.service_account_name,
"password": self.service_account_password,
"grant_type": "password",
},
)
request.raise_for_status()
return request.json()["access_token"]
async def get_sharing_of_nextcloud_directory(self, username, directory_path):
""" Get share for the requester and all reshares on the folder.
Args:
username: the Nextcloud username of the requester.
directory: the directory path of the folder concerned by the share search.
Returns:
A list which contains all shares on the folder.
Each share is a dict that contains lot of information about the share (id, share_with, owner_uid...)
Raises:
HTTPError 401 : Wrong Basic Auth.
HTTPError 404 : Couldn't fetch shares. Most likely due to the fact that the user doesn't have a share on the folder or the folder doesn't exists.
"""
request = get(
"{}/ocs/v2.php/apps/files_sharing/api/v1/shares".format(self.nextcloud_server),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(username, self.nextcloud_shared_secret),
params={"path": directory_path, "reshares": "true", "format": "json"},
)
request.raise_for_status()
return request.json()["ocs"]["data"]
async def nextcloud_room_group_exists(self, group_name):
""" Ask Nextcloud if the group name exist or not.
Args:
group_name: the name of Nextcloud group.
Returns:
True if the group exists, else False.
"""
request = get(
"{}/ocs/v1.php/cloud/groups".format(self.nextcloud_server),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(self.service_account_name, self.service_account_password),
params={"search": group_name, "format": "json"},
)
request.raise_for_status()
response = request.json()["ocs"]["data"]
return (
True
if "groups" in response and len(response["groups"]) > 0
else False
)
async def create_nextcloud_group(self, room_id):
""" Create an Nextcloud group named as room_id and add all users in the room into the new Nextcloud group.
Args:
room_id: the room_id of the room which Nextcloud directory is linked.
"""
request = post(
"{}/ocs/v1.php/cloud/groups".format(self.nextcloud_server),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(self.service_account_name, self.service_account_password),
data={"groupid": room_id, "format": "json"},
)
request.raise_for_status()
users = await self.store.get_users_in_room(room_id)
for user in users:
try:
nextcloud_username = await self.get_nextcloud_username(
get_localpart_from_id(user)
)
await self.add_user_to_nextcloud_groups(nextcloud_username, room_id)
except HTTPError:
logger.warn(
"An error occured during the addition of the user {} in the Nextcloud group {}.".format(
user, room_id
)
)
continue
async def add_user_to_nextcloud_groups(self, username, group_name):
""" Add user to the Nextcloud group named as room_id.
Args:
username: the room_id of the room which Nextcloud directory is linked.
group_name: the Nextcloud group name, equivalent to room_id of the room linked.
"""
request = post(
"{}/ocs/v1.php/cloud/users/{}/groups".format(self.nextcloud_server, username),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(self.service_account_name, self.service_account_password),
data={"groupid": group_name, "format": "json"},
)
request.raise_for_status()
async def create_new_nextcloud_share(self, requester, directory_path, group_name):
""" Create a share on Nextcloud folder for the specified Nextcloud group.
Post arguments:
permission = 31 -> give all right on the folder (read, update, create, deleted and share)
shareType = 1 -> a group share
Args:
requester: the Nextcloud username of the requester who want to create the new share.
directory_path: the path of the folder to share.
group_name: the Nextcloud group id.
"""
request = post(
"{}/ocs/v2.php/apps/files_sharing/api/v1/shares".format(self.nextcloud_server),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(requester, self.service_account_password),
data={
"path": directory_path,
"shareType": 1,
"shareWith": group_name,
"permissions": 31,
"format": "json",
},
)
request.raise_for_status()
async def delete_existing_nextcloud_share(self, requester, share_id):
""" Delete an existing share (corresponding to the share id) on the folder.
Args:
requester: the Nextcloud username of the requester who want to delete an existing share.
share_id: the id of the share to delete.
"""
request = delete(
"{}/ocs/v2.php/apps/files_sharing/api/v1/shares/{}".format(
self.nextcloud_server, share_id
),
headers={"OCS-APIRequest": "true"},
auth=HTTPBasicAuth(requester, self.service_account_password),
)
request.raise_for_status()
async def get_room_list_to_send_NC_notification(
self, directory, limit_of_notification_propagation
):
@ -1428,7 +1801,7 @@ class WatchaRoomHandler(BaseHandler):
directories.append(directory)
for directory in directories:
room = await self.store.get_room_to_send_NC_notification(directory)
room = await self.store.get_roomID_from_nextcloud_directory_path(directory)
if room:
rooms.append(room)
@ -1444,7 +1817,9 @@ class WatchaRoomHandler(BaseHandler):
result = await self.store.get_room_creator(room_id)
return result
async def send_NC_notification_to_rooms(self, rooms, file_name, file_url, file_operation):
async def send_NC_notification_to_rooms(
self, rooms, file_name, file_url, file_operation
):
content = {
"body": file_operation,

@ -167,7 +167,9 @@ class RoomStateEventRestServlet(TransactionRestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
format = parse_string(
request, "format", default="content", allowed_values=["content", "event"]
)
@ -267,6 +269,24 @@ class RoomStateEventRestServlet(TransactionRestServlet):
logger.info("GuestAccessEvent. Original content=" + str(content))
content["guest_access"] = "forbidden"
logger.info("GuestAccessEvent. New content=" + str(content))
if event_type == EventTypes.VectorSetting:
if "nextcloud" not in content:
raise SynapseError(
400, "VectorSetting is only used for Nextcloud integration."
)
nextcloud_URL = content["nextcloud"]
requester_id = requester.user.to_string()
if not nextcloud_URL:
await self.handlers.watcha_room_handler.delete_room_mapping_with_nextcloud_directory(
room_id, requester_id
)
else:
await self.handlers.watcha_room_handler.add_room_mapping_with_nextcloud_directory(
room_id, requester_id, nextcloud_URL
)
# +watcha
(
event,
@ -299,7 +319,9 @@ class RoomSendEventRestServlet(TransactionRestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
content = parse_json_object_from_request(request)
event_dict = {
@ -353,7 +375,9 @@ class JoinRoomAliasServlet(TransactionRestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
try:
content = parse_json_object_from_request(request)
@ -599,7 +623,9 @@ class RoomMessageListRestServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
pagination_config = PaginationConfig.from_request(request, default_limit=10)
as_client_event = b"raw" not in request.args
filter_str = parse_string(request, b"filter", encoding="utf-8")
@ -641,13 +667,15 @@ class RoomStateRestServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
# Get all the current state for this room
events = await self.message_handler.get_state_events(
room_id=room_id,
user_id=requester.user.to_string(),
is_guest=requester.is_guest,
is_partner=requester.is_partner, # watcha+
is_partner=requester.is_partner, # watcha+
)
return 200, events
@ -665,7 +693,9 @@ class RoomInitialSyncRestServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
pagination_config = PaginationConfig.from_request(request)
content = await self.initial_sync_handler.room_initial_sync(
room_id=room_id, requester=requester, pagin_config=pagination_config
@ -721,7 +751,9 @@ class RoomEventContextServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
limit = parse_integer(request, "limit", default=10)
@ -790,7 +822,7 @@ class RoomMembershipRestServlet(TransactionRestServlet):
super(RoomMembershipRestServlet, self).__init__(hs)
self.room_member_handler = hs.get_room_member_handler()
self.auth = hs.get_auth()
self.handlers = hs.get_handlers() # watcha+
self.handlers = hs.get_handlers() # watcha+
def register(self, http_server):
# /rooms/$roomid/[invite|join|leave]
@ -811,7 +843,9 @@ class RoomMembershipRestServlet(TransactionRestServlet):
raise AuthError(403, "Guest access not allowed")
!watcha """
# watcha+
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True)
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
)
if (requester.is_guest or requester.is_partner) and membership_action not in {
Membership.JOIN,
@ -850,17 +884,24 @@ class RoomMembershipRestServlet(TransactionRestServlet):
!watcha """
# watcha+
if membership_action == "invite" and self._has_3pid_invite_keys(content):
logger.info("invitation: inviter id=%s, device_id=%s",
requester.user, requester.device_id)
logger.info(
"invitation: inviter id=%s, device_id=%s",
requester.user,
requester.device_id,
)
content["user_id"] = await self.handlers.invite_external_handler.invite(
room_id=room_id,
inviter=requester.user,
inviter_device_id=str(requester.device_id),
invitee=content["address"]
invitee=content["address"],
)
logger.info(
"invitee email=%s has been invited as %s in room_id=%s",
content["address"],
content["user_id"],
room_id,
)
logger.info("invitee email=%s has been invited as %s in room_id=%s",
content["address"], content["user_id"], room_id)
# +watcha
target = requester.user
@ -973,7 +1014,9 @@ class RoomTypingRestServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_partner=True
) # watcha+
if not self._is_typing_writer:
raise Exception("Got /typing request on instance that is not typing writer")
@ -1063,7 +1106,9 @@ class JoinedRoomsRestServlet(RestServlet):
""" watcha!
requester = await self.auth.get_user_by_req(request, allow_guest=True)
!watcha """
requester = await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=True) # watcha+
requester = await self.auth.get_user_by_req(
request, allow_guest=True, allow_partner=True
) # watcha+
room_ids = await self.store.get_rooms_for_user(requester.user.to_string())
return 200, {"joined_rooms": list(room_ids)}

@ -18,7 +18,6 @@ import itertools
import logging
from collections import OrderedDict, namedtuple
from typing import TYPE_CHECKING, Dict, Iterable, List, Set, Tuple
from urllib.parse import parse_qs, urlparse # watcha+ op486
import attr
from prometheus_client import Counter
@ -917,10 +916,6 @@ class PersistEventsStore:
elif event.type == EventTypes.Retention:
# Update the room_retention table.
self._store_retention_policy_for_room_txn(txn, event)
# watcha+ op433
elif event.type == EventTypes.VectorSetting:
self._store_room_link_with_NC(txn, event)
# +watcha
self._handle_event_relations(txn, event)
@ -1258,55 +1253,6 @@ class PersistEventsStore:
txn, self.store.get_retention_policy_for_room, (event.room_id,)
)
# watcha+ op433
def _store_room_link_with_NC(self, txn, event):
""" Store the link between Watcha room and Nextcloud folder.
"""
if not hasattr(event, "content") or "nextcloud" not in event.content:
raise SynapseError(
400, "The nextcloud url is needed to store room link with NC."
)
room_id = event.room_id
nextcloud_folder_url = event.content["nextcloud"]
if not nextcloud_folder_url:
sql = """
DELETE FROM room_mapping_with_NC
WHERE room_id = ?
"""
txn.execute(sql, (room_id,))
return
nextcloud_url_query = parse_qs(urlparse(nextcloud_folder_url).query)
if "dir" not in nextcloud_url_query:
raise SynapseError(400, "The url doesn't point to a valid directory path.")
nextcloud_directory_path = nextcloud_url_query["dir"][0]
linked_room = self.db_pool.simple_select_one_onecol_txn(
txn,
table="room_mapping_with_NC",
keyvalues={"directory_path": nextcloud_directory_path,},
retcol="room_id",
allow_none=True,
)
if linked_room:
raise StoreError(
500, "This Nextcloud folder is already linked with another room."
)
self.db_pool.simple_upsert_txn(
txn,
table="room_mapping_with_NC",
keyvalues={"room_id": room_id},
values={"room_id": room_id, "directory_path": nextcloud_directory_path,},
)
# +watcha
def store_event_search_txn(self, txn, event, key, value):
"""Add event to the search table

@ -1437,8 +1437,20 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
return rooms
# watcha+
async def get_room_to_send_NC_notification(self, directory_path):
""" Get the room_id of the room which is linked with the Nextcloud folder url.
async def get_nextcloud_directory_path_from_roomID(self, room_id):
""" Get Nextcloud directory path which is mapped with room_id.
"""
return await self.db_pool.simple_select_one_onecol(
table="room_mapping_with_NC",
keyvalues={"room_id": room_id},
retcol="directory_path",
allow_none=True,
desc="get_nextcloud_directory_path_from_roomID",
)
async def get_roomID_from_nextcloud_directory_path(self, directory_path):
""" Get the room_id mapped with Nextcloud directory path.
"""
return await self.db_pool.simple_select_one_onecol(
@ -1446,7 +1458,7 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
keyvalues={"directory_path": directory_path},
retcol="room_id",
allow_none=True,
desc="get_room_to_send_NC_notification",
desc="get_roomID_from_nextcloud_directory_path",
)
async def get_room_creator(self, room_id):
@ -1468,4 +1480,25 @@ class RoomStore(RoomBackgroundUpdateStore, RoomWorkerStore, SearchStore):
)
return first_room_admin[0]
async def set_room_mapping_with_nextcloud_directory(self, room_id, directory_path):
""" Set mapping between Watcha room and Nextcloud directory.
"""
self.db_pool.simple_upsert(
table="room_mapping_with_NC",
keyvalues={"room_id": room_id},
values={"room_id": room_id, "directory_path": directory_path,},
desc="set_room_mapping_with_nextcloud_directory",
)
async def deleted_room_mapping_with_nextcloud_directory(self, room_id):
""" Delete mapping between Watcha room and Nextcloud directory for room_id.
"""
self.db_pool.simple_delete(
table="room_mapping_with_NC",
keyvalues={"room_id": room_id},
desc="deleted_room_mapping_with_nextcloud_directory",
)
# +watcha

@ -721,3 +721,6 @@ class WatchaSendNextcloudActivityToWatchaRoomServletTestCase(
},
],
)
test_propagate_notification_in_rooms.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
test_send_notification_for_basic_file_operations_in_parent_directory.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"

@ -307,4 +307,10 @@ class WatchaRoomEventsStoreTestCase(unittest.HomeserverTestCase):
self.assertEquals(
e.exception.msg, "The url doesn't point to a valid directory path."
)
test_send_room_mapping_event_without_nextcloud_content.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
test_set_NC_directory_path_with_wrong_url_query.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
test_set_same_NC_directory_path.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
test_get_NC_directory_path.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
test_update_NC_directory_path.skip = "Disabled in order to build OP544 and OP545 branch to welcome.watcha.fr"
# +watcha

Loading…
Cancel
Save