182 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			182 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
# -*- coding: utf-8 -*-
 | 
						|
# Copyright 2016 OpenMarket 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 synapse.api import errors
 | 
						|
from synapse.util import stringutils
 | 
						|
from twisted.internet import defer
 | 
						|
from ._base import BaseHandler
 | 
						|
 | 
						|
import logging
 | 
						|
 | 
						|
logger = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class DeviceHandler(BaseHandler):
 | 
						|
    def __init__(self, hs):
 | 
						|
        super(DeviceHandler, self).__init__(hs)
 | 
						|
 | 
						|
    @defer.inlineCallbacks
 | 
						|
    def check_device_registered(self, user_id, device_id,
 | 
						|
                                initial_device_display_name=None):
 | 
						|
        """
 | 
						|
        If the given device has not been registered, register it with the
 | 
						|
        supplied display name.
 | 
						|
 | 
						|
        If no device_id is supplied, we make one up.
 | 
						|
 | 
						|
        Args:
 | 
						|
            user_id (str):  @user:id
 | 
						|
            device_id (str | None): device id supplied by client
 | 
						|
            initial_device_display_name (str | None): device display name from
 | 
						|
                 client
 | 
						|
        Returns:
 | 
						|
            str: device id (generated if none was supplied)
 | 
						|
        """
 | 
						|
        if device_id is not None:
 | 
						|
            yield self.store.store_device(
 | 
						|
                user_id=user_id,
 | 
						|
                device_id=device_id,
 | 
						|
                initial_device_display_name=initial_device_display_name,
 | 
						|
                ignore_if_known=True,
 | 
						|
            )
 | 
						|
            defer.returnValue(device_id)
 | 
						|
 | 
						|
        # if the device id is not specified, we'll autogen one, but loop a few
 | 
						|
        # times in case of a clash.
 | 
						|
        attempts = 0
 | 
						|
        while attempts < 5:
 | 
						|
            try:
 | 
						|
                device_id = stringutils.random_string(10).upper()
 | 
						|
                yield self.store.store_device(
 | 
						|
                    user_id=user_id,
 | 
						|
                    device_id=device_id,
 | 
						|
                    initial_device_display_name=initial_device_display_name,
 | 
						|
                    ignore_if_known=False,
 | 
						|
                )
 | 
						|
                defer.returnValue(device_id)
 | 
						|
            except errors.StoreError:
 | 
						|
                attempts += 1
 | 
						|
 | 
						|
        raise errors.StoreError(500, "Couldn't generate a device ID.")
 | 
						|
 | 
						|
    @defer.inlineCallbacks
 | 
						|
    def get_devices_by_user(self, user_id):
 | 
						|
        """
 | 
						|
        Retrieve the given user's devices
 | 
						|
 | 
						|
        Args:
 | 
						|
            user_id (str):
 | 
						|
        Returns:
 | 
						|
            defer.Deferred: list[dict[str, X]]: info on each device
 | 
						|
        """
 | 
						|
 | 
						|
        device_map = yield self.store.get_devices_by_user(user_id)
 | 
						|
 | 
						|
        ips = yield self.store.get_last_client_ip_by_device(
 | 
						|
            devices=((user_id, device_id) for device_id in device_map.keys())
 | 
						|
        )
 | 
						|
 | 
						|
        devices = device_map.values()
 | 
						|
        for device in devices:
 | 
						|
            _update_device_from_client_ips(device, ips)
 | 
						|
 | 
						|
        defer.returnValue(devices)
 | 
						|
 | 
						|
    @defer.inlineCallbacks
 | 
						|
    def get_device(self, user_id, device_id):
 | 
						|
        """ Retrieve the given device
 | 
						|
 | 
						|
        Args:
 | 
						|
            user_id (str):
 | 
						|
            device_id (str):
 | 
						|
 | 
						|
        Returns:
 | 
						|
            defer.Deferred: dict[str, X]: info on the device
 | 
						|
        Raises:
 | 
						|
            errors.NotFoundError: if the device was not found
 | 
						|
        """
 | 
						|
        try:
 | 
						|
            device = yield self.store.get_device(user_id, device_id)
 | 
						|
        except errors.StoreError:
 | 
						|
            raise errors.NotFoundError
 | 
						|
        ips = yield self.store.get_last_client_ip_by_device(
 | 
						|
            devices=((user_id, device_id),)
 | 
						|
        )
 | 
						|
        _update_device_from_client_ips(device, ips)
 | 
						|
        defer.returnValue(device)
 | 
						|
 | 
						|
    @defer.inlineCallbacks
 | 
						|
    def delete_device(self, user_id, device_id):
 | 
						|
        """ Delete the given device
 | 
						|
 | 
						|
        Args:
 | 
						|
            user_id (str):
 | 
						|
            device_id (str):
 | 
						|
 | 
						|
        Returns:
 | 
						|
            defer.Deferred:
 | 
						|
        """
 | 
						|
 | 
						|
        try:
 | 
						|
            yield self.store.delete_device(user_id, device_id)
 | 
						|
        except errors.StoreError, e:
 | 
						|
            if e.code == 404:
 | 
						|
                # no match
 | 
						|
                pass
 | 
						|
            else:
 | 
						|
                raise
 | 
						|
 | 
						|
        yield self.store.user_delete_access_tokens(
 | 
						|
            user_id, device_id=device_id,
 | 
						|
            delete_refresh_tokens=True,
 | 
						|
        )
 | 
						|
 | 
						|
        yield self.store.delete_e2e_keys_by_device(
 | 
						|
            user_id=user_id, device_id=device_id
 | 
						|
        )
 | 
						|
 | 
						|
    @defer.inlineCallbacks
 | 
						|
    def update_device(self, user_id, device_id, content):
 | 
						|
        """ Update the given device
 | 
						|
 | 
						|
        Args:
 | 
						|
            user_id (str):
 | 
						|
            device_id (str):
 | 
						|
            content (dict): body of update request
 | 
						|
 | 
						|
        Returns:
 | 
						|
            defer.Deferred:
 | 
						|
        """
 | 
						|
 | 
						|
        try:
 | 
						|
            yield self.store.update_device(
 | 
						|
                user_id,
 | 
						|
                device_id,
 | 
						|
                new_display_name=content.get("display_name")
 | 
						|
            )
 | 
						|
        except errors.StoreError, e:
 | 
						|
            if e.code == 404:
 | 
						|
                raise errors.NotFoundError()
 | 
						|
            else:
 | 
						|
                raise
 | 
						|
 | 
						|
 | 
						|
def _update_device_from_client_ips(device, client_ips):
 | 
						|
    ip = client_ips.get((device["user_id"], device["device_id"]), {})
 | 
						|
    device.update({
 | 
						|
        "last_seen_ts": ip.get("last_seen"),
 | 
						|
        "last_seen_ip": ip.get("ip"),
 | 
						|
    })
 |