Skip to content

Commit

Permalink
Switch to appservice login as specified in matrix-org/matrix-spec-pro…
Browse files Browse the repository at this point in the history
  • Loading branch information
tulir committed Sep 18, 2020
1 parent cc43498 commit 12d7c48
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 30 deletions.
2 changes: 1 addition & 1 deletion mautrix/__init__.py
@@ -1,3 +1,3 @@
__version__ = "0.7.3"
__version__ = "0.8.0+dev"
__author__ = "Tulir Asokan <tulir@maunium.net>"
__all__ = ["api", "appservice", "bridge", "client", "crypto", "errors", "util", "types"]
26 changes: 14 additions & 12 deletions mautrix/bridge/e2ee.py
Expand Up @@ -6,20 +6,19 @@
from typing import Tuple, Union, Optional, Dict, TYPE_CHECKING
import logging
import asyncio
import hashlib
import hmac

from mautrix.types import (Filter, RoomFilter, EventFilter, RoomEventFilter, StateFilter, EventType,
RoomID, Serializable, JSON, MessageEvent, EncryptedEvent, StateEvent,
EncryptedMegolmEventContent, RequestedKeyInfo, RoomKeyWithheldCode)
EncryptedMegolmEventContent, RequestedKeyInfo, RoomKeyWithheldCode,
LoginType)
from mautrix.appservice import AppService
from mautrix.errors import EncryptionError
from mautrix.client import Client, SyncStore
from mautrix.crypto import (OlmMachine, CryptoStore, StateStore, PgCryptoStore, PickleCryptoStore,
DeviceIdentity, RejectKeyShare, TrustState)
from mautrix.util.logging import TraceLogger

from .crypto_state_store import GetPortalFunc, PgCryptoStateStore, SQLCryptoStateStore
from .crypto_state_store import PgCryptoStateStore, SQLCryptoStateStore

try:
from mautrix.client.state_store.sqlalchemy import UserProfile
Expand Down Expand Up @@ -47,23 +46,21 @@ class EncryptionManager:

bridge: 'Bridge'
az: AppService
login_shared_secret: bytes
_id_prefix: str
_id_suffix: str

sync_task: asyncio.Future
_share_session_events: Dict[RoomID, asyncio.Event]

def __init__(self, bridge: 'Bridge', login_shared_secret: str, homeserver_address: str,
user_id_prefix: str, user_id_suffix: str, db_url: str,
key_sharing_config: Dict[str, bool] = None) -> None:
def __init__(self, bridge: 'Bridge', homeserver_address: str, user_id_prefix: str,
user_id_suffix: str, db_url: str, key_sharing_config: Dict[str, bool] = None
) -> None:
self.loop = bridge.loop or asyncio.get_event_loop()
self.bridge = bridge
self.az = bridge.az
self.device_name = bridge.name
self._id_prefix = user_id_prefix
self._id_suffix = user_id_suffix
self.login_shared_secret = login_shared_secret.encode("utf-8")
self._share_session_events = {}
self.key_sharing_config = key_sharing_config or {}
pickle_key = "mautrix.bridge.e2ee"
Expand Down Expand Up @@ -161,17 +158,22 @@ async def decrypt(self, evt: EncryptedEvent) -> MessageEvent:
self.log.trace("Decrypted event %s: %s", evt.event_id, decrypted)
return decrypted

async def check_server_support(self) -> bool:
flows = await self.client.get_login_flows()
return flows.supports_type(LoginType.APPSERVICE)

async def start(self) -> None:
self.log.debug("Logging in with bridge bot user")
password = hmac.new(self.login_shared_secret, self.az.bot_mxid.encode("utf-8"),
hashlib.sha512).hexdigest()
if self.crypto_db:
await self.crypto_db.start()
await self.crypto_store.open()
device_id = await self.crypto_store.get_device_id()
if device_id:
self.log.debug(f"Found device ID in database: {device_id}")
await self.client.login(password=password, device_name=self.device_name,
# We set the API token to the AS token here to authenticate the appservice login
# It'll get overridden after the login
self.client.api.token = self.az.as_token
await self.client.login(login_type=LoginType.APPSERVICE, device_name=self.device_name,
device_id=device_id, store_access_token=True, update_hs_url=False)
await self.crypto.load()
if not device_id:
Expand Down
24 changes: 13 additions & 11 deletions mautrix/bridge/matrix.py
Expand Up @@ -57,16 +57,13 @@ def __init__(self, command_processor: Optional[CommandProcessor] = None,
if self.config["bridge.encryption.allow"]:
if not EncryptionManager:
self.log.error("Encryption enabled in config, but dependencies not installed.")
elif not self.config["bridge.login_shared_secret"]:
self.log.warning("Encryption enabled in config, but login_shared_secret not set.")
else:
self.e2ee = EncryptionManager(
bridge=bridge,
user_id_prefix=self.user_id_prefix, user_id_suffix=self.user_id_suffix,
login_shared_secret=self.config["bridge.login_shared_secret"],
homeserver_address=self.config["homeserver.address"],
db_url=self._get_db_url(bridge.config),
key_sharing_config=self.config["bridge.encryption.key_sharing"])
return
self.e2ee = EncryptionManager(
bridge=bridge,
user_id_prefix=self.user_id_prefix, user_id_suffix=self.user_id_suffix,
homeserver_address=self.config["homeserver.address"],
db_url=self._get_db_url(bridge.config),
key_sharing_config=self.config["bridge.encryption.key_sharing"])

@staticmethod
def _get_db_url(config: 'BaseBridgeConfig') -> str:
Expand Down Expand Up @@ -123,7 +120,12 @@ async def init_as_bot(self) -> None:

async def init_encryption(self) -> None:
if self.e2ee:
await self.e2ee.start()
if not await self.e2ee.check_server_support():
self.log.error("Encryption enabled in config, but homeserver does not "
"support appservice login")
self.e2ee = None
else:
await self.e2ee.start()

@staticmethod
async def allow_message(user: 'BaseUser') -> bool:
Expand Down
6 changes: 3 additions & 3 deletions mautrix/client/api/authentication.py
Expand Up @@ -7,7 +7,7 @@

from mautrix.errors import MatrixResponseError
from mautrix.api import Method, Path
from mautrix.types import (UserID, LoginType, UserIdentifier, LoginResponse, LoginFlow,
from mautrix.types import (UserID, LoginType, UserIdentifier, LoginResponse, LoginFlowList,
MatrixUserIdentifier)

from .base import BaseClientAPI
Expand All @@ -24,7 +24,7 @@ class ClientAuthenticationMethods(BaseClientAPI):
# region 5.5 Login
# API reference: https://matrix.org/docs/spec/client_server/r0.6.1.html#login

async def get_login_flows(self) -> List[LoginFlow]:
async def get_login_flows(self) -> LoginFlowList:
"""
Get login flows supported by the homeserver.
Expand All @@ -35,7 +35,7 @@ async def get_login_flows(self) -> List[LoginFlow]:
"""
resp = await self.api.request(Method.GET, Path.login)
try:
return [LoginFlow.deserialize(flow) for flow in resp["flows"]]
return LoginFlowList.deserialize(resp)
except KeyError:
raise MatrixResponseError("`flows` not in response.")

Expand Down
2 changes: 1 addition & 1 deletion mautrix/types/__init__.py
Expand Up @@ -41,7 +41,7 @@
from .auth import (LoginType, UserIdentifierType, MatrixUserIdentifier, ThirdPartyIdentifier,
PhoneIdentifier, UserIdentifier, LoginResponse, DiscoveryInformation,
DiscoveryServer, DiscoveryIntegrations, DiscoveryIntegrationServer,
LoginFlow)
LoginFlow, LoginFlowList)
from .crypto import UnsignedDeviceInfo, DeviceKeys, ClaimKeysResponse, QueryKeysResponse
from .media import MediaRepoConfig, MXOpenGraph, OpenGraphVideo, OpenGraphImage, OpenGraphAudio
from .util import (Obj, Lst, SerializerError, Serializable, SerializableEnum, SerializableAttrs,
Expand Down
19 changes: 17 additions & 2 deletions mautrix/types/auth.py
Expand Up @@ -7,7 +7,7 @@
from attr import dataclass
import attr

from .primitive import UserID, JSON
from .primitive import UserID, DeviceID, JSON
from .util import SerializableAttrs, ExtensibleEnum, deserializer, Obj


Expand All @@ -20,6 +20,10 @@ class LoginType(ExtensibleEnum):
"""
PASSWORD: 'LoginType' = "m.login.password"
TOKEN: 'LoginType' = "m.login.token"
SSO: 'LoginType' = "m.login.sso"

JWT: 'LoginType' = "org.matrix.login.jwt"
APPSERVICE: 'LoginType' = "uk.half-shot.msc2778.login.application_service"


@dataclass
Expand All @@ -33,6 +37,17 @@ class LoginFlow(SerializableAttrs['LoginFlow']):
type: LoginType


@dataclass
class LoginFlowList(SerializableAttrs['LoginFlowList']):
flows: List[LoginFlow]

def supports_type(self, type: LoginType) -> bool:
for flow in self.flows:
if flow.type == type:
return True
return False


class UserIdentifierType(ExtensibleEnum):
"""
A user identifier type, as specified in the `Identifier types`_ section of the login spec.
Expand Down Expand Up @@ -150,6 +165,6 @@ class LoginResponse(SerializableAttrs['LoginResponse']):
https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-login
"""
user_id: UserID
device_id: str
device_id: DeviceID
access_token: str
well_known: DiscoveryInformation = attr.ib(factory=DiscoveryInformation)

0 comments on commit 12d7c48

Please sign in to comment.