Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

[Feature] Add client-server endpoint to logout from all current WEB sessions #5400

Open
menturion opened this issue Jun 8, 2019 · 1 comment
Labels
A-Login P4 (OBSOLETE: use S- labels.) Okay backlog: will not schedule, will accept patches S-Tolerable Minor significance, cosmetic issues, low or no impact to users. Security T-Enhancement New features, changes in functionality, improvements in performance, or user-facing enhancements. z-feature (Deprecated Label) z-p3 (Deprecated Label)

Comments

@menturion
Copy link

menturion commented Jun 8, 2019

It would be helpful to have a client-server endpoint to logout from all current WEB sessions.

Similar to the POST /_matrix/client/r0/logout/all endpoint, however, excluding mobile Android and iOS clients.

@neilisfragile neilisfragile added z-feature (Deprecated Label) z-p3 (Deprecated Label) labels Jun 10, 2019
@holyCowMp3
Copy link

Hi!
You can use module for custom logout.

from enum import Enum
import json

from twisted.web.resource import Resource
from twisted.internet import reactor, defer
from twisted.internet.task import deferLater
from twisted.web.server import NOT_DONE_YET
from twisted.web.server import Request
import datetime
from synapse.module_api import ModuleApi
from synapse.storage.database import DatabasePool

class Constants(Enum):
    AUTH_TOKEN_HEADER = "YOUR_CUSTOM_TOKEN_HEADER"

def select_devices_txn(txn, homeserver_domain, user_id):
    timestamp = datetime.datetime.now().timestamp() * 1000
    txn.execute(
        """
            select device_id, user_id from devices 
            WHERE display_name is NULL or display_name like ? 
            and user_id = ? 
            and last_seen <= ?
            """,
        (f"{homeserver_domain}/element%", user_id, int(timestamp)),
    )
    return DatabasePool.cursor_to_dict(txn)


def set_access_token_timestamp(txn, device_id, user_id, store):
    timestamp = 0
    txn.execute(
        """
            update access_tokens 
            set valid_until_ms = ? 
            where device_id = ? 
            and user_id = ?
            """,
        (timestamp, device_id, user_id),
    )
    txn.execute(
        """
            SELECT token, id, device_id 
            FROM access_tokens
            WHERE device_id = ? 
            AND user_id = ?
        """,
        (device_id, user_id),
    )
    tokens_and_devices = [(r[0], r[1], r[2]) for r in txn]
    for token, _, _ in tokens_and_devices:
        store._invalidate_cache_and_stream(txn, store.get_user_by_access_token, (token,))
    return txn

class RevokeUserSessionFromAuth(Resource):
    
    def __init__(self, config):
        super(RevokeUserSessionFromAuth, self).__init__()
        self.homeserver_domain = config.get("homeserver_domain") or ""
        self.session_lifetime_in_seconds = (
            int(config.get("session_lifetime_in_minutes")) or 60) * 60
        self.api = config.get('api')
        self.security_token = config.get('security_token')

    def _delayedRender(self, request):    
        user_id = request.args.get(b'user_id')[0].decode()
        print(f"Start cleaning web session for user_id {user_id}")
        devices = self.api.run_db_interaction(
            "select_devices", select_devices_txn, self.homeserver_domain, user_id)

        def update_tokens(devices):
            for device in devices:
                device_id = device['device_id']
                user_id = device['user_id']
                self.api.run_db_interaction("set_expire_for_web_access_tokens", set_access_token_timestamp, device_id, user_id, self.api._store).addCallback(print)
            return devices

        def render_result(res):
            request.setHeader(b"Content-Type", b"application/json")
            request.write(json.dumps(res).encode())
            request.finish()

        devices.addCallback(update_tokens).addCallback(render_result)

    def render_PUT(self, request: Request):
        sessionToken = request.getHeader(Constants.AUTH_TOKEN_HEADER.value)
        if(sessionToken == self.security_token):  
            d = deferLater(reactor, 5, lambda: request)
            d.addCallback(self._delayedRender)
            return NOT_DONE_YET
        else:
            request.code = 403
            return "NO!".encode()

class SessionManager:
    def __init__(self, config: dict, api: ModuleApi):
        self.api = api
        config['api'] = api
        
        self.api.register_web_resource(
            path="/_synapse/client/logout/all/web",
            resource=RevokeUserSessionFromAuth(config),
        )

@squahtx squahtx added S-Tolerable Minor significance, cosmetic issues, low or no impact to users. Security T-Enhancement New features, changes in functionality, improvements in performance, or user-facing enhancements. P4 (OBSOLETE: use S- labels.) Okay backlog: will not schedule, will accept patches A-Login labels Jul 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
A-Login P4 (OBSOLETE: use S- labels.) Okay backlog: will not schedule, will accept patches S-Tolerable Minor significance, cosmetic issues, low or no impact to users. Security T-Enhancement New features, changes in functionality, improvements in performance, or user-facing enhancements. z-feature (Deprecated Label) z-p3 (Deprecated Label)
Projects
None yet
Development

No branches or pull requests

4 participants