def onHello(self, realm, details): try: # check if the realm the session wants to join actually exists # if realm not in self._router_factory: return types.Deny( ApplicationError.NO_SUCH_REALM, message="no realm '{}' exists on this router".format( realm)) authmethods = details.authmethods or ["anonymous"] # perform authentication # if self._transport._authid is not None and ( self._transport._authmethod == u'trusted' or self._transport._authprovider in authmethods): # already authenticated .. e.g. via HTTP Cookie or TLS client-certificate # check if role still exists on realm # allow = self._router_factory[realm].has_role( self._transport._authrole) if allow: return types.Accept( authid=self._transport._authid, authrole=self._transport._authrole, authmethod=self._transport._authmethod, authprovider=self._transport._authprovider) else: return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "session was previously authenticated (via transport), but role '{}' no longer exists on realm '{}'" .format(self._transport._authrole, realm)) else: # if authentication is enabled on the transport .. # if "auth" in self._transport_config: # iterate over authentication methods announced by client .. # for authmethod in authmethods: # .. and if the configuration has an entry for the authmethod # announced, process .. if authmethod in self._transport_config["auth"]: # "WAMP-Challenge-Response" authentication # if authmethod == u"wampcra": cfg = self._transport_config['auth']['wampcra'] if cfg['type'] == 'static': if details.authid in cfg.get('users', {}): user = cfg['users'][details.authid] # the authid the session will be authenticated as is from the user data, or when # the user data doesn't contain an authid, from the HELLO message the client sent # authid = user.get( "authid", details.authid) # construct a pending WAMP-CRA authentication # self._pending_auth = PendingAuthWampCra( details.pending_session, authid, user['role'], u'static', user['secret'].encode('utf8')) # send challenge to client # extra = { u'challenge': self._pending_auth.challenge } # when using salted passwords, provide the client with # the salt and then PBKDF2 parameters used # if 'salt' in user: extra[u'salt'] = user['salt'] extra[u'iterations'] = user.get( 'iterations', 1000) extra[u'keylen'] = user.get( 'keylen', 32) return types.Challenge( u'wampcra', extra) else: return types.Deny( message= "no user with authid '{}' in user database" .format(details.authid)) elif cfg['type'] == 'dynamic': # call the configured dynamic authenticator procedure # via the router's service session # service_session = self._router_factory.get( realm)._realm.session session_details = { # forward transport level details of the WAMP session that # wishes to authenticate 'transport': self._transport._transport_info, # the following WAMP session ID will be assigned to the session # if (and only if) the subsequent authentication succeeds. 'session': self._pending_session_id } d = service_session.call( cfg['authenticator'], realm, details.authid, session_details) def on_authenticate_ok(user): # the authid the session will be authenticated as is from the dynamic # authenticator response, or when the response doesn't contain an authid, # from the HELLO message the client sent # authid = user.get( "authid", details.authid) # construct a pending WAMP-CRA authentication # self._pending_auth = PendingAuthWampCra( details.pending_session, authid, user['role'], u'dynamic', user['secret'].encode('utf8')) # send challenge to client # extra = { u'challenge': self._pending_auth.challenge } # when using salted passwords, provide the client with # the salt and the PBKDF2 parameters used # if 'salt' in user: extra[u'salt'] = user['salt'] extra[u'iterations'] = user.get( 'iterations', 1000) extra[u'keylen'] = user.get( 'keylen', 32) return types.Challenge( u'wampcra', extra) def on_authenticate_error(err): error = None message = "dynamic WAMP-CRA credential getter failed: {}".format( err) if isinstance(err.value, ApplicationError): error = err.value.error if err.value.args and len( err.value.args): message = str( err.value.args[0] ) # exception does not need to contain a string return types.Deny(error, message) d.addCallbacks(on_authenticate_ok, on_authenticate_error) return d else: return types.Deny( message= "illegal WAMP-CRA authentication config (type '{0}' is unknown)" .format(cfg['type'])) # WAMP-Ticket authentication # elif authmethod == u"ticket": cfg = self._transport_config['auth']['ticket'] # use static principal database from configuration # if cfg['type'] == 'static': if details.authid in cfg.get( 'principals', {}): principal = cfg['principals'][ details.authid] # the authid the session will be authenticated as is from the principal data, or when # the principal data doesn't contain an authid, from the HELLO message the client sent # authid = principal.get( "authid", details.authid) self._pending_auth = PendingAuthTicket( realm, authid, principal['role'], u'static', principal['ticket'].encode('utf8')) return types.Challenge(u'ticket') else: return types.Deny( message= "no principal with authid '{}' in principal database" .format(details.authid)) # use configured procedure to dynamically get a ticket for the principal # elif cfg['type'] == 'dynamic': self._pending_auth = PendingAuthTicket( realm, details.authid, None, cfg['authenticator'], None) return types.Challenge(u'ticket') else: return types.Deny( message= "illegal WAMP-Ticket authentication config (type '{0}' is unknown)" .format(cfg['type'])) # "Mozilla Persona" authentication # elif authmethod == u"mozilla_persona": cfg = self._transport_config['auth'][ 'mozilla_persona'] audience = cfg.get('audience', self._transport._origin) provider = cfg.get( 'provider', "https://verifier.login.persona.org/verify" ) # authrole mapping # authrole = cfg.get('role', 'anonymous') # check if role exists on realm anyway # if not self._router_factory[realm].has_role( authrole): return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "authentication failed - realm '{}' has no role '{}'" .format(realm, authrole)) # ok, now challenge the client for doing Mozilla Persona auth. # self._pending_auth = PendingAuthPersona( provider, audience, authrole) return types.Challenge("mozilla-persona") # "Anonymous" authentication # elif authmethod == u"anonymous": cfg = self._transport_config['auth'][ 'anonymous'] # authrole mapping # authrole = cfg.get('role', 'anonymous') # check if role exists on realm anyway # if not self._router_factory[realm].has_role( authrole): return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "authentication failed - realm '{}' has no role '{}'" .format(realm, authrole)) # authid generation if 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.newid(24) self._transport._authid = authid self._transport._authrole = authrole self._transport._authmethod = authmethod return types.Accept( authid=authid, authrole=authrole, authmethod=self._transport._authmethod) # "Cookie" authentication # elif authmethod == u"cookie": # the client requested cookie authentication, but there is 1) no cookie set, # or 2) a cookie set, but that cookie wasn't authenticated before using # a different auth method (if it had been, we would never have entered here, since then # auth info would already have been extracted from the transport) # consequently, we skip this auth method and move on to next auth method. pass # Unknown authentication method # else: self.log.info("unknown authmethod '{}'".format( authmethod)) return types.Deny( message="unknown authentication method {}". format(authmethod)) # if authentication is configured, by default, deny. # return types.Deny( message= "authentication using method '{}' denied by configuration" .format(authmethod)) else: # if authentication is _not_ configured, by default, allow anyone. # # authid generation if 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.newid(24) return types.Accept(authid=authid, authrole="anonymous", authmethod="anonymous") except Exception as e: traceback.print_exc() return types.Deny(message="internal error: {}".format(e))
def onHello(self, realm, details): try: ## check if the realm the session wants to join actually exists ## if realm not in self._router_factory: return types.Deny( ApplicationError.NO_SUCH_REALM, message="no realm '{}' exists on this router".format( realm)) ## perform authentication ## if self._transport._authid is not None: ## already authenticated .. e.g. via cookie ## check if role still exists on realm ## allow = self._router_factory[realm].has_role( self._transport._authrole) if allow: return types.Accept(authid=self._transport._authid, authrole=self._transport._authrole, authmethod=self._transport._authmethod, authprovider='transport') else: return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "session was previously authenticated (via transport), but role '{}' no longer exists on realm '{}'" .format(self._transport._authrole, realm)) else: ## if authentication is enabled on the transport .. ## if "auth" in self._transport_config: ## iterate over authentication methods announced by client .. ## for authmethod in details.authmethods or ["anonymous"]: ## .. and if the configuration has an entry for the authmethod ## announced, process .. if authmethod in self._transport_config["auth"]: ## "WAMP-Challenge-Response" authentication ## if authmethod == u"wampcra": cfg = self._transport_config['auth']['wampcra'] if cfg['type'] == 'static': if details.authid in cfg.get('users', {}): user = cfg['users'][details.authid] self._pending_auth = PendingAuthWampCra( details.pending_session, details.authid, user['role'], u'static', user['secret'].encode('utf8')) ## send challenge to client ## extra = { u'challenge': self._pending_auth.challenge } ## when using salted passwords, provide the client with ## the salt and then PBKDF2 parameters used if 'salt' in user: extra[u'salt'] = user['salt'] extra[u'iterations'] = user.get( 'iterations', 1000) extra[u'keylen'] = user.get( 'keylen', 32) return types.Challenge( u'wampcra', extra) else: return types.Deny( message= "no user with authid '{}' in user database" .format(details.authid)) elif cfg['type'] == 'dynamic': ## Get the Crossbar.io service session on the router/realm ## to issue the WAMP call to the custom authorizer ## router = self._router_factory.get(realm) service_session = router._realm.session d = service_session.call( cfg['authenticator'], realm, details.authid) def on_authenticate_ok(user): ## construct a pending WAMP-CRA authentication ## self._pending_auth = PendingAuthWampCra( details.pending_session, details.authid, user['role'], u'dynamic', user['secret'].encode('utf8')) ## send challenge to client ## extra = { u'challenge': self._pending_auth.challenge } ## when using salted passwords, provide the client with ## the salt and the PBKDF2 parameters used ## if 'salt' in user: extra[u'salt'] = user['salt'] extra[u'iterations'] = user.get( 'iterations', 1000) extra[u'keylen'] = user.get( 'keylen', 32) return types.Challenge( u'wampcra', extra) def on_authenticate_error(err): error = None message = "dynamic WAMP-CRA credential getter failed: {}".format( err) if isinstance(err.value, ApplicationError): error = err.value.error if err.value.args and len( err.value.args): message = err.value.args[0] return types.Deny(error, message) d.addCallbacks(on_authenticate_ok, on_authenticate_error) return d else: return types.Deny( message= "illegal WAMP-CRA config (type '{0}' is unknown)" .format(cfg['type'])) ## "Mozilla Persona" authentication ## elif authmethod == u"mozilla_persona": cfg = self._transport_config['auth'][ 'mozilla_persona'] audience = cfg.get('audience', self._transport._origin) provider = cfg.get( 'provider', "https://verifier.login.persona.org/verify" ) ## authrole mapping ## authrole = cfg.get('role', 'anonymous') ## check if role exists on realm anyway ## if not self._router_factory[realm].has_role( authrole): return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "authentication failed - realm '{}' has no role '{}'" .format(realm, authrole)) ## ok, now challenge the client for doing Mozilla Persona auth. ## self._pending_auth = PendingAuthPersona( provider, audience, authrole) return types.Challenge("mozilla-persona") ## "Anonymous" authentication ## elif authmethod == u"anonymous": cfg = self._transport_config['auth'][ 'anonymous'] ## authrole mapping ## authrole = cfg.get('role', 'anonymous') ## check if role exists on realm anyway ## if not self._router_factory[realm].has_role( authrole): return types.Deny( ApplicationError.NO_SUCH_ROLE, message= "authentication failed - realm '{}' has no role '{}'" .format(realm, authrole)) ## authid generation ## if 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.newid(24) self._transport._authid = authid self._transport._authrole = authrole self._transport._authmethod = authmethod return types.Accept( authid=authid, authrole=authrole, authmethod=self._transport._authmethod) ## "Cookie" authentication ## elif authmethod == u"cookie": pass # if self._transport._cbtid: # cookie = self._transport.factory._cookies[self._transport._cbtid] # authid = cookie['authid'] # authrole = cookie['authrole'] # authmethod = "cookie.{}".format(cookie['authmethod']) # return types.Accept(authid = authid, authrole = authrole, authmethod = authmethod) # else: # return types.Deny() else: log.msg("unknown authmethod '{}'".format( authmethod)) return types.Deny( message="unknown authentication method {}". format(authmethod)) ## if authentication is configured, by default, deny. ## return types.Deny( message= "authentication using method '{}' denied by configuration" .format(authmethod)) else: ## if authentication is _not_ configured, by default, allow anyone. ## ## authid generation ## if 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.newid(24) return types.Accept(authid=authid, authrole="anonymous", authmethod="anonymous") except Exception as e: traceback.print_exc() return types.Deny(message="internal error: {}".format(e))