Beispiel #1
0
                def done(res):
                    res = json.loads(res)
                    try:
                        if res['status'] == 'okay':

                            # awesome: Mozilla Persona successfully authenticated the user
                            self._transport._authid = res['email']
                            self._transport._authrole = self._pending_auth.role
                            self._transport._authmethod = 'mozilla_persona'

                            log.msg("Authenticated user {} with role {}".format(self._transport._authid, self._transport._authrole))
                            dres.callback(types.Accept(authid=self._transport._authid, authrole=self._transport._authrole, authmethod=self._transport._authmethod))

                            # remember the user's auth info (this marks the cookie as authenticated)
                            if self._transport._cbtid and self._transport.factory._cookiestore:
                                cs = self._transport.factory._cookiestore
                                cs.setAuth(self._transport._cbtid, self._transport._authid, self._transport._authrole, self._transport._authmethod)

                                # kick all sessions using same cookie (but not _this_ connection)
                                if True:
                                    for proto in cs.getProtos(self._transport._cbtid):
                                        if proto and proto != self._transport:
                                            try:
                                                proto.close()
                                            except Exception as e:
                                                pass
                        else:
                            log.msg("Authentication failed!")
                            log.msg(res)
                            dres.callback(types.Deny(reason="wamp.error.authorization_failed", message=res.get("reason", None)))
                    except Exception as e:
                        log.msg("internal error during authentication verification: {}".format(e))
                        dres.callback(types.Deny(reason="wamp.error.internal_error", message=str(e)))
Beispiel #2
0
 def _accept(self):
     return types.Accept(realm=self._realm,
                         authid=self._authid,
                         authrole=self._authrole,
                         authmethod=self._authmethod,
                         authprovider=self._authprovider,
                         authextra=self._authextra)
Beispiel #3
0
    def onAuthenticate(self, router_session, signature, extra):
        try:
            challenge = router_session.challenge
            authid = challenge["authid"]
            if challenge == None:
                return
            if router_session.challenge.get("authmethod") != u"cookie":
                return
            for field in ["authrole", "authmethod", "authprovider"]:
                if field not in challenge:
                    # Challenge not in expected format. It was probably
                    #   created by another plugin.
                    return
        except Exception as e:
            # let another plugin handle this
            return

        cookie = self.cookies.get(authid)
        if cookie != signature:
            log("Failed cookie login for %s." % challenge["authid"])
            return types.Deny(message=u"Invalid cookie.")

        log("Successful cookie login for %s." % challenge["authid"])
        return types.Accept(authid=challenge["authid"],
                            authrole=challenge["authrole"],
                            authmethod=challenge["authmethod"],
                            authprovider=challenge["authprovider"])
Beispiel #4
0
 def onHello(self, realm, details):
     print "onHello: {} {}".format(realm, details)
     if self._transport._authenticated is not None:
         return types.Accept(authid=self._transport._authenticated)
     else:
         return types.Challenge("mozilla-persona")
     return accept
Beispiel #5
0
    def onAuthenticate(self, signature, extra):
        """
        Callback fired when a client responds to an authentication challenge.
        """
        print("onAuthenticate: {} {}".format(signature, extra))

        # if there is a pending auth, and the signature provided by client matches ..
        if self._pending_auth:

            if signature == self._pending_auth.signature:

                # accept the client
                return types.Accept(
                    authid=self._pending_auth.authid,
                    authrole=self._pending_auth.authrole,
                    authmethod=self._pending_auth.authmethod,
                    authprovider=self._pending_auth.authprovider)
            else:

                # deny client
                return types.Deny(message=u"signature is invalid")
        else:

            # deny client
            return types.Deny(message=u"no pending authentication")
Beispiel #6
0
 def onHello(self, realm, details):
     """On receiving a hello, issue a Challenge to the client"""
     if getattr(self._transport, '_authenticated', None) is not None:
         return types.Accept(authid=self._transport._authenticated)
     else:
         return types.Challenge('userpass', {
             'timestamp': time.time(),
         })
Beispiel #7
0
 def on_success(response):
     content = json.loads(response)
     if content.get('success'):
         username = content.get('username')
         if username:
             return types.Accept(content['username'])
     log.msg('Rejected login: %s' % (content, ))
     return types.Deny()
Beispiel #8
0
 def onHello(self, router_session, realm, details):
     for authmethod in details.authmethods:
         if authmethod == u"anonymous":
             log("Successful anonymous login (ID: %s)." % \
                     details.pending_session)
             return types.Accept(authid=u"anonymous",
                                 authrole=u"anonymous",
                                 authmethod=u"anonymous",
                                 authprovider=u"anonymous")
Beispiel #9
0
    def onHello(self, realm, details):
        """
        Callback fired when client wants to attach session.
        """
        print("MyRouterSession.onHello: {} {}".format(realm, details))

        for authmethod in details.authmethods:
            if authmethod == u"cookie" and self._transport._authenticated is not None:
                # already authenticated via Cookie on transport
                return types.Accept(authid=self._transport._authenticated, authrole="user", authmethod="cookie")
            elif authmethod == u"mozilla-persona":
                # not yet authenticated: send challenge
                return types.Challenge("mozilla-persona")

        return types.Deny()
Beispiel #10
0
    def onAuthenticate(self, router_session, signature, extra):
        try:
            challenge = router_session.challenge
            if challenge == None:
                return
            if router_session.challenge.get("authmethod") != u"wampcra":
                return
            for field in ["authid", "authrole", "authmethod", "authprovider"]:
                if field not in challenge:
                    # Challenge not in expected format. It was probably
                    #   created by another plugin.
                    return

            if not router_session.challenge or not router_session.signature:
                log("Failed wampcra login for %s." % challenge["authid"])
                return types.Deny(message=u"No pending authentication.")

            if len(signature) != len(router_session.signature):
                log("Failed wampcra login for %s." % challenge["authid"])
                return types.Deny(message=u"Invalid signature.")

            success = True

            # Check each character to prevent HMAC timing attacks. This is
            #   really not an issue since each challenge gets a new nonce,
            #   but better safe than sorry.
            for i in range(len(router_session.signature)):
                if signature[i] != router_session.signature[i]:
                    success = False

            # Reject the user if we did not actually find them in the database.
            if not router_session.exists:
                log("User %s not found." % challenge["authid"])
                success = False

            if success:
                log("Successful wampcra login for %s." % challenge["authid"])
                return types.Accept(authid=challenge["authid"],
                                    authrole=challenge["authrole"],
                                    authmethod=challenge["authmethod"],
                                    authprovider=challenge["authprovider"])

            log("Failed wampcra login for %s." % challenge["authid"])
            return types.Deny(message=u"Invalid signature.")

        except:
            # let another plugin handle this
            return
Beispiel #11
0
                    def on_authenticate_ok(principal):

                        if isinstance(principal, dict):
                            # dynamic ticket authenticator returned a dictionary (new)
                            authid = principal.get("authid", self._pending_auth.authid)
                            authrole = principal["role"]
                        else:
                            # backwards compatibility: dynamic ticket authenticator
                            # was expected to return a role directly
                            authid = self._pending_auth.authid
                            authrole = principal

                        return types.Accept(authid=authid,
                                            authrole=authrole,
                                            authmethod=self._pending_auth.authmethod,
                                            authprovider=self._pending_auth.authprovider)
Beispiel #12
0
        def done(res):
            res = json.loads(res)
            try:
                if res['status'] == 'okay':
                    # Mozilla Persona successfully authenticated the user

                    # remember the user's email address. this marks the cookie as
                    # authenticated
                    self._transport.factory._cookies[self._transport._cbtid]['authenticated'] = res['email']

                    log.msg("Authenticated user {}".format(res['email']))
                    dres.callback(types.Accept(authid=res['email'], authrole="user", authmethod="mozilla-persona"))
                else:
                    log.msg("Authentication failed!")
                    dres.callback(types.Deny())
            except Exception as e:
                print "ERRR", e
    def onHello(self, realm, details):
        print("On hello: {}".format(details))
        if u"exodoc-ticket" not in details.authmethods:
            return types.Deny(
                u"wamp.error.not_authorized",
                message=u"Only 'exodoc-ticket' supported as authmethods")
        if not details.authid.startswith("good-ticket-"):
            return types.Deny(u"wamp.error.not_authorized",
                              message=u"Bad credential")

        self._authid = details.authid[len("good-ticket-"):]
        self._authmethod = u"exodoc-ticket"
        self._authrole = u"chatter"
        self._authprovider = u"redis-token"
        return types.Accept(
            authid=self._authid,
            authrole=self._authrole,
            authmethod=self._authmethod,
            authprovider=self._authprovider,
        )
Beispiel #14
0
    def onAuthenticate(self, signature, extra):
        """
      Callback fired when a client responds to an authentication challenge.
      """
        print("onAuthenticate: {} {}".format(signature, extra))

        ## if there is a pending auth, and the signature provided by client matches ..
        if self._pending_auth:
            if signature == auth.compute_totp(self._pending_auth.secret) or \
               signature == auth.compute_totp(self._pending_auth.secret, 1) or \
               signature == auth.compute_totp(self._pending_auth.secret, -1):
                ## accept the client
                return types.Accept(
                    authid=self._pending_auth.authid,
                    authrole=self._pending_auth.authrole,
                    authmethod=self._pending_auth.authmethod,
                    authprovider=self._pending_auth.authprovider)

        ## deny client
        return types.Deny()
Beispiel #15
0
    def onHello(self, realm, details):

        try:
            # default authentication method is "WAMP-Anonymous" if client doesn't specify otherwise
            authmethods = details.authmethods or [u'anonymous']

            # if the client had a reassigned realm during authentication, restore it from the cookie
            if hasattr(self._transport,
                       '_authrealm') and self._transport._authrealm:
                realm = self._transport._authrealm

            # 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(
                        realm=realm,
                        authid=self._transport._authid,
                        authrole=self._transport._authrole,
                        authmethod=self._transport._authmethod,
                        authprovider=self._transport._authprovider,
                        authextra=None)
                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:
                auth_config = self._transport_config.get(u'auth', None)

                if not auth_config:
                    # if authentication is _not_ configured, allow anyone to join as "anonymous"!

                    # .. but don't if the client isn't ready/willing to go on "anonymous"
                    if u'anonymous' not in authmethods:
                        return types.Deny(
                            ApplicationError.NO_AUTH_METHOD,
                            message=
                            u'cannot authenticate using any of the offered authmethods {}'
                            .format(authmethods))

                    if not realm:
                        return types.Deny(ApplicationError.NO_SUCH_REALM,
                                          message=u'no realm requested')

                    if realm not in self._router_factory:
                        return types.Deny(
                            ApplicationError.NO_SUCH_REALM,
                            message=u'no realm "{}" exists on this router'.
                            format(realm))

                    # 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()

                    return types.Accept(realm=realm,
                                        authid=authid,
                                        authrole=u'anonymous',
                                        authmethod=u'anonymous',
                                        authprovider=u'static',
                                        authextra=None)

                else:
                    # iterate over authentication methods announced by client ..
                    for authmethod in authmethods:

                        # invalid authmethod
                        if authmethod not in AUTHMETHODS:
                            return types.Deny(
                                message=u'invalid authmethod "{}"'.format(
                                    authmethod))

                        # authmethod not configured
                        if authmethod not in auth_config:
                            self.log.debug(
                                "client requested valid, but unconfigured authentication method {authmethod}",
                                authmethod=authmethod)
                            continue

                        # authmethod not available
                        if authmethod not in AUTHMETHOD_MAP:
                            self.log.debug(
                                "client requested valid, but unavailable authentication method {authmethod}",
                                authmethod=authmethod)
                            continue

                        # WAMP-Anonymous, WAMP-Ticket, WAMP-CRA, WAMP-TLS, WAMP-Cryptosign
                        if authmethod in [
                                u'anonymous', u'ticket', u'wampcra', u'tls',
                                u'cryptosign'
                        ]:
                            PendingAuthKlass = AUTHMETHOD_MAP[authmethod]
                            self._pending_auth = PendingAuthKlass(
                                self, auth_config[authmethod])
                            return self._pending_auth.hello(realm, details)

                        # WAMP-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.
                            continue

                        else:
                            # should not arrive here
                            raise Exception("logic error")

                    # no suitable authmethod found!
                    return types.Deny(
                        ApplicationError.NO_AUTH_METHOD,
                        message=
                        u'cannot authenticate using any of the offered authmethods {}'
                        .format(authmethods))

        except Exception as e:
            self.log.failure('internal error: {log_failure.value}')
            self.log.critical("internal error: {msg}", msg=str(e))
            return types.Deny(message=u'internal error: {}'.format(e))
Beispiel #16
0
    def onAuthenticate(self, signature, extra):
        """
        Callback fired when a client responds to an authentication challenge.
        """
        print("onAuthenticate: {} {}".format(signature, extra))

        # if there is a pending auth, check the challenge response. The specifics
        # of how to check depend on the authentication method
        #
        if self._pending_auth:

            # WAMP-CRA
            #
            if isinstance(self._pending_auth, PendingAuthWampCra):

                if signature == self._pending_auth.signature:
                    # WAMP-CRA authentication signature was valid: accept the client
                    #
                    return types.Accept(
                        authid=self._pending_auth.authid,
                        authrole=self._pending_auth.authrole,
                        authmethod=self._pending_auth.authmethod,
                        authprovider=self._pending_auth.authprovider)
                else:
                    # WAMP-CRA authentication signature was invalid: deny client
                    #
                    return types.Deny(message=u"signature is invalid")

            # WAMP-Ticket
            #
            elif isinstance(self._pending_auth, PendingAuthTicket):

                # when doing WAMP-Ticket from static configuration, the ticket we
                # expect was store on the pending authentication object and we just compare ..
                #
                if self._pending_auth.authprovider == 'static':
                    if signature == self._pending_auth.ticket:
                        # WAMP-Ticket authentication ticket was valid: accept the client
                        #
                        return types.Accept(
                            authid=self._pending_auth.authid,
                            authrole=self._pending_auth.authrole,
                            authmethod=self._pending_auth.authmethod,
                            authprovider=self._pending_auth.authprovider)
                    else:
                        # WAMP-Ticket authentication ticket was invalid: deny client
                        #
                        return types.Deny(message=u"ticket is invalid")

                # WAMP-Ticket dynamic ..
                #
                else:
                    # call the configured dynamic authenticator procedure
                    # via the router's service session
                    #
                    service_session = self._router_factory.get(
                        self._pending_auth.realm)._realm.session

                    d = service_session.call(self._pending_auth.authprovider,
                                             self._pending_auth.realm,
                                             self._pending_auth.authid,
                                             signature)

                    def on_authenticate_ok(principal):

                        if isinstance(principal, dict):
                            # dynamic ticket authenticator returned a dictionary (new)
                            authid = principal.get("authid",
                                                   self._pending_auth.authid)
                            authrole = principal["role"]
                        else:
                            # backwards compatibility: dynamic ticket authenticator
                            # was expected to return a role directly
                            authid = self._pending_auth.authid
                            authrole = principal

                        return types.Accept(
                            authid=authid,
                            authrole=authrole,
                            authmethod=self._pending_auth.authmethod,
                            authprovider=self._pending_auth.authprovider)

                    def on_authenticate_error(err):
                        error = None
                        message = "dynamic WAMP-Ticket 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

            elif isinstance(self._pending_auth, PendingAuthPersona):

                dres = Deferred()

                # The client did it's Mozilla Persona authentication thing
                # and now wants to verify the authentication and login.
                assertion = signature
                audience = str(self._pending_auth.audience
                               )  # eg "http://192.168.1.130:8080/"
                provider = str(
                    self._pending_auth.provider
                )  # eg "https://verifier.login.persona.org/verify"

                # To verify the authentication, we need to send a HTTP/POST
                # to Mozilla Persona. When successful, Persona will send us
                # back something like:

                # {
                #    "audience": "http://192.168.1.130:8080/",
                #    "expires": 1393681951257,
                #    "issuer": "gmail.login.persona.org",
                #    "email": "*****@*****.**",
                #    "status": "okay"
                # }

                headers = {'Content-Type': 'application/x-www-form-urlencoded'}
                body = urllib.urlencode({
                    'audience': audience,
                    'assertion': assertion
                })

                from twisted.web.client import getPage
                d = getPage(url=provider,
                            method='POST',
                            postdata=body,
                            headers=headers)

                self.log.info("Authentication request sent.")

                def done(res):
                    res = json.loads(res)
                    try:
                        if res['status'] == 'okay':

                            # awesome: Mozilla Persona successfully authenticated the user
                            self._transport._authid = res['email']
                            self._transport._authrole = self._pending_auth.role
                            self._transport._authmethod = 'mozilla_persona'

                            self.log.info(
                                "Authenticated user {} with role {}".format(
                                    self._transport._authid,
                                    self._transport._authrole))
                            dres.callback(
                                types.Accept(
                                    authid=self._transport._authid,
                                    authrole=self._transport._authrole,
                                    authmethod=self._transport._authmethod))

                            # remember the user's auth info (this marks the cookie as authenticated)
                            if self._transport._cbtid and self._transport.factory._cookiestore:
                                cs = self._transport.factory._cookiestore
                                cs.setAuth(self._transport._cbtid,
                                           self._transport._authid,
                                           self._transport._authrole,
                                           self._transport._authmethod)

                                # kick all sessions using same cookie (but not _this_ connection)
                                if True:
                                    for proto in cs.getProtos(
                                            self._transport._cbtid):
                                        if proto and proto != self._transport:
                                            try:
                                                proto.close()
                                            except Exception as e:
                                                pass
                        else:
                            self.log.info("Authentication failed!")
                            self.log.info(res)
                            dres.callback(
                                types.Deny(
                                    reason="wamp.error.authorization_failed",
                                    message=res.get("reason", None)))
                    except Exception as e:
                        self.log.info(
                            "internal error during authentication verification: {}"
                            .format(e))
                        dres.callback(
                            types.Deny(reason="wamp.error.internal_error",
                                       message=str(e)))

                def error(err):
                    self.log.info("Authentication request failed: {}".format(
                        err.value))
                    dres.callback(
                        types.Deny(
                            reason="wamp.error.authorization_request_failed",
                            message=str(err.value)))

                d.addCallbacks(done, error)

                return dres

            else:

                self.log.info("don't know how to authenticate")

                return types.Deny()

        else:

            # deny client
            return types.Deny(message=u"no pending authentication")
Beispiel #17
0
    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))
Beispiel #18
0
    def onAuthenticate(self, signature, extra):
        """
      Callback fired when a client responds to an authentication challenge.
      """
        print("onAuthenticate: {} {}".format(signature, extra))

        ## if there is a pending auth, and the signature provided by client matches ..
        if self._pending_auth:

            if isinstance(self._pending_auth, PendingAuthWampCra):

                if signature == self._pending_auth.signature:

                    ## accept the client
                    return types.Accept(
                        authid=self._pending_auth.authid,
                        authrole=self._pending_auth.authrole,
                        authmethod=self._pending_auth.authmethod,
                        authprovider=self._pending_auth.authprovider)
                else:

                    ## deny client
                    return types.Deny(message=u"signature is invalid")

            elif isinstance(self._pending_auth, PendingAuthPersona):

                dres = Deferred()

                ## The client did it's Mozilla Persona authentication thing
                ## and now wants to verify the authentication and login.
                assertion = signature
                audience = str(self._pending_auth.audience
                               )  # eg "http://192.168.1.130:8080/"
                provider = str(
                    self._pending_auth.provider
                )  # eg "https://verifier.login.persona.org/verify"

                ## To verify the authentication, we need to send a HTTP/POST
                ## to Mozilla Persona. When successful, Persona will send us
                ## back something like:

                # {
                #    "audience": "http://192.168.1.130:8080/",
                #    "expires": 1393681951257,
                #    "issuer": "gmail.login.persona.org",
                #    "email": "*****@*****.**",
                #    "status": "okay"
                # }

                headers = {'Content-Type': 'application/x-www-form-urlencoded'}
                body = urllib.urlencode({
                    'audience': audience,
                    'assertion': assertion
                })

                from twisted.web.client import getPage
                d = getPage(url=provider,
                            method='POST',
                            postdata=body,
                            headers=headers)

                log.msg("Authentication request sent.")

                def done(res):
                    res = json.loads(res)
                    try:
                        if res['status'] == 'okay':

                            ## awesome: Mozilla Persona successfully authenticated the user
                            self._transport._authid = res['email']
                            self._transport._authrole = self._pending_auth.role
                            self._transport._authmethod = 'mozilla_persona'

                            log.msg(
                                "Authenticated user {} with role {}".format(
                                    self._transport._authid,
                                    self._transport._authrole))
                            dres.callback(
                                types.Accept(
                                    authid=self._transport._authid,
                                    authrole=self._transport._authrole,
                                    authmethod=self._transport._authmethod))

                            ## remember the user's auth info (this marks the cookie as authenticated)
                            if self._transport._cbtid and self._transport.factory._cookiestore:
                                cs = self._transport.factory._cookiestore
                                cs.setAuth(self._transport._cbtid,
                                           self._transport._authid,
                                           self._transport._authrole,
                                           self._transport._authmethod)

                                ## kick all sessions using same cookie (but not _this_ connection)
                                if True:
                                    for proto in cs.getProtos(
                                            self._transport._cbtid):
                                        if proto and proto != self._transport:
                                            try:
                                                proto.close()
                                            except Exception as e:
                                                pass
                        else:
                            log.msg("Authentication failed!")
                            log.msg(res)
                            dres.callback(
                                types.Deny(
                                    reason="wamp.error.authorization_failed",
                                    message=res.get("reason", None)))
                    except Exception as e:
                        log.msg(
                            "internal error during authentication verification: {}"
                            .format(e))
                        dres.callback(
                            types.Deny(reason="wamp.error.internal_error",
                                       message=str(e)))

                def error(err):
                    log.msg("Authentication request failed: {}".format(
                        err.value))
                    dres.callback(
                        types.Deny(
                            reason="wamp.error.authorization_request_failed",
                            message=str(err.value)))

                d.addCallbacks(done, error)

                return dres

            else:

                log.msg("don't know how to authenticate")

                return types.Deny()

        else:

            ## deny client
            return types.Deny(message=u"no pending authentication")
Beispiel #19
0
    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))
Beispiel #20
0
 def onHello(self, realm, details):
     return types.Accept()
Beispiel #21
0
 def onAuthenticate(self, signature, extra):
     return types.Accept()
Beispiel #22
0
 def on_authenticate_ok(role):
    return types.Accept(authid = self._pending_auth.authid,
       authrole = role,
       authmethod = self._pending_auth.authmethod,
       authprovider = self._pending_auth.authprovider)
Beispiel #23
0
    def onHello(self, realm, details):

        if self._transport._authid is not None:
            ## already authenticated .. e.g. via cookie
            ##
            return types.Accept(authid=self._transport._authid,
                                authrole=self._transport._authrole,
                                authmethod=self._transport._authmethod)
        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"]:

                        ## Mozilla Persona
                        ##
                        if authmethod == "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 = None
                            try:
                                if 'role' in cfg:
                                    if cfg['role']['type'] == 'static':
                                        authrole = cfg['role']['value']
                            except Exception as e:
                                log.msg(
                                    "error processing 'role' part of 'auth' config: {}"
                                    .format(e))

                            self._pending_auth = PendingAuthPersona(
                                provider, audience, authrole)
                            return types.Challenge("mozilla-persona")

                        ## Anonymous
                        ##
                        elif authmethod == "anonymous":
                            cfg = self._transport_config['auth']['anonymous']

                            ## authrole mapping
                            ##
                            authrole = "anonymous"
                            try:
                                if 'role' in cfg:
                                    if cfg['role']['type'] == 'static':
                                        authrole = cfg['role']['value']
                            except Exception as e:
                                log.msg(
                                    "error processing 'role' part of 'auth' config: {}"
                                    .format(e))

                            ## authid generation
                            ##
                            if self._transport._cbtid:
                                ## set authid to cookie value
                                authid = self._transport._cbtid
                            else:
                                authid = util.newid(24)

                            self._transport._authid = authid
                            self._transport._authrole = authrole
                            self._transport._authmethod = "anonymous"

                            return types.Accept(
                                authid=authid,
                                authrole=authrole,
                                authmethod=self._transport._authmethod)

                        elif authmethod == "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))

                ## if authentication is configured, by default, deny.
                ##
                return types.Deny()
            else:
                ## FIXME: if not "auth" key present, allow anyone
                return types.Accept(authid="anonymous",
                                    authrole="anonymous",
                                    authmethod="anonymous")
Beispiel #24
0
    def verify(self, signature):
        """
        The WAMP client has answered with a WAMP AUTHENTICATE message. Verify the message and
        return `types.Accept` or `types.Deny`.
        """
        # WAMP-Ticket "static"
        #
        if self.authprovider == 'static':

            # when doing WAMP-Ticket from static configuration, the ticket we
            # expect was store on the pending authentication object and we just compare ..
            if signature == self.ticket:
                # ticket was valid: accept the client
                return types.Accept(realm=self.realm,
                                    authid=self.authid,
                                    authrole=self.authrole,
                                    authmethod=self.authmethod,
                                    authprovider=self.authprovider)
            else:
                # ticket was invalid: deny client
                return types.Deny(message=u"ticket in static WAMP-Ticket authentication is invalid")

        # WAMP-Ticket "dynamic"
        #
        else:
            details = {
                'transport': self.session._transport._transport_info,
                'session': self.session._pending_session_id,
                'ticket': signature
            }
            d = self.authenticator_session.call(self.authenticator, self.realm, self.authid, details)

            def on_authenticate_ok(principal):
                if isinstance(principal, dict):
                    # dynamic ticket authenticator returned a dictionary (new)
                    realm = principal.get("realm", self.realm)
                    authid = principal.get("authid", self.authid)
                    authrole = principal["role"]
                else:
                    # backwards compatibility: dynamic ticket authenticator
                    # was expected to return a role directly
                    realm = self.realm
                    authid = self.authid
                    authrole = principal

                return types.Accept(realm=realm,
                                    authid=authid,
                                    authrole=authrole,
                                    authmethod=self.authmethod,
                                    authprovider=self.authprovider)

            def on_authenticate_error(err):
                error = None
                message = "WAMP-Ticket dynamic authenticator 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