def detach(self, session=None) -> List[int]: self.log.debug('{func}(session={session})', func=hltype(self.detach), session=session) detached_session_ids = [] if session is None: # detach all sessions from router for session in list(self._session_id_to_session.values()): self._detach(session) detached_session_ids.append(session._session_id) else: # detach single session from router self._detach(session) detached_session_ids.append(session._session_id) self.log.info( '{func} router session detached from realm "{realm}" (session={session}, ' 'detached_session_ids={detached_session_ids}, authid="{authid}", authrole="{authrole}", ' 'authmethod="{authmethod}", authprovider="{authprovider}")', func=hltype(self.detach), session=hlid(session._session_id) if session else '', authid=hlid(session._authid), authrole=hlid(session._authrole), authmethod=hlval(session._authmethod), authprovider=hlval(session._authprovider), detached_session_ids=hlval(len(detached_session_ids)), realm=hlid(session._realm)) return detached_session_ids
def delAuth(self, cbtid: str) -> bool: """ Delete an existing cookie (if any), including any authentication info and the cookie itself. :param cbtid: Cookie value (ID) of cookie to delete. :return: Flag indicating an existing cookie was deleted. """ was_existing = False with self._db.begin(write=True) as txn: cookie_oid = self._schema.idx_cookies_by_value[txn, cbtid] if cookie_oid: del self._schema.cookies[txn, cookie_oid] was_existing = True if was_existing: self.log.info( '{func} cookie with cbtid="{cbtid}" did exist and was deleted', func=hltype(self.delAuth), cbtid=hlid(cbtid)) else: self.log.info('{func} no cookie with cbtid="{cbtid}" exists', func=hltype(self.delAuth), cbtid=hlid(cbtid)) return was_existing
def authenticate(realm, authid, details): self.log.info( '{func}(realm="{realm}", authid="{authid}", details=details)', func=authenticate, realm=hlid(realm), authid=hlid(authid), details=details) return 'anonymous'
async def onJoin(self, details): self.log.info('Ok, client joined on realm "{realm}" [session={session}, authid="{authid}", authrole="{authrole}"]', realm=hlid(details.realm), session=hlid(details.session), authid=hlid(details.authid), authrole=hlid(details.authrole), details=details) if 'ready' in self.config.extra: txaio.resolve(self.config.extra['ready'], (self, details))
def dropProto(self, cbtid: str, proto: ISession): """ Remove given WebSocket connection from the set of connections associated with the cookie having the given ID. Return the new count of connections associated with the cookie. """ self.log.debug('{func} removing proto {proto} from cookie "{cbtid}"', func=hltype(self.dropProto), proto=proto, cbtid=hlid(cbtid)) if self.exists(cbtid): if cbtid in self._connections: # remove this WebSocket connection from the set of connections # associated with the same cookie self._connections[cbtid].discard(proto) remaining = len(self._connections[cbtid]) if remaining: return remaining else: del self._connections[cbtid] else: if cbtid in self._connections: del self._connections[cbtid] return 0
def onJoin(self, details): assert self.config.extra and 'on_ready' in self.config.extra assert self.config.extra and 'other' in self.config.extra remote = self.config.extra['other'] assert isinstance(remote, RLinkRemoteSession) self._exclude_authid = self.config.extra.get('exclude_authid', None) self._exclude_authrole = self.config.extra.get('exclude_authrole', None) # setup local->remote event forwarding forward_events = self.config.extra.get('forward_events', False) if forward_events: yield self._setup_event_forwarding(remote) # setup local->remote invocation forwarding forward_invocations = self.config.extra.get('forward_invocations', False) if forward_invocations: yield self._setup_invocation_forwarding(remote) self.log.debug( 'Router link local session ready (forward_events={forward_events}, forward_invocations={forward_invocations}, realm={realm}, authid={authid}, authrole={authrole}, session={session}) {method}', method=hltype(RLinkLocalSession.onJoin), forward_events=hluserid(forward_events), forward_invocations=hluserid(forward_invocations), realm=hluserid(details.realm), authid=hluserid(details.authid), authrole=hluserid(details.authrole), session=hlid(details.session)) on_ready = self.config.extra.get('on_ready', None) if on_ready and not on_ready.called: self.config.extra['on_ready'].callback(self)
def onLeave(self, details): self.log.warn( 'Router link local session down! (realm={realm}, authid={authid}, authrole={authrole}, session={session}, details={details}) {method}', method=hltype(RLinkLocalSession.onLeave), realm=hluserid(self.config.realm), authid=hluserid(self._authid), authrole=hluserid(self._authrole), details=details, session=hlid(self._session_id)) BridgeSession.onLeave(self, details)
def delAuth(self, cbtid: str) -> bool: """ Delete an existing cookie (if any), including any authentication info and the cookie itself. :param cbtid: Cookie value (ID) of cookie to delete. :return: Flag indicating an existing cookie was deleted. """ if self.exists(cbtid): cookie = self._cookies[cbtid] cookie['deleted'] = util.utcnow() del self._cookies[cbtid] self._persist(cbtid, cookie, status='deleted') self.log.info( '{func} cookie with cbtid="{cbtid}" did exist and was deleted', func=hltype(self.delAuth), cbtid=hlid(cbtid)) return True else: self.log.info('{func} no cookie with cbtid="{cbtid}" exists', func=hltype(self.delAuth), cbtid=hlid(cbtid)) return False
def getAuth( self, cbtid: str ) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str], Optional[Dict[str, Any]]]: """ Get the authentication info (if any) for a cookie stored in the database. :param cbtid: Cookie value (ID) to get authentication for. :return: A tuple ``(authid, authrole, authmethod, authrealm, authextra)`` if the cookie exists, or a tuple ``(None, None, None, None, None)`` if no cookie with given ``cbtid`` is currently stored. """ with self._db.begin() as txn: # find cookie OID by cookie value cookie_oid = self._schema.idx_cookies_by_value[txn, cbtid] if cookie_oid: # if we found a cookie OID, read the actual cookie from database cookie = self._schema.cookies[txn, cookie_oid] assert cookie cbtid_ = cookie.value cookie_auth_info = cookie.authid, cookie.authrole, cookie.authmethod, cookie.authrealm, cookie.authextra else: cbtid_ = None cookie_auth_info = None, None, None, None, None if cbtid_: self.log.info( '{func} cookie auth info for "{cbtid}" retrieved: {cookie_auth_info}', func=hltype(self.getAuth), cbtid=hlid(cbtid_), cookie_auth_info=cookie_auth_info) else: self.log.info( '{func} no cookie for "{cbtid}" stored in cookiestore database', func=hltype(self.getAuth), cbtid=hlid(cbtid)) return cookie_auth_info
def delAuth(self, cbtid: str) -> bool: """ Delete an existing cookie (if any), including any authentication info and the cookie itself. :param cbtid: Cookie value (ID) of cookie to delete. :return: Flag indicating an existing cookie was deleted. """ was_existing = False if cbtid in self._cookies: del self._cookies[cbtid] was_existing = True if was_existing: self.log.info( '{func} cookie with cbtid="{cbtid}" did exist and was deleted', func=hltype(self.setAuth), cbtid=hlid(cbtid)) else: self.log.info('{func} no cookie with cbtid="{cbtid}" exists', func=hltype(self.setAuth), cbtid=hlid(cbtid)) return was_existing
def attach(self, session: ISession): """ Implements :func:`autobahn.wamp.interfaces.IRouter.attach` """ self.log.debug('{func}(session={session})', func=hltype(self.attach), session=session) if session._session_id not in self._session_id_to_session: self._session_id_to_session[session._session_id] = session else: raise Exception("session with ID {} already attached".format( session._session_id)) self._broker.attach(session) self._dealer.attach(session) self._attached += 1 self.log.info( '{func} new session attached for realm="{realm}", session={session}, authid="{authid}", ' 'authrole="{authrole}", authmethod="{authmethod}", authprovider="{authprovider}", authextra=\n{authextra}', func=hltype(self.attach), session=hlid(session._session_id) if session else '', authid=hlid(session._authid), authrole=hlid(session._authrole), authmethod=hlval(session._authmethod), authprovider=hlval(session._authprovider), authextra=pformat(session._authextra) if session._authextra else None, realm=hlid(session._realm)) return { 'broker': self._broker._role_features, 'dealer': self._dealer._role_features }
def onJoin(self, details): self.log.debug('{klass}.onJoin(details={details})', klass=self.__class__.__name__, details=details) assert self.config.extra and 'on_ready' in self.config.extra assert self.config.extra and 'other' in self.config.extra local = self.config.extra['other'] assert isinstance(local, RLinkLocalSession) local._tracker.connected = True self._exclude_authid = self.config.extra.get('exclude_authid', None) self._exclude_authrole = self.config.extra.get('exclude_authrole', None) # setup remote->local event forwarding forward_events = self.config.extra.get('forward_events', False) if forward_events: yield self._setup_event_forwarding(local) # setup remote->local invocation forwarding forward_invocations = self.config.extra.get('forward_invocations', False) if forward_invocations: yield self._setup_invocation_forwarding(local) self.log.info( '{klass}.onJoin(): rlink remote session ready (forward_events={forward_events}, forward_invocations={forward_invocations}, realm={realm}, authid={authid}, authrole={authrole}, session={session}) {method}', klass=self.__class__.__name__, method=hltype(RLinkRemoteSession.onJoin), forward_events=hluserid(forward_events), forward_invocations=hluserid(forward_invocations), realm=hluserid(details.realm), authid=hluserid(details.authid), authrole=hluserid(details.authrole), session=hlid(details.session)) # we are ready! on_ready = self.config.extra.get('on_ready', None) if on_ready and not on_ready.called: self.config.extra['on_ready'].callback(self)
def addProto(self, cbtid: str, proto: ISession) -> int: """ Add given WebSocket connection to the set of connections associated with the cookie having the given ID. Return the new count of connections associated with the cookie. """ self.log.debug('{func} adding proto {proto} for cookie "{cbtid}"', func=hltype(self.addProto), proto=proto, cbtid=hlid(cbtid)) if self.exists(cbtid): if cbtid not in self._connections: self._connections[cbtid] = set() self._connections[cbtid].add(proto) return len(self._connections[cbtid]) else: if cbtid in self._connections: del self._connections[cbtid] return 0
def get_member(self, ethadr_raw): if self.is_attached(): is_member = yield self.call('xbr.network.is_member', ethadr_raw) if is_member: member_data = yield self.call('xbr.network.get_member_by_wallet', ethadr_raw) member_data['address'] = web3.Web3.toChecksumAddress(member_data['address']) member_data['oid'] = uuid.UUID(bytes=member_data['oid']) member_data['balance']['eth'] = web3.Web3.fromWei(unpack_uint256(member_data['balance']['eth']), 'ether') member_data['balance']['xbr'] = web3.Web3.fromWei(unpack_uint256(member_data['balance']['xbr']), 'ether') member_data['created'] = np.datetime64(member_data['created'], 'ns') member_level = member_data['level'] member_data['level'] = { # Member is active. 1: 'ACTIVE', # Member is active and verified. 2: 'VERIFIED', # Member is retired. 3: 'RETIRED', # Member is subject to a temporary penalty. 4: 'PENALTY', # Member is currently blocked and cannot current actively participate in the market. 5: 'BLOCKED', }.get(member_level, None) self.log.info( 'Member {member_oid} found for address 0x{member_adr} - current member level {member_level}', member_level=hlval(member_data['level']), member_oid=hlid(member_data['oid']), member_adr=hlval(member_data['address'])) return member_data else: self.log.warn('Address {output_ethadr} is not a member in the XBR network', output_ethadr=ethadr_raw) else: self.log.warn('not connected: could not retrieve member data for address {output_ethadr}', output_ethadr=ethadr_raw)
def onLeave(self, details): # When the rlink is going down, make sure to unsubscribe to # all events that are subscribed on the local-leg. # This avoids duplicate events that would otherwise arrive # See: https://github.com/crossbario/crossbar/issues/1916 for k, v in self._subs.items(): if v['sub'].active: yield v['sub'].unsubscribe() self._subs = {} self.config.extra['other']._tracker.connected = False self.log.warn( '{klass}.onLeave(): rlink remote session left! (realm={realm}, authid={authid}, authrole={authrole}, session={session}, details={details}) {method}', klass=self.__class__.__name__, method=hltype(RLinkLocalSession.onLeave), realm=hluserid(self.config.realm), authid=hluserid(self._authid), authrole=hluserid(self._authrole), session=hlid(self._session_id), details=details) BridgeSession.onLeave(self, details)
def onConnect(self, request): self.log.debug('{func}(request={request})', func=hltype(self.onConnect), request=request) if self.factory.debug_traffic: from twisted.internet import reactor def print_traffic(): self.log.info( "Traffic {peer}: {wire_in} / {wire_out} in / out bytes - {ws_in} / {ws_out} in / out msgs", peer=self.peer, wire_in=self.trafficStats.incomingOctetsWireLevel, wire_out=self.trafficStats.outgoingOctetsWireLevel, ws_in=self.trafficStats.incomingWebSocketMessages, ws_out=self.trafficStats.outgoingWebSocketMessages, ) reactor.callLater(1, print_traffic) print_traffic() # if WebSocket client did not set WS subprotocol, assume "wamp.2.json" # self.STRICT_PROTOCOL_NEGOTIATION = self.factory._requireWebSocketSubprotocol # handle WebSocket opening handshake # protocol, headers = websocket.WampWebSocketServerProtocol.onConnect( self, request) self.log.debug( '{func}: proceed with WebSocket opening handshake for WebSocket subprotocol "{protocol}"', func=hltype(self.onConnect), protocol=hlval(protocol)) try: self._origin = request.origin # transport-level WMAP authentication info # self._authid = None self._authrole = None self._authrealm = None self._authmethod = None self._authextra = None self._authprovider = None # cookie tracking and cookie-based authentication # self._cbtid = None if self.factory._cookiestore: # try to parse an already set cookie from HTTP request headers self._cbtid = self.factory._cookiestore.parse(request.headers) if self._cbtid: self.log.info( '{func}: parsed tracking/authentication cookie cbtid "{cbtid}" from HTTP request headers', func=hltype(self.onConnect), cbtid=hlval(self._cbtid)) else: self.log.info( '{func}: no tracking/authentication cookie cbtid found in HTTP request headers!', func=hltype(self.onConnect)) # if no cookie is set, or it doesn't exist in our database, create a new cookie if self._cbtid is None or not self.factory._cookiestore.exists( self._cbtid): self._cbtid, headers[ 'Set-Cookie'] = self.factory._cookiestore.create() if 'cookie' in self.factory._config: if 'secure' in self.factory._config[ 'cookie'] and self.factory._config['cookie'][ 'secure'] is True: headers['Set-Cookie'] += ';Secure' if 'http_strict' in self.factory._config[ 'cookie'] and self.factory._config['cookie'][ 'http_strict'] is True: headers['Set-Cookie'] += ';HttpOnly' if 'same_site' in self.factory._config['cookie']: headers[ 'Set-Cookie'] += ';SameSite=' + self.factory._config[ 'cookie']['same_site'] self.log.info('{func}: setting new cookie {cookie}', func=hltype(self.onConnect), cookie=hlval(headers['Set-Cookie'], color='yellow')) else: self.log.info( '{func}: tracking/authentication cookie cbtid "{cbtid}" already set and stored', func=hltype(self.onConnect), cbtid=hlval(self._cbtid)) # add this WebSocket connection to the set of connections # associated with the same cookie self.factory._cookiestore.addProto(self._cbtid, self) self.log.debug( "Cookie tracking enabled on WebSocket connection {ws}", ws=self) # if cookie-based authentication is enabled, set auth info from cookie store # if 'auth' in self.factory._config and 'cookie' in self.factory._config[ 'auth']: self._authid, self._authrole, self._authmethod, self._authrealm, self._authextra = self.factory._cookiestore.getAuth( self._cbtid) if self._authid: # there is a cookie set, and the cookie was previously successfully authenticated, # so immediately authenticate the client using that information self._authprovider = 'cookie' self.log.info( '{func} authenticated client via cookie {cookiename}={cbtid} as authid="{authid}", authrole="{authrole}", authmethod="{authmethod}", authprovider="{authprovider}", authrealm="{authrealm}"', func=hltype(self.onConnect), cookiename=self.factory._cookiestore. _cookie_id_field, cbtid=hlval(self._cbtid, color='green'), authid=hlid(self._authid), authrole=hlid(self._authrole), authmethod=hlval(self._authmethod), authprovider=hlval(self._authprovider), authrealm=hlid(self._authrealm)) else: # there is a cookie set, but the cookie wasn't authenticated yet using a different auth method self.log.info( '{func} cookie-based authentication enabled, but cookie {cbtid} is not authenticated yet', cbtid=hlval(self._cbtid, color='blue'), func=hltype(self.onConnect)) else: self.log.info( '{func} cookie-based authentication disabled on connection', func=hltype(self.onConnect)) else: self.log.info( '{func} cookie tracking disabled on WebSocket connection', func=hltype(self.onConnect)) # negotiated WebSocket subprotocol in use, e.g. "wamp.2.cbor.batched" self._transport_details.websocket_protocol = protocol # WebSocket extensions in use. will be filled in onOpen(), see below self._transport_details.websocket_extensions_in_use = None # Crossbar.io tracking ID (for cookie tracking) self._transport_details.http_cbtid = self._cbtid # all HTTP headers as received by the WebSocket client self._transport_details.http_headers_received = request.headers # only customer user headers (such as cookie) self._transport_details.http_headers_sent = headers # accept the WebSocket connection, speaking subprotocol `protocol` # and setting HTTP headers `headers` return protocol, headers except: self.log.failure()
def setAuth(self, cbtid: str, authid: Optional[str], authrole: Optional[str], authmethod: Optional[str], authextra: Optional[Dict[str, Any]], authrealm: Optional[str]) -> bool: """ Update the authentication information associated and stored for an existing cookie (if any). :param cbtid: Cookie value (ID) to update authentication for. :param authid: The WAMP authid a cookie-authenticating session is to be assigned. :param authrole: The WAMP authrole a cookie-authenticating session is to join under. :param authmethod: The WAMP authentication method to be returned to the client performing this cookie-based authentication. :param authextra: The WAMP authentication extra data to be returned to the client performing this cookie-based authentication. :param authrealm: The WAMP realm a cookie-authenticating session is to join. :return: Flag indicating an existing cookie was modified. """ was_existing = False was_modified = False with self._db.begin(write=True) as txn: cookie_oid = self._schema.idx_cookies_by_value[txn, cbtid] if cookie_oid: # read current cookie from database cookie = self._schema.cookies[txn, cookie_oid] assert cookie was_existing = True if (authid != cookie.authid or authrole != cookie.authrole or authmethod != cookie.authmethod or authrealm != cookie.authrealm or authextra != cookie.authextra): cookie.authid = authid cookie.authrole = authrole cookie.authmethod = authmethod cookie.authrealm = authrealm cookie.authextra = authextra # write updated cookie to database self._schema.cookies[txn, cookie.oid] = cookie was_modified = True if was_existing: if was_modified: self.log.info( '{func} cookie with cbtid="{cbtid}" exists, but was updated (authid="{authid}", authrole=' '"{authrole}", authmethod="{authmethod}", authrealm="{authrealm}", authextra={authextra})', func=hltype(self.setAuth), cbtid=hlid(cbtid), authid=hlid(authid), authrole=hlid(authrole), authmethod=hlval(authmethod), authrealm=hlval(authrealm), authextra=pformat(authextra)) else: self.log.info( '{func} cookie with cbtid="{cbtid}" exists and needs no update', func=hltype(self.setAuth), cbtid=hlid(cbtid)) else: self.log.info( '{func} no cookie to modify with cbtid="{cbtid}" exists', func=hltype(self.setAuth), cbtid=hlid(cbtid)) return was_modified