def _on_success(res): msg = None # it is possible this session has disconnected # while onHello was taking place if self._transport is None: self.log.info( "Client session disconnected during authentication", ) return if isinstance(res, types.Accept): custom = { u'x_cb_node_id': self._router_factory._node_id } welcome(res.realm, res.authid, res.authrole, res.authmethod, res.authprovider, res.authextra, custom) elif isinstance(res, types.Challenge): msg = message.Challenge(res.method, res.extra) elif isinstance(res, types.Deny): msg = message.Abort(res.reason, res.message) else: pass if msg: self._transport.send(msg)
def test_on_leave_after_bad_challenge(self): ''' onLeave raises error after onChallenge fails ''' session = MockApplicationSession() exception = RuntimeError("such challenge") session.onLeave = exception_raiser(exception) session.onChallenge = exception_raiser(exception) # make a challenge (which will fail, and then the # subsequent onLeave will also fail) msg = message.Challenge("foo") session.onMessage(msg) self.assertEqual(2, len(session.errors)) self.assertEqual(exception, session.errors[0][0])
def test_on_challenge_deferred(self): session = MockApplicationSession() exception = RuntimeError("such challenge") session.onChallenge = async_exception_raiser(exception) msg = message.Challenge(u"foo") # execute session.onMessage(msg) # we already handle any onChallenge errors as "abort the # connection". So make sure our error showed up in the # fake-transport. self.assertEqual(0, len(session.errors)) self.assertEqual(1, len(session._transport.messages)) reply = session._transport.messages[0] self.assertIsInstance(reply, message.Abort) self.assertEqual("such challenge", reply.message)
def success(res): msg = None if isinstance(res, types.Accept): welcome(self._realm, res.authid, res.authrole, res.authmethod, res.authprovider) elif isinstance(res, types.Challenge): msg = message.Challenge(res.method, res.extra) elif isinstance(res, types.Deny): msg = message.Abort(res.reason, res.message) else: pass if msg: self._transport.send(msg)
def success(res): msg = None if isinstance(res, types.Accept): custom = { u'x_cb_node_id': self._router_factory._node_id } welcome(res.realm, res.authid, res.authrole, res.authmethod, res.authprovider, res.authextra, custom) elif isinstance(res, types.Challenge): msg = message.Challenge(res.method, res.extra) elif isinstance(res, types.Deny): msg = message.Abort(res.reason, res.message) else: pass if msg: self._transport.send(msg)
def send(msg): bytes, _ = serializer.serialize(msg) l = struct.pack("!I", len(bytes)) sys.stdout.write(l) sys.stdout.write(bytes) msgs = [] ## HELLO ## roles = [ role.RolePublisherFeatures(), role.RoleSubscriberFeatures(), role.RoleCallerFeatures(), role.RoleCalleeFeatures() ] msgs.append(message.Hello("foobar", roles)) ## CHALLENGE ## msgs.append(message.Challenge("cookie")) ## HEARTBEAT ## msgs.append(message.Heartbeat(3, 7, "throw me away")) for msg in msgs: send(msg)
def _process_Hello(self, msg): """ We have received a Hello from the frontend client. Now we do any authentication necessary with them and connect to our backend. """ self.log.info('{klass}._process_Hello(msg={msg})', klass=self.__class__.__name__, msg=msg) self._pending_session_id = util.id() self._goodbye_sent = False extra_auth_methods = self._controller.personality.EXTRA_AUTH_METHODS # allow "Personality" classes to add authmethods authmethods = list(extra_auth_methods.keys()) + (msg.authmethods or ['anonymous']) # if the client had a reassigned realm during authentication, restore it from the cookie if hasattr(self.transport, '_authrealm') and self.transport._authrealm: if 'cookie' in authmethods: realm = self.transport._authrealm # noqa authextra = self.transport._authextra # noqa elif self.transport._authprovider == 'cookie': # revoke authentication and invalidate cookie (will be revalidated if following auth is successful) self.transport._authmethod = None self.transport._authrealm = None self.transport._authid = None if hasattr(self.transport, '_cbtid'): self.transport.factory._cookiestore.setAuth(self.transport._cbtid, None, None, None, None, None) else: pass # TLS authentication is not revoked here # already authenticated, eg via HTTP-cookie or TLS-client-certificate authentication if self.transport._authid is not None and (self.transport._authmethod == 'trusted' or self.transport._authprovider in authmethods): msg.realm = self.transport._realm msg.authid = self.transport._authid msg.authrole = self.transport._authrole details = types.HelloDetails( realm=msg.realm, authmethods=authmethods, authid=msg.authid, authrole=msg.authrole, authextra=msg.authextra, session_roles=msg.roles, pending_session=self._pending_session_id ) auth_config = self._transport_config.get('auth', None) # if authentication is _not_ configured, allow anyone to join as "anonymous"! if not auth_config: # we ignore any details.authid the client might have announced, and use # a cookie value or a random value if hasattr(self.transport, "_cbtid") and self.transport._cbtid: # if cookie tracking is enabled, set authid to cookie value authid = self.transport._cbtid else: # if no cookie tracking, generate a random value for authid authid = util.generate_serial_number() auth_config = { 'anonymous': { 'type': 'static', 'authrole': 'anonymous', 'authid': authid, } } self.log.warn('No authentication configured for proxy frontend: using default anonymous access policy for incoming proxy frontend session') for authmethod in authmethods: # invalid authmethod if authmethod not in AUTHMETHOD_MAP and authmethod not in extra_auth_methods: self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='authmethod "{}" not allowed'.format(authmethod))) return # authmethod is valid, but not configured: continue trying other authmethods the client is announcing if authmethod not in auth_config: continue # authmethod not available if authmethod not in AUTHMETHOD_MAP and authmethod not in extra_auth_methods: self.log.debug("client requested valid, but unavailable authentication method {authmethod}", authmethod=authmethod) continue # create instance of authenticator using authenticator class for the respective authmethod authklass = extra_auth_methods[authmethod] if authmethod in extra_auth_methods else AUTHMETHOD_MAP[authmethod] self._pending_auth = authklass( self._pending_session_id, self.transport._transport_info, self._controller, auth_config[authmethod], ) try: # call into authenticator for processing the HELLO message hello_result = self._pending_auth.hello(msg.realm, details) except Exception as e: self.log.failure() self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='Frontend connection accept failed ({})'.format(e))) return self.log.info('{klass}._process_Hello() processed authmethod "{authmethod}" using {authklass}: {hello_result}', klass=self.__class__.__name__, authmethod=authmethod, authklass=authklass, hello_result=hello_result) # if the frontend session is accepted right away (eg when doing "anonymous" authentication), process the # frontend accept .. if isinstance(hello_result, types.Accept): try: # get a backend session mapped to the incoming frontend session session = yield self._accept(hello_result) except Exception as e: self.log.failure() self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='Frontend connection accept failed ({})'.format(e))) return def _on_backend_joined(session, details): # we now got everything! the frontend is authenticated, and a backend session is associated. msg = message.Welcome(self._session_id, ProxyFrontendSession.ROLES, realm=details.realm, authid=details.authid, authrole=details.authrole, authmethod=hello_result.authmethod, authprovider=hello_result.authprovider, authextra=dict(details.authextra or {}, **self._custom_authextra)) self._backend_session = session self.transport.send(msg) self.log.info('Proxy frontend session WELCOME: session_id={session}, session={session}, session_details={details}', session_id=hlid(self._session_id), session=self, details=details) session.on('join', _on_backend_joined) # if the client is required to do an authentication message exchange, answer sending a CHALLENGE message elif isinstance(hello_result, types.Challenge): self.transport.send(message.Challenge(hello_result.method, extra=hello_result.extra)) # if the client is denied right away, answer by sending an ABORT message elif isinstance(hello_result, types.Deny): self.transport.send(message.Abort(hello_result.reason, message=hello_result.message)) # should not arrive here: internal (logic) error else: self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='internal error: unexpected authenticator return type {}'.format(type(hello_result)))) return self.transport.send(message.Abort(ApplicationError.NO_AUTH_METHOD, message='no suitable authmethod found'))
def _process_Hello(self, msg): """ We have received a Hello from the frontend client. Now we do any authentication necessary with them and connect to our backend. """ self.log.info('{klass}._process_Hello(msg={msg})', klass=self.__class__.__name__, msg=msg) self._pending_session_id = util.id() self._goodbye_sent = False authmethods = msg.authmethods or ['anonymous'] details = types.HelloDetails( realm=msg.realm, authmethods=authmethods, authid=msg.authid, authrole=msg.authrole, authextra=msg.authextra, session_roles=msg.roles, pending_session=self._pending_session_id ) auth_config = self._transport_config.get('auth', None) # allow "Personality" classes to add authmethods extra_auth_methods = dict() # if self._router_factory._worker: # personality = self._router_factory._worker.personality # extra_auth_methods = personality.EXTRA_AUTH_METHODS for authmethod in authmethods: # invalid authmethod if authmethod not in AUTHMETHOD_MAP and authmethod not in extra_auth_methods: self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='authmethod "{}" not allowed'.format(authmethod))) return # authmethod is valid, but not configured: continue trying other authmethods the client is announcing if authmethod not in auth_config: continue # authmethod not available if authmethod not in AUTHMETHOD_MAP and authmethod not in extra_auth_methods: self.log.debug("client requested valid, but unavailable authentication method {authmethod}", authmethod=authmethod) continue PendingAuthKlass = AUTHMETHOD_MAP[authmethod] # pending_session_id, transport_info, realm_container, config self._pending_auth = PendingAuthKlass( self._pending_session_id, self.transport._transport_info, # FIXME * no_such_role self._controller, auth_config[authmethod], ) try: hello_result = self._pending_auth.hello(msg.realm, details) except Exception as e: self.log.failure() self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='Frontend connection accept failed ({})'.format(e))) return self.log.info('{klass}._process_Hello() processed authmethod "{authmethod}" using {authklass}: {hello_result}', klass=self.__class__.__name__, authmethod=authmethod, authklass=PendingAuthKlass, hello_result=hello_result) if isinstance(hello_result, types.Accept): try: session = yield self._accept(hello_result) except Exception as e: self.log.failure() self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='Frontend connection accept failed ({})'.format(e))) return def _on_backend_joined(session, details): msg = message.Welcome(self._session_id, ProxyFrontendSession.ROLES, realm=details.realm, authid=details.authid, authrole=details.authrole, authmethod=hello_result.authmethod, authprovider=hello_result.authprovider, authextra=dict(details.authextra or {}, **self._custom_authextra)) self._backend_session = session self.transport.send(msg) self.log.info('Proxy frontend session WELCOME: session_id={session}, session={session}, session_details={details}', session_id=hlid(self._session_id), session=self, details=details) session.on('join', _on_backend_joined) elif isinstance(hello_result, types.Challenge): self.transport.send(message.Challenge(hello_result.method, extra=hello_result.extra)) elif isinstance(hello_result, types.Deny): self.transport.send(message.Abort(hello_result.reason, message=hello_result.message)) else: # should not arrive here: logic error self.transport.send(message.Abort(ApplicationError.AUTHENTICATION_FAILED, message='internal error: unexpected authenticator return type {}'.format(type(hello_result)))) return self.transport.send(message.Abort(ApplicationError.NO_AUTH_METHOD, message='no suitable authmethod found'))