diff --git a/synapse/api/errors.py b/synapse/api/errors.py index 56db6dd86..b75da0a44 100644 --- a/synapse/api/errors.py +++ b/synapse/api/errors.py @@ -77,18 +77,10 @@ class Codes: USER_DEACTIVATED = "M_USER_DEACTIVATED" BAD_ALIAS = "M_BAD_ALIAS" # watcha+ - NEXTCLOUD_USER_DOES_NOT_EXIST = "W_NEXTCLOUD_USER_DOES_NOT_EXIST" - NEXTCLOUD_CAN_NOT_CREATE_USER = "W_NEXTCLOUD_CAN_NOT_CREATE_USER" - NEXTCLOUD_CAN_NOT_DELETE_USER = "W_NEXTCLOUD_CAN_NOT_DELETE_USER" + NEXTCLOUD_CAN_NOT_ADD_MEMBERS_TO_GROUP = "W_NEXTCLOUD_CAN_NOT_ADD_MEMBERS_TO_GROUP" NEXTCLOUD_CAN_NOT_CREATE_GROUP = "W_NEXTCLOUD_CAN_NOT_CREATE_GROUP" - NEXTCLOUD_CAN_NOT_DELETE_GROUP = "W_NEXTCLOUD_CAN_NOT_DELETE_GROUP" - NEXTCLOUD_CAN_NOT_ADD_USER_TO_GROUP = "W_NEXTCLOUD_CAN_NOT_ADD_USER_TO_GROUP" - NEXTCLOUD_CAN_NOT_REMOVE_USER_FROM_GROUP = ( - "W_NEXTCLOUD_CAN_NOT_REMOVE_USER_FROM_GROUP" - ) - NEXTCLOUD_CAN_NOT_GET_SHARES = "W_NEXTCLOUD_CAN_NOT_GET_SHARES" NEXTCLOUD_CAN_NOT_SHARE = "W_NEXTCLOUD_CAN_NOT_SHARE" - NEXTCLOUD_CAN_NOT_UNSHARE = "W_NEXTCLOUD_CAN_NOT_UNSHARE" + # +watcha @@ -159,6 +151,25 @@ class SynapseError(CodeMessageException): return cs_error(self.msg, self.errcode) +# watcha+ +class NextcloudError(CodeMessageException): + """A base exception type for Nextcloud errors which have an error code and error + message (corresponding to status code and status message from Nextcloud API documentation). + """ + + def __init__(self, code: int, msg: str): + """Constructs a Nextcloud error. + + Args: + code: The integer error code (corresponding to status code in response body) + msg: The human-readable error message. + """ + super().__init__(code, msg) + + +# +watcha + + class ProxiedRequestError(SynapseError): """An error from a general matrix endpoint, eg. from a proxied Matrix API call. diff --git a/synapse/handlers/watcha_nextcloud.py b/synapse/handlers/watcha_nextcloud.py index fdf40029b..88fe74c76 100644 --- a/synapse/handlers/watcha_nextcloud.py +++ b/synapse/handlers/watcha_nextcloud.py @@ -1,10 +1,13 @@ import logging -from pathlib import Path from jsonschema.exceptions import SchemaError, ValidationError -from synapse.api.constants import EventTypes -from synapse.api.errors import Codes, SynapseError +from synapse.api.errors import ( + Codes, + HttpResponseException, + NextcloudError, + SynapseError, +) from ._base import BaseHandler @@ -12,54 +15,70 @@ logger = logging.getLogger(__name__) # echo -n watcha | md5sum | head -c 10 NEXTCLOUD_GROUP_NAME_PREFIX = "c4d96a06b7_" +NEXTCLOUD_CLIENT_ERRORS = ( + NextcloudError, + SchemaError, + ValidationError, + HttpResponseException, +) class NextcloudHandler(BaseHandler): - def __init__(self, hs): + def __init__(self, hs: "Homeserver"): self.store = hs.get_datastore() self.event_creation_handler = hs.get_event_creation_handler() self.keycloak_client = hs.get_keycloak_client() self.nextcloud_client = hs.get_nextcloud_client() - async def unbind(self, room_id): + async def unbind(self, room_id: str): """Unbind a Nextcloud folder from a room. Args : room_id: the id of the room to bind. """ + group_name = NEXTCLOUD_GROUP_NAME_PREFIX + room_id + try: + await self.nextcloud_client.delete_group(group_name) + except NEXTCLOUD_CLIENT_ERRORS as error: + logger.error( + f"[watcha] delete nextcloud group {group_name} - failed: {error}" + ) - await self.nextcloud_client.delete_group(NEXTCLOUD_GROUP_NAME_PREFIX + room_id) - - await self.store.unbind(room_id) + await self.store.delete_share(room_id) - async def bind(self, user_id, room_id, path): - """Bind a Nextcloud folder with a room. + async def bind(self, requester_id: str, room_id: str, path: str): + """Bind a Nextcloud folder with a room in three steps : + 1 - create a new Nextcloud group + 2 - add all room members in the new group + 3 - create a share on folder for the new group Args : - user_id: the matrix user id of the requester. + requester_id: the mxid of the requester. room_id: the id of the room to bind. path: the path of the Nextcloud folder to bind. """ group_name = NEXTCLOUD_GROUP_NAME_PREFIX + room_id - nextcloud_username = await self.store.get_nextcloud_username(user_id) - - await self.nextcloud_client.add_group(group_name) - - await self.add_room_users_to_nextcloud_group(room_id) - - old_share_id = await self.store.get_nextcloud_share_id_from_room_id(room_id) - - if old_share_id: - await self.nextcloud_client.unshare(nextcloud_username, old_share_id) - new_share_id = await self.nextcloud_client.share( - nextcloud_username, path, group_name - ) + try: + await self.nextcloud_client.add_group(group_name) + except NEXTCLOUD_CLIENT_ERRORS as error: + # Do not raise error if Nextcloud group already exist + if isinstance(error, NextcloudError) and error.code == 102: + logger.warn( + f"[watcha] add nextcloud group {group_name} - failed: the group already exists" + ) + else: + raise SynapseError( + 500, + f"[watcha] add nextcloud group {group_name} - failed: {error}", + Codes.NEXTCLOUD_CAN_NOT_CREATE_GROUP, + ) - await self.store.bind(room_id, path, new_share_id) + await self.add_room_members_to_group(room_id) + await self.create_share(requester_id, room_id, path) - async def add_room_users_to_nextcloud_group(self, room_id): - """Add all users of a room to a Nextcloud. + async def add_room_members_to_group(self, room_id: str): + """Add all members of a room to a Nextcloud group. Args: room_id: the id of the room which the Nextcloud group name is infered from. @@ -68,42 +87,91 @@ class NextcloudHandler(BaseHandler): user_ids = await self.store.get_users_in_room(room_id) for user_id in user_ids: - nextcloud_username = await self.store.get_nextcloud_username(user_id) + nextcloud_username = await self.store.get_username(user_id) try: await self.nextcloud_client.add_user_to_group( nextcloud_username, group_name ) - except (SynapseError, ValidationError, SchemaError) as error: - logger.warn( - "Unable to add the user {} to the Nextcloud group {}.".format( - user_id, group_name + except NEXTCLOUD_CLIENT_ERRORS as error: + # Do not raise error if some users can not be added to group + if isinstance(error, NextcloudError) and (error.code in (103, 105)): + logger.error( + f"[watcha] add user {user_id} to group {group_name} - failed: {error}" ) - ) + else: + raise SynapseError( + 500, + f"[watcha] add members of room {room_id} to group {group_name} - failed: {error}", + Codes.NEXTCLOUD_CAN_NOT_ADD_MEMBERS_TO_GROUP, + ) + + async def create_share(self, requester_id: str, room_id: str, path: str): + """Create a new share on folder for a specific Nextcloud group. + Before that, delete old existing share for this group if it exist. - async def update_share(self, user_id, room_id, membership): + Args: + requester_id: the mxid of the requester. + room_id: the id of the room to bind. + path: the path of the Nextcloud folder to bind. + """ + group_name = NEXTCLOUD_GROUP_NAME_PREFIX + room_id + nextcloud_username = await self.store.get_username(requester_id) + old_share_id = await self.store.get_share_id(room_id) + if old_share_id: + try: + await self.nextcloud_client.unshare(nextcloud_username, old_share_id) + except NEXTCLOUD_CLIENT_ERRORS as error: + logger.error(f"[watcha] unshare {old_share_id} - failed: {error}") + + try: + new_share_id = await self.nextcloud_client.share( + nextcloud_username, path, group_name + ) + except NEXTCLOUD_CLIENT_ERRORS as error: + await self.unbind(room_id) + # raise 404 error if folder to share do not exist + http_code = ( + error.code + if isinstance(error, NextcloudError) and error.code == 404 + else 500 + ) + raise SynapseError( + http_code, + f"[watcha] share folder {path} with group {group_name} - failed: {error}", + Codes.NEXTCLOUD_CAN_NOT_SHARE, + ) + + await self.store.register_share(room_id, new_share_id) + + async def update_group(self, user_id: str, room_id: str, membership: str): + """Update a Nextcloud group by adding or removing users. + If membership is 'join' or 'invite', the user is add to the Nextcloud group infered from the room. + Else, the users is removed from the group. + + Args: + user_id: mxid of the user concerned by the membership event + room_id: the id of the room where the membership event was sent + membership: membership event. Can be 'invite', 'join', 'kick' or 'leave' + """ group_name = NEXTCLOUD_GROUP_NAME_PREFIX + room_id - nextcloud_username = await self.store.get_nextcloud_username(user_id) + nextcloud_username = await self.store.get_username(user_id) if membership in ("invite", "join"): try: await self.nextcloud_client.add_user_to_group( nextcloud_username, group_name ) - except (SynapseError, ValidationError, SchemaError): + except NEXTCLOUD_CLIENT_ERRORS as error: logger.warn( - "Unable to add the user {} to the Nextcloud group {}.".format( - user_id, group_name - ), + f"[watcha] add user {user_id} to group {group_name} - failed: {error}" ) else: try: await self.nextcloud_client.remove_user_from_group( nextcloud_username, group_name ) - except (SynapseError, ValidationError, SchemaError): + except NEXTCLOUD_CLIENT_ERRORS as error: logger.warn( - "Unable to remove the user {} from the Nextcloud group {}.".format( - user_id, group_name - ), + f"[watcha] remove user {user_id} from group {group_name} - failed: {error}" ) diff --git a/synapse/handlers/watcha_partner.py b/synapse/handlers/watcha_partner.py index cb488d3d7..a94cc6ff5 100644 --- a/synapse/handlers/watcha_partner.py +++ b/synapse/handlers/watcha_partner.py @@ -2,7 +2,7 @@ import logging from jsonschema.exceptions import SchemaError, ValidationError -from synapse.api.errors import HttpResponseException, SynapseError +from synapse.api.errors import HttpResponseException, NextcloudError from synapse.config.emailconfig import ThreepidBehaviour from synapse.push.mailer import Mailer from synapse.util.watcha import Secrets @@ -48,9 +48,17 @@ class PartnerHandler(BaseHandler): try: await self.nextcloud_client.add_user(keycloak_user_id) - except (SynapseError, HttpResponseException, ValidationError, SchemaError): - await self.keycloak_client.delete_user(keycloak_user_id) - raise + except ( + NextcloudError, + HttpResponseException, + ValidationError, + SchemaError, + ) as error: + if isinstance(error, NextcloudError) and error.code == 102: + logger.warn(f"[watcha] add user {keycloak_user_id} - failed: {error}") + else: + await self.keycloak_client.delete_user(keycloak_user_id) + raise invitee_id = await self.registration_handler.register_user( localpart=keycloak_user_id, diff --git a/synapse/http/watcha_nextcloud_client.py b/synapse/http/watcha_nextcloud_client.py index db17d7c3b..6fba82588 100644 --- a/synapse/http/watcha_nextcloud_client.py +++ b/synapse/http/watcha_nextcloud_client.py @@ -3,7 +3,7 @@ from base64 import b64encode from jsonschema import validate from typing import Any, List -from synapse.api.errors import Codes, SynapseError +from synapse.api.errors import Codes, NextcloudError from synapse.http.client import SimpleHttpClient logger = logging.getLogger(__name__) @@ -64,7 +64,7 @@ class NextcloudClient(SimpleHttpClient): https://docs.nextcloud.com/server/latest/developer_manual/client_apis/index.html """ - def __init__(self, hs): + def __init__(self, hs: "Homeserver"): super().__init__(hs) self.nextcloud_url = hs.config.nextcloud_url @@ -79,21 +79,16 @@ class NextcloudClient(SimpleHttpClient): "Authorization": [ "Basic " + b64encode( - "{}:{}".format( - self.service_account_name, self.service_account_password - ).encode() + f"{self.service_account_name}:{self.service_account_password}".encode() ).decode() ], } def _raise_for_status(self, meta: List[Any], errcode: Codes): if meta["status"] == "failure": - raise SynapseError( - 400, - "OCS error : status code {status_code} - message {msg}".format( - status_code=meta["statuscode"], msg=meta["message"] - ), - errcode, + raise NextcloudError( + meta["statuscode"], + meta["message"], ) async def add_user(self, username: str, displayname: str = None): @@ -101,7 +96,7 @@ class NextcloudClient(SimpleHttpClient): Args: username: the username of the user to create. - displayname: displayname of invitee. Defaults to user keycloak id. + displayname: displayname of the user. Defaults to user keycloak id. Status codes: 100 - successful @@ -120,20 +115,13 @@ class NextcloudClient(SimpleHttpClient): payload["displayName"] = displayname response = await self.post_json_get_json( - uri="{}/ocs/v1.php/cloud/users".format(self.nextcloud_url), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/users", post_json=payload, headers=self._headers, ) - + validate(response, WITH_DATA_SCHEMA) - meta = response["ocs"]["meta"] - - if meta["statuscode"] == 102: - logger.info( - "User '{}' already exists on the Nextcloud server.".format(username) - ) - else: - self._raise_for_status(meta, Codes.NEXTCLOUD_CAN_NOT_CREATE_USER) + self._raise_for_status(response["ocs"]["meta"]) async def delete_user(self, username: str): """Delete an existing user. @@ -145,17 +133,13 @@ class NextcloudClient(SimpleHttpClient): 100 - successful 101 - failure """ - response = await self.delete_get_json( - uri="{}/ocs/v1.php/cloud/users/{}".format(self.nextcloud_url, username), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/users/{username}", headers=self._headers, ) validate(response, WITHOUT_DATA_SCHEMA) - - self._raise_for_status( - response["ocs"]["meta"], Codes.NEXTCLOUD_CAN_NOT_DELETE_USER - ) + self._raise_for_status(response["ocs"]["meta"]) async def add_group(self, group_name: str): """Adds a new Nextcloud group. @@ -169,20 +153,14 @@ class NextcloudClient(SimpleHttpClient): 102: group already exists 103: failed to add the group """ - response = await self.post_json_get_json( - uri="{}/ocs/v1.php/cloud/groups".format(self.nextcloud_url), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/groups", post_json={"groupid": group_name}, headers=self._headers, ) validate(response, WITHOUT_DATA_SCHEMA) - meta = response["ocs"]["meta"] - - if meta["statuscode"] == 102: - logger.info("Nextcloud group {} already exists.".format(group_name)) - else: - self._raise_for_status(meta, Codes.NEXTCLOUD_CAN_NOT_CREATE_GROUP) + self._raise_for_status(response["ocs"]["meta"]) async def delete_group(self, group_name: str): """Removes a existing Nextcloud group. @@ -195,17 +173,13 @@ class NextcloudClient(SimpleHttpClient): 101: group does not exist 102: failed to delete group """ - response = await self.delete_get_json( - uri="{}/ocs/v1.php/cloud/groups/{}".format(self.nextcloud_url, group_name), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/groups/{group_name}", headers=self._headers, ) validate(response, WITHOUT_DATA_SCHEMA) - - self._raise_for_status( - response["ocs"]["meta"], Codes.NEXTCLOUD_CAN_NOT_DELETE_GROUP - ) + self._raise_for_status(response["ocs"]["meta"]) async def add_user_to_group(self, username: str, group_name: str): """Add user to the Nextcloud group. @@ -222,25 +196,14 @@ class NextcloudClient(SimpleHttpClient): 104: insufficient privileges 105: failed to add user to group """ - response = await self.post_json_get_json( - uri="{}/ocs/v1.php/cloud/users/{}/groups".format( - self.nextcloud_url, username - ), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/users/{username}/groups", post_json={"groupid": group_name}, headers=self._headers, ) validate(response, WITHOUT_DATA_SCHEMA) - meta = response["ocs"]["meta"] - - errcode = ( - Codes.NEXTCLOUD_USER_DOES_NOT_EXIST - if meta["statuscode"] == 103 - else Codes.NEXTCLOUD_CAN_NOT_ADD_USER_TO_GROUP - ) - - self._raise_for_status(meta, errcode) + self._raise_for_status(response["ocs"]["meta"]) async def remove_user_from_group(self, username: str, group_name: str): """Removes the specified user from the specified group. @@ -257,32 +220,21 @@ class NextcloudClient(SimpleHttpClient): 104: insufficient privileges 105: failed to remove user from group """ - response = await self.delete_get_json( - uri="{}/ocs/v1.php/cloud/users/{}/groups".format( - self.nextcloud_url, username - ), + uri=f"{self.nextcloud_url}/ocs/v1.php/cloud/users/{username}/groups", headers=self._headers, json_body={"groupid": group_name}, ) validate(response, WITHOUT_DATA_SCHEMA) - meta = response["ocs"]["meta"] - - errcode = ( - Codes.NEXTCLOUD_USER_DOES_NOT_EXIST - if meta["statuscode"] == 103 - else Codes.NEXTCLOUD_CAN_NOT_REMOVE_USER_FROM_GROUP - ) - - self._raise_for_status(meta, errcode) + self._raise_for_status(response["ocs"]["meta"]) async def share(self, requester: str, path: str, group_name: str): """Share an existing file or folder with all permissions for a group. Args: requester: the user who want to create the share - path: the path of folder to share + path: the path of the folder to share group_name: the name of group which will share the folder Payload: @@ -310,11 +262,8 @@ class NextcloudClient(SimpleHttpClient): Returns: the id of Nextcloud share """ - response = await self.post_json_get_json( - uri="{}/ocs/v2.php/apps/watcha_integrator/api/v1/shares".format( - self.nextcloud_url - ), + uri=f"{self.nextcloud_url}/ocs/v2.php/apps/watcha_integrator/api/v1/shares", headers=self._headers, post_json={ "path": path, @@ -326,8 +275,7 @@ class NextcloudClient(SimpleHttpClient): ) validate(response, WITH_DATA_SCHEMA) - - self._raise_for_status(response["ocs"]["meta"], Codes.NEXTCLOUD_CAN_NOT_SHARE) + self._raise_for_status(response["ocs"]["meta"]) return response["ocs"]["data"]["id"] @@ -342,15 +290,11 @@ class NextcloudClient(SimpleHttpClient): 100: successful 404: Share couldn’t be deleted. """ - response = await self.delete_get_json( - uri="{}/ocs/v2.php/apps/watcha_integrator/api/v1/shares/{}".format( - self.nextcloud_url, share_id - ), + uri=f"{self.nextcloud_url}/ocs/v2.php/apps/watcha_integrator/api/v1/shares/{share_id}", headers=self._headers, json_body={"requester": requester}, ) validate(response, WITHOUT_DATA_SCHEMA) - - self._raise_for_status(response["ocs"]["meta"], Codes.NEXTCLOUD_CAN_NOT_UNSHARE) + self._raise_for_status(response["ocs"]["meta"]) diff --git a/synapse/rest/client/v1/room.py b/synapse/rest/client/v1/room.py index 17539aef5..515a10a45 100644 --- a/synapse/rest/client/v1/room.py +++ b/synapse/rest/client/v1/room.py @@ -86,10 +86,12 @@ class RoomCreateRestServlet(TransactionRestServlet): return self.txns.fetch_or_execute_request(request, self.on_POST, request) async def on_POST(self, request): - """ watcha! + """watcha! requester = await self.auth.get_user_by_req(request) - !watcha """ - requester = await self.auth.get_user_by_req(request, allow_partner=False) # watcha+ + !watcha""" + # watcha+ + requester = await self.auth.get_user_by_req(request, allow_partner=False) + # +watcha info, _ = await self._room_creation_handler.create_room( requester, self.get_room_config(request) @@ -208,10 +210,8 @@ class RoomStateEventRestServlet(TransactionRestServlet): content=content, ) # watcha+ - mapped_directory = await self.store.get_path_from_room_id( - room_id - ) - if mapped_directory and membership in [ + share_id = await self.store.get_share_id(room_id) + if share_id and membership in [ "invite", "join", "kick", @@ -222,25 +222,19 @@ class RoomStateEventRestServlet(TransactionRestServlet): if membership in ["join", "leave"] else state_key ) - await self.nextcloud_handler.update_share( - user, room_id, membership - ) + await self.nextcloud_handler.update_group(user, room_id, membership) # +watcha else: # watcha+ - if event_type == EventTypes.VectorSetting: - if "nextcloudShare" not in content: - raise SynapseError( - 400, "VectorSetting is only used for Nextcloud integration." - ) - + if ( + event_type == EventTypes.VectorSetting + and "nextcloudShare" in content + ): nextcloud_url = content["nextcloudShare"] requester_id = requester.user.to_string() if not nextcloud_url: - await self.nextcloud_handler.unbind( - room_id - ) + await self.nextcloud_handler.unbind(room_id) else: url_query = urlparse.parse_qs( urlparse.urlparse(nextcloud_url).query @@ -249,13 +243,13 @@ class RoomStateEventRestServlet(TransactionRestServlet): if "dir" not in url_query: raise SynapseError( 400, - "The url doesn't point to a valid nextcloud directory path.", + "[watcha] binding Nextcloud folder with room - failed: wrong folder path", ) - nextcloud_directory_path = url_query["dir"][0] + nextcloud_folder_path = url_query["dir"][0] await self.nextcloud_handler.bind( - requester_id, room_id, nextcloud_directory_path + requester_id, room_id, nextcloud_folder_path ) # +watcha ( @@ -380,11 +374,9 @@ class JoinRoomAliasServlet(TransactionRestServlet): ) # watcha+ - mapped_directory = await self.store.get_path_from_room_id( - room_id - ) - if mapped_directory: - await self.nextcloud_handler.update_share( + share_id = await self.store.get_share_id(room_id) + if share_id: + await self.nextcloud_handler.update_group( requester.user.to_string(), room_id, "join" ) # +watcha @@ -412,10 +404,14 @@ class PublicRoomListRestServlet(TransactionRestServlet): server = parse_string(request, "server", default=None) try: - """ watcha! + """watcha! await self.auth.get_user_by_req(request, allow_guest=True) - !watcha """ - await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=False) # watcha+ + !watcha""" + # watcha+ + await self.auth.get_user_by_req( + request, allow_guest=True, allow_partner=False + ) + # +watcha except InvalidClientCredentialsError as e: # Option to allow servers to require auth when accessing # /publicRooms via CS API. This is especially helpful in private @@ -464,10 +460,12 @@ class PublicRoomListRestServlet(TransactionRestServlet): return 200, data async def on_POST(self, request): - """ watcha! + """watcha! await self.auth.get_user_by_req(request, allow_guest=True) - !watcha """ - await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=False) # watcha+ + !watcha""" + # watcha+ + await self.auth.get_user_by_req(request, allow_guest=True, allow_partner=False) + # +watcha server = parse_string(request, "server", default=None) content = parse_json_object_from_request(request) @@ -802,7 +800,7 @@ class RoomMembershipRestServlet(TransactionRestServlet): self.store = hs.get_datastore() self.administration_handler = hs.get_administration_handler() self.nextcloud_handler = hs.get_nextcloud_handler() - self.partner_handler = hs.get_partner_handler() + self.partner_handler = hs.get_partner_handler() # +watcha def register(self, http_server): @@ -901,10 +899,8 @@ class RoomMembershipRestServlet(TransactionRestServlet): pass # watcha+ - mapped_directory = await self.store.get_path_from_room_id( - room_id - ) - if mapped_directory and membership_action in [ + share_id = await self.store.get_share_id(room_id) + if share_id and membership_action in [ "invite", "join", "kick", @@ -915,9 +911,7 @@ class RoomMembershipRestServlet(TransactionRestServlet): if membership_action in ["join", "leave"] else content["user_id"] ) - await self.nextcloud_handler.update_share( - user, room_id, membership_action - ) + await self.nextcloud_handler.update_group(user, room_id, membership_action) # +watcha return_value = {} diff --git a/synapse/rest/client/v1/watcha.py b/synapse/rest/client/v1/watcha.py index 5553840c0..47065dc58 100644 --- a/synapse/rest/client/v1/watcha.py +++ b/synapse/rest/client/v1/watcha.py @@ -2,7 +2,12 @@ import logging from jsonschema.exceptions import SchemaError, ValidationError -from synapse.api.errors import AuthError, HttpResponseException, SynapseError +from synapse.api.errors import ( + AuthError, + HttpResponseException, + SynapseError, + NextcloudError, +) from synapse.config.emailconfig import ThreepidBehaviour from synapse.http.servlet import RestServlet, parse_json_object_from_request from synapse.push.mailer import Mailer @@ -153,9 +158,17 @@ class WatchaRegisterRestServlet(RestServlet): try: await self.nextcloud_client.add_user(keycloak_user_id, displayname) - except (SynapseError, HttpResponseException, ValidationError, SchemaError): - await self.keycloak_client.delete_user(keycloak_user_id) - raise + except ( + NextcloudError, + HttpResponseException, + ValidationError, + SchemaError, + ) as error: + if isinstance(error, NextcloudError) and error.code == 102: + logger.warn(f"[watcha] add user {keycloak_user_id} - failed: {error}") + else: + await self.keycloak_client.delete_user(keycloak_user_id) + raise try: user_id = await self.registration_handler.register_user( diff --git a/synapse/storage/databases/main/schema/delta/59/watcha_01drop_directory_path_column.sql b/synapse/storage/databases/main/schema/delta/59/watcha_01drop_directory_path_column.sql new file mode 100644 index 000000000..35bfe9b74 --- /dev/null +++ b/synapse/storage/databases/main/schema/delta/59/watcha_01drop_directory_path_column.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS watcha_nextcloud_shares ( + room_id TEXT NOT NULL PRIMARY KEY, + share_id INTEGER +); + +INSERT INTO watcha_nextcloud_shares +SELECT DISTINCT + room_id, + share_id +FROM room_nextcloud_mapping; + +DROP TABLE room_nextcloud_mapping; \ No newline at end of file diff --git a/synapse/storage/databases/main/watcha_nextcloud.py b/synapse/storage/databases/main/watcha_nextcloud.py index 147799daf..2d6d2cfa2 100644 --- a/synapse/storage/databases/main/watcha_nextcloud.py +++ b/synapse/storage/databases/main/watcha_nextcloud.py @@ -3,55 +3,53 @@ from synapse.storage.database import DatabasePool class NextcloudStore(SQLBaseStore): - def __init__(self, database: DatabasePool, db_conn, hs): + def __init__(self, database: DatabasePool, db_conn, hs: "Homeserver"): super().__init__(database, db_conn, hs) - async def get_path_from_room_id(self, room_id): - """Get the Nextcloud folder path which is bound with room_id.""" - - return await self.db_pool.simple_select_one_onecol( - table="room_nextcloud_mapping", - keyvalues={"room_id": room_id}, - retcol="directory_path", - allow_none=True, - desc="get_path_from_room_id", - ) - - async def get_nextcloud_share_id_from_room_id(self, room_id): - """Get Nextcloud share id of the room id.""" + async def get_share_id(self, room_id: str): + """Get Nextcloud share id of a room. + Args: + room_id: id of the room + """ return await self.db_pool.simple_select_one_onecol( - table="room_nextcloud_mapping", + table="watcha_nextcloud_shares", keyvalues={"room_id": room_id}, retcol="share_id", allow_none=True, - desc="get_nextcloud_share_id_from_room_id", + desc="get_share_id", ) - async def bind(self, room_id, path, share_id): - """Bind a room with a Nextcloud folder.""" + async def register_share(self, room_id: str, share_id: str): + """Register a share between a room and a Nextcloud folder + Args: + room_id: id of the room + share_id: id of the Nextcloud share + """ await self.db_pool.simple_upsert( - table="room_nextcloud_mapping", + table="watcha_nextcloud_shares", keyvalues={"room_id": room_id}, values={ "room_id": room_id, - "directory_path": path, "share_id": share_id, }, desc="bind", ) - async def unbind(self, room_id): - """Delete mapping between Watcha room and Nextcloud directory for room_id.""" + async def delete_share(self, room_id: str): + """Delete an existing share of a room + Args: + room_id: id of the room where the share is associated + """ await self.db_pool.simple_delete( - table="room_nextcloud_mapping", + table="watcha_nextcloud_shares", keyvalues={"room_id": room_id}, desc="unbind", ) - async def get_nextcloud_username(self, user_id): + async def get_username(self, user_id: str): """Look up a Nextcloud username by their user_id Args: @@ -65,5 +63,5 @@ class NextcloudStore(SQLBaseStore): keyvalues={"user_id": user_id}, retcol="nextcloud_username", allow_none=True, - desc="get_nextcloud_username", + desc="get_username", ) diff --git a/tests/handlers/test_watcha_nextcloud.py b/tests/handlers/test_watcha_nextcloud.py index f9653de63..dbfb5e68e 100644 --- a/tests/handlers/test_watcha_nextcloud.py +++ b/tests/handlers/test_watcha_nextcloud.py @@ -1,6 +1,6 @@ from mock import AsyncMock -from synapse.api.errors import Codes, SynapseError +from synapse.api.errors import SynapseError, NextcloudError from synapse.rest.client.v1 import login, room from synapse.rest import admin from tests.unittest import HomeserverTestCase @@ -18,158 +18,172 @@ class NextcloudHandlerTestCase(HomeserverTestCase): def prepare(self, reactor, clock, hs): self.store = hs.get_datastore() self.nextcloud_handler = hs.get_nextcloud_handler() - self.keycloak_client = self.nextcloud_handler.keycloak_client self.nextcloud_client = self.nextcloud_handler.nextcloud_client - # Create a room with two users : self.creator = self.register_user("creator", "pass", admin=True) self.creator_tok = self.login("creator", "pass") - self.inviter = self.register_user("inviter", "pass") inviter_tok = self.login("inviter", "pass") - self.room_id = self.helper.create_room_as(self.creator, tok=self.creator_tok) self.helper.invite( self.room_id, src=self.creator, targ=self.inviter, tok=self.creator_tok ) self.helper.join(self.room_id, self.inviter, tok=inviter_tok) + self.group_name = NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id - # Mock Nextcloud client functions : self.nextcloud_client.add_group = AsyncMock() self.nextcloud_client.delete_group = AsyncMock() self.nextcloud_client.add_user_to_group = AsyncMock() self.nextcloud_client.remove_user_from_group = AsyncMock() self.nextcloud_client.unshare = AsyncMock() - self.nextcloud_client.share = AsyncMock(return_value=1) + self.nextcloud_client.share = AsyncMock(return_value="share_1") - def test_set_a_new_bind(self): self.get_success( - self.nextcloud_handler.bind(self.creator, self.room_id, "/directory") - ) - - mapped_directory = self.get_success( - self.store.get_path_from_room_id(self.room_id) - ) - - share_id = self.get_success( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) + self.nextcloud_handler.bind(self.creator, self.room_id, "/folder") ) + self.nextcloud_client.add_group.reset_mock() + self.nextcloud_client.add_user_to_group.reset_mock() + self.nextcloud_client.share.reset_mock() - # Verify that mocked functions are called once - self.nextcloud_client.add_group.assert_called_once() - self.nextcloud_client.share.assert_called_once() - - # Verify that mocked functions are called twice - self.assertEquals(self.nextcloud_client.add_user_to_group.call_count, 2) - - # Verify that mocked functions are not called - self.nextcloud_client.unshare.assert_not_called() - - self.assertEqual(mapped_directory, "/directory") - - def test_update_an_existing_bind(self): - self.get_success(self.store.bind(self.room_id, "/directory", 2)) + def test_unbind(self): + self.get_success(self.nextcloud_handler.unbind(self.room_id)) + share_id = self.get_success(self.store.get_share_id(self.room_id)) - old_mapped_directory = self.get_success( - self.store.get_path_from_room_id(self.room_id) - ) + self.nextcloud_client.delete_group.assert_called() + self.assertIsNone(share_id) - old_share_id = self.get_success( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) + def test_unbind_with_unexisting_group(self): + self.nextcloud_client.delete_group = AsyncMock( + side_effect=NextcloudError(code=101, msg="") ) + with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: + self.get_success(self.nextcloud_handler.unbind(self.room_id)) - self.assertEqual(old_mapped_directory, "/directory") - self.assertEqual(old_share_id, 2) - - self.get_success( - self.nextcloud_handler.bind(self.creator, self.room_id, "/directory2") - ) + share_id = self.get_success(self.store.get_share_id(self.room_id)) - mapped_directory = self.get_success( - self.store.get_path_from_room_id(self.room_id) + self.assertIn( + f"[watcha] delete nextcloud group {self.group_name} - failed:", + cm.output[0], ) + self.nextcloud_client.delete_group.assert_called() + self.assertIsNone(share_id) - new_share_id = self.get_success( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) + def test_update_bind(self): + old_share_id = self.get_success(self.store.get_share_id(self.room_id)) + self.nextcloud_client.share = AsyncMock(return_value="share_2") + self.get_success( + self.nextcloud_handler.bind(self.creator, self.room_id, "/new_folder") ) + share_id = self.get_success(self.store.get_share_id(self.room_id)) - # Verify that mocked functions has called : + self.assertEqual(old_share_id, "share_1") + self.nextcloud_client.add_group.assert_called_once() + self.nextcloud_client.share.assert_called_once() + self.assertEquals(self.nextcloud_client.add_user_to_group.call_count, 2) self.nextcloud_client.unshare.assert_called() + self.assertEquals(share_id, "share_2") - self.assertEqual(mapped_directory, "/directory2") - self.assertEqual(new_share_id, 1) + def test_update_bind_with_existing_group(self): + self.nextcloud_client.add_group = AsyncMock( + side_effect=NextcloudError(code=102, msg="") + ) - def test_delete_an_existing_bind(self): - self.get_success(self.store.bind(self.room_id, "/directory", 2)) - self.get_success(self.nextcloud_handler.unbind(self.room_id)) + with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: + self.get_success( + self.nextcloud_handler.bind(self.creator, self.room_id, "/new_folder") + ) - mapped_directory = self.get_success( - self.store.get_path_from_room_id(self.room_id) + self.assertIn( + f"[watcha] add nextcloud group {self.group_name} - failed: the group already exists", + cm.output[0], ) - share_id = self.get_success( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) + def test_update_bind_with_invalid_input_data(self): + self.nextcloud_client.add_group = AsyncMock( + side_effect=NextcloudError(code=101, msg="") + ) + self.get_failure( + self.nextcloud_handler.bind(self.creator, self.room_id, "/new_folder"), + SynapseError, ) - self.nextcloud_client.delete_group.assert_called() - self.assertIsNone(mapped_directory) - self.assertIsNone(share_id) - - def test_add_user_to_nextcloud_group_without_nextcloud_account(self): + def test_add_user_to_group_without_account(self): self.nextcloud_client.add_user_to_group = AsyncMock( - side_effect=SynapseError(code=400, msg="") + side_effect=NextcloudError(code=103, msg="") ) with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: self.get_success( - self.nextcloud_handler.add_room_users_to_nextcloud_group(self.room_id) + self.nextcloud_handler.add_room_members_to_group(self.room_id) ) self.assertIn( - "Unable to add the user {} to the Nextcloud group {}".format( - self.creator, - NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id, - ), + f"[watcha] add user {self.creator} to group {self.group_name} - failed", cm.output[0], ) - self.assertIn( - "Unable to add the user {} to the Nextcloud group {}".format( - self.inviter, - NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id, - ), + f"[watcha] add user {self.inviter} to group {self.group_name} - failed", cm.output[1], ) - def test_add_user_to_nextcloud_group_with_exception(self): - group_name = NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id - self.nextcloud_client.add_user_to_group = AsyncMock( - side_effect=SynapseError(code=400, msg="") + def test_create_share_with_unexisting_folder(self): + old_share_id = self.get_success(self.store.get_share_id(self.room_id)) + self.nextcloud_client.share = AsyncMock( + side_effect=NextcloudError(code=404, msg="") + ) + self.nextcloud_client.unshare = AsyncMock( + side_effect=NextcloudError(code=404, msg="") ) + self.nextcloud_handler.unbind = AsyncMock() with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: - self.get_success( - self.nextcloud_handler.add_room_users_to_nextcloud_group(self.room_id) + error = self.get_failure( + self.nextcloud_handler.create_share( + self.creator, self.room_id, "/new_folder" + ), + SynapseError, ) - self.assertIn( - "Unable to add the user {} to the Nextcloud group {}.".format( - self.creator, group_name - ), - cm.output[0], + self.assertEquals(error.value.code, 404) + self.nextcloud_handler.unbind.assert_called_once() + self.assertIn(f"[watcha] unshare {old_share_id} - failed", cm.output[0]) + + def test_create_share_with_other_exceptions(self): + old_share_id = self.get_success(self.store.get_share_id(self.room_id)) + self.nextcloud_client.share = AsyncMock( + side_effect=NextcloudError(code=400, msg="") + ) + self.nextcloud_client.unshare = AsyncMock( + side_effect=NextcloudError(code=404, msg="") ) + self.nextcloud_handler.unbind = AsyncMock() - self.assertIn( - "Unable to add the user {} to the Nextcloud group {}.".format( - self.inviter, group_name - ), - cm.output[1], + with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: + error = self.get_failure( + self.nextcloud_handler.create_share( + self.creator, self.room_id, "/new_folder" + ), + SynapseError, + ) + + self.assertEquals(error.value.code, 500) + self.nextcloud_handler.unbind.assert_called_once() + self.assertIn(f"[watcha] unshare {old_share_id} - failed", cm.output[0]) + + def test_add_user_to_unexisting_group(self): + self.nextcloud_client.add_user_to_group = AsyncMock( + side_effect=NextcloudError(code=102, msg="") ) - def test_update_existing_nextcloud_share_on_invite_membership(self): + self.get_failure( + self.nextcloud_handler.add_room_members_to_group(self.room_id), + SynapseError, + ) + + def test_update_existing_group_on_invite_membership(self): self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( "@second_inviter:test", self.room_id, "invite" ) ) @@ -177,9 +191,9 @@ class NextcloudHandlerTestCase(HomeserverTestCase): self.nextcloud_client.add_user_to_group.assert_called_once() self.nextcloud_client.remove_user_from_group.assert_not_called() - def test_update_existing_nextcloud_share_on_join_membership(self): + def test_update_existing_group_on_join_membership(self): self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( "@second_inviter:test", self.room_id, "join" ) ) @@ -187,9 +201,9 @@ class NextcloudHandlerTestCase(HomeserverTestCase): self.nextcloud_client.add_user_to_group.assert_called_once() self.nextcloud_client.remove_user_from_group.assert_not_called() - def test_update_existing_nextcloud_share_on_leave_membership(self): + def test_update_existing_group_on_leave_membership(self): self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( "@second_inviter:test", self.room_id, "leave" ) ) @@ -197,9 +211,9 @@ class NextcloudHandlerTestCase(HomeserverTestCase): self.nextcloud_client.remove_user_from_group.assert_called_once() self.nextcloud_client.add_user_to_group.assert_not_called() - def test_update_existing_nextcloud_share_on_kick_membership(self): + def test_update_existing_group_on_kick_membership(self): self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( "@second_inviter:test", self.room_id, "kick" ) ) @@ -207,42 +221,36 @@ class NextcloudHandlerTestCase(HomeserverTestCase): self.nextcloud_client.remove_user_from_group.assert_called_once() self.nextcloud_client.add_user_to_group.assert_not_called() - def test_update_existing_nextcloud_share_on_invite_membership_with_exception(self): + def test_update_existing_group_on_invite_membership_with_exception(self): self.nextcloud_client.add_user_to_group = AsyncMock( - side_effect=SynapseError(code=400, msg="") + side_effect=NextcloudError(code=103, msg="") ) second_inviter = "@second_inviter:test" with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( second_inviter, self.room_id, "invite" ) ) - self.assertIn( - "Unable to add the user {} to the Nextcloud group {}.".format( - second_inviter, NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id - ), + f"[watcha] add user {second_inviter} to group {self.group_name} - failed", cm.output[0], ) - def test_update_existing_nextcloud_share_on_leave_membership_with_exception(self): + def test_update_existing_group_on_leave_membership_with_exception(self): self.nextcloud_client.remove_user_from_group = AsyncMock( - side_effect=SynapseError(code=400, msg="") + side_effect=NextcloudError(code=103, msg="") ) second_inviter = "@second_inviter:test" with self.assertLogs("synapse.handlers.watcha_nextcloud", level="WARN") as cm: self.get_success( - self.nextcloud_handler.update_share( + self.nextcloud_handler.update_group( second_inviter, self.room_id, "leave" ) ) - self.assertIn( - "Unable to remove the user {} from the Nextcloud group {}.".format( - second_inviter, NEXTCLOUD_GROUP_NAME_PREFIX + self.room_id - ), + f"[watcha] remove user {second_inviter} from group {self.group_name} - failed", cm.output[0], ) diff --git a/tests/rest/client/v1/test_watcha_nextcloud.py b/tests/rest/client/v1/test_watcha_nextcloud.py index 067b855b9..95dd0fcc2 100644 --- a/tests/rest/client/v1/test_watcha_nextcloud.py +++ b/tests/rest/client/v1/test_watcha_nextcloud.py @@ -3,8 +3,7 @@ from mock import AsyncMock from synapse.api.errors import SynapseError from synapse.rest import admin -from synapse.rest.client.v1 import login, room, watcha -from synapse.types import UserID, create_requester +from synapse.rest.client.v1 import login, room from tests import unittest @@ -20,22 +19,17 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): def prepare(self, reactor, clock, hs): self.store = hs.get_datastore() + self.nextcloud_handler = hs.get_nextcloud_handler() + self.keycloak_client = self.nextcloud_handler.keycloak_client + self.nextcloud_client = self.nextcloud_handler.nextcloud_client + self.creator = self.register_user("creator", "pass") self.creator_tok = self.login("creator", "pass") - self.inviter = self.register_user("inviter", "pass") self.inviter_tok = self.login("inviter", "pass") - self.room_id = self.helper.create_room_as(self.creator, tok=self.creator_tok) + self.get_success(self.store.register_share(self.room_id, 1)) - # map a room with a Nextcloud directory : - self.get_success(self.store.bind(self.room_id, "/directory", 1)) - - # mock some functions of WatchaRoomNextcloudMappingHandler - self.nextcloud_handler = hs.get_nextcloud_handler() - - self.keycloak_client = self.nextcloud_handler.keycloak_client - self.nextcloud_client = self.nextcloud_handler.nextcloud_client self.nextcloud_handler.bind = AsyncMock() self.nextcloud_handler.unbind = AsyncMock() self.nextcloud_directory_url = ( @@ -51,7 +45,7 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): def send_room_nextcloud_mapping_event(self, request_content): channel = self.make_request( "PUT", - "/rooms/{}/state/im.vector.web.settings".format(self.room_id), + f"/rooms/{self.room_id}/state/im.vector.web.settings", content=json.dumps(request_content), access_token=self.creator_tok, ) @@ -70,11 +64,10 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): self.send_room_nextcloud_mapping_event( {"nextcloudShare": self.nextcloud_directory_url} ) - self.assertTrue(self.nextcloud_handler.bind.called) - channel = self.send_room_nextcloud_mapping_event({"nextcloudShare": ""}) - self.assertTrue(self.nextcloud_handler.unbind.called) + self.assertTrue(self.nextcloud_handler.bind.called) + self.assertTrue(self.nextcloud_handler.unbind.called) self.assertEquals(200, channel.code) def test_update_existing_room_nextcloud_mapping(self): @@ -94,12 +87,7 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): ) self.assertFalse(self.nextcloud_handler.bind.called) - self.assertRaises(SynapseError) - self.assertEquals(400, channel.code) - self.assertEquals( - "VectorSetting is only used for Nextcloud integration.", - json.loads(channel.result["body"])["error"], - ) + self.assertEquals(200, channel.code) def test_create_new_room_nextcloud_mapping_with_wrong_url(self): channel = self.send_room_nextcloud_mapping_event( @@ -110,7 +98,7 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): self.assertRaises(SynapseError) self.assertEquals(400, channel.code) self.assertEquals( - "The url doesn't point to a valid nextcloud directory path.", + "[watcha] binding Nextcloud folder with room - failed: wrong folder path", json.loads(channel.result["body"])["error"], ) @@ -118,10 +106,9 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): self.helper.invite( self.room_id, self.creator, self.inviter, tok=self.creator_tok ) - channel = self.make_request( "POST", - "/_matrix/client/r0/rooms/{}/join".format(self.room_id), + f"/_matrix/client/r0/rooms/{self.room_id}/join", access_token=self.inviter_tok, ) @@ -132,10 +119,9 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): self.helper.invite( self.room_id, self.creator, self.inviter, tok=self.creator_tok ) - channel = self.make_request( "POST", - "/_matrix/client/r0/rooms/{}/leave".format(self.room_id), + f"/_matrix/client/r0/rooms/{self.room_id}/leave", access_token=self.inviter_tok, ) @@ -148,10 +134,9 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): self.room_id, self.creator, self.inviter, tok=self.creator_tok ) self.helper.join(self.room_id, user=self.inviter, tok=self.inviter_tok) - channel = self.make_request( "POST", - "/_matrix/client/r0/rooms/{}/kick".format(self.room_id), + f"/_matrix/client/r0/rooms/{self.room_id}/kick", content={"user_id": self.inviter}, access_token=self.inviter_tok, ) @@ -162,9 +147,7 @@ class NextcloudShareTestCase(unittest.HomeserverTestCase): def test_update_nextcloud_share_with_an_unmapped_room(self): self.nextcloud_handler.update_existing_nextcloud_share_for_user = AsyncMock() - room_id = self.helper.create_room_as(self.creator, tok=self.creator_tok) - self.helper.invite(room_id, self.creator, self.inviter, tok=self.creator_tok) self.nextcloud_handler.update_existing_nextcloud_share_for_user.assert_not_called() diff --git a/tests/storage/test_watcha_nextcloud.py b/tests/storage/test_watcha_nextcloud.py index 143273ba0..22f5fad17 100644 --- a/tests/storage/test_watcha_nextcloud.py +++ b/tests/storage/test_watcha_nextcloud.py @@ -1,72 +1,28 @@ -from twisted.internet import defer - from tests import unittest -from tests.utils import setup_test_homeserver class NextcloudStorageTestCase(unittest.HomeserverTestCase): - @defer.inlineCallbacks - def setUp(self): - hs = setup_test_homeserver(self.addCleanup) + def prepare(self, reactor, clock, hs): self.store = hs.get_datastore() self.room_id = "room1" - self.directory_path = "/directory" self.share_id = 1 - # Set mapping between a room and a nextcloud directory : - yield defer.ensureDeferred( - self.store.bind( - self.room_id, self.directory_path, self.share_id - ) - ) - - @defer.inlineCallbacks - def test_get_room_mapping_with_nextcloud_directory(self): - mapped_directory = yield defer.ensureDeferred( - self.store.get_path_from_room_id(self.room_id) - ) - share_id = yield defer.ensureDeferred( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) - ) + self.get_success(self.store.register_share(self.room_id, self.share_id)) - self.assertEquals(mapped_directory, self.directory_path) + def test_get_share_id(self): + share_id = self.get_success(self.store.get_share_id(self.room_id)) self.assertEquals(share_id, self.share_id) - @defer.inlineCallbacks - def test_delete_room_nextcloud_mapping(self): - yield defer.ensureDeferred( - self.store.unbind(self.room_id) - ) - mapped_directory = yield defer.ensureDeferred( - self.store.get_path_from_room_id(self.room_id) - ) - - self.assertIsNone(mapped_directory) - - share_id = yield defer.ensureDeferred( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) - ) + def test_delete_share(self): + self.get_success(self.store.delete_share(self.room_id)) + share_id = self.get_success(self.store.get_share_id(self.room_id)) self.assertIsNone(share_id) - @defer.inlineCallbacks - def test_update_room_mapping_with_nextcloud_directory(self): - new_directory_path = "/directory2" + def test_update_group(self): new_share_id = 2 - yield defer.ensureDeferred( - self.store.bind( - self.room_id, new_directory_path, new_share_id - ) - ) - mapped_directory = yield defer.ensureDeferred( - self.store.get_path_from_room_id(self.room_id) - ) - - self.assertEquals(mapped_directory, new_directory_path) - - share_id = yield defer.ensureDeferred( - self.store.get_nextcloud_share_id_from_room_id(self.room_id) - ) + self.get_success(self.store.register_share(self.room_id, new_share_id)) + share_id = self.get_success(self.store.get_share_id(self.room_id)) self.assertEquals(share_id, new_share_id)