def test_02_app_local_store(self):
        store1 = get_app_local_store()
        store1["hello"] = "world"
        g.test_flag = True

        # We get the same store even if we push another app context for the same app
        with self.app.app_context():
            store2 = get_app_local_store()
            self.assertEqual(store1, store2)
            self.assertEqual(store2["hello"], "world")
            self.assertNotIn("test_flag", g)
            g.test_flag = False

        self.assertEqual(g.test_flag, True)
        g.pop("test_flag")

        # We get a different store if we push a context for another app
        new_app = create_app("testing", "")
        with new_app.app_context():
            store3 = get_app_local_store()
            self.assertNotIn("hello", store3)
            self.assertNotEqual(store3, store1)
            store3["hello"] = "no!"
            store4 = get_app_local_store()
            store3["something"] = "else"
            self.assertEqual(store4["hello"], "no!")
            self.assertEqual(store4["something"], "else")

        self.assertEqual(store1, store2)
        self.assertEqual(store2["hello"], "world")
    def test_02_app_local_store(self):
        store1 = get_app_local_store()
        store1["hello"] = "world"
        g.test_flag = True

        # We get the same store even if we push another app context for the same app
        with self.app.app_context():
            store2 = get_app_local_store()
            self.assertEqual(store1, store2)
            self.assertEqual(store2["hello"], "world")
            self.assertNotIn("test_flag", g)
            g.test_flag = False

        self.assertEquals(g.test_flag, True)
        g.pop("test_flag")

        # We get a different store if we push a context for another app
        new_app = create_app("testing", "")
        with new_app.app_context():
            store3 = get_app_local_store()
            self.assertNotIn("hello", store3)
            self.assertNotEqual(store3, store1)
            store3["hello"] = "no!"
            store4 = get_app_local_store()
            store3["something"] = "else"
            self.assertEqual(store4["hello"], "no!")
            self.assertEqual(store4["something"], "else")

        self.assertEqual(store1, store2)
        self.assertEqual(store2["hello"], "world")
Beispiel #3
0
 def register_app(self, app):
     """
     Create an instance of a ``BaseQueue`` subclass according to the app config's
     ``PI_JOB_QUEUE_CLASS`` option and store it in the ``job_queue`` config.
     Register all collected jobs with this application.
     This instance is shared between threads!
     This function should only be called once per process.
     :param app: privacyIDEA app
     """
     with app.app_context():
         store = get_app_local_store()
     if "job_queue" in store:
         raise RuntimeError("App already has a job queue: {!r}".format(
             store["job_queue"]))
     try:
         package_name, class_name = app.config[JOB_QUEUE_CLASS].rsplit(
             ".", 1)
         queue_class = get_module_class(package_name, class_name)
     except (ImportError, ValueError) as exx:
         log.warning(u"Could not import job queue class {!r}: {!r}".format(
             app.config[JOB_QUEUE_CLASS], exx))
         return
     # Extract configuration from app config: All options starting with PI_JOB_QUEUE_
     options = {}
     for k, v in app.config.items():
         if k.startswith(JOB_QUEUE_OPTION_PREFIX) and k != JOB_QUEUE_CLASS:
             options[k[len(JOB_QUEUE_OPTION_PREFIX):].lower()] = v
     job_queue = queue_class(options)
     log.info(u"Created a new job queue: {!r}".format(job_queue))
     store["job_queue"] = job_queue
     for name, (func, args, kwargs) in self._jobs.items():
         job_queue.register_job(name, func, *args, **kwargs)
Beispiel #4
0
def get_firebase_access_token(config_file_name):
    """
    This returns the access token for a given JSON config file name

    :param config_file_name: The json file with the Service account credentials
    :type config_file_name: str
    :return: Firebase credentials
    :rtype: google.oauth2.service_account.Credentials
    """
    fbt = "firebase_token"
    app_store = get_app_local_store()

    if fbt not in app_store or not isinstance(app_store[fbt], dict):
        # initialize the firebase_token in the app_store as dict
        app_store[fbt] = {}

    if not isinstance(app_store[fbt].get(config_file_name), service_account.Credentials) or \
            app_store[fbt].get(config_file_name).expired:
        # If the type of the config is not of class Credentials or if the token
        # has expired we get new scoped access token credentials
        credentials = service_account.Credentials.from_service_account_file(config_file_name,
                                                                            scopes=SCOPES)

        log.debug("Fetching a new access_token for {!r} from firebase...".format(config_file_name))
        # We do not use a lock here: The worst that could happen is that two threads
        # fetch new auth tokens concurrently. In this case, one of them wins and
        # is written to the dictionary.
        app_store[fbt][config_file_name] = credentials
        readable_time = credentials.expiry.isoformat() if credentials.expiry else 'Never'
        log.debug(u"Setting the expiration for {!r} of the new access_token "
                  u"to {!s}.".format(config_file_name, readable_time))

    return app_store[fbt][config_file_name]
Beispiel #5
0
def get_registry():
    """
    Return the ``EngineRegistry`` object associated with the current application.
    If there is no such object yet, create one and write it to the app-local store.
    This respects the ``PI_ENGINE_REGISTRY_CLASS`` config option.
    :return: an ``EngineRegistry`` object
    """
    # This function will be called concurrently by multiple threads.
    # This is no problem when we already have an engine registry object.
    # However, if there is no registry object yet, two threads may concurrently
    # decide to create a new one. But as ``setdefault`` is atomic, only the
    # first one will be the written to ``app_store['config']``. The latter
    # one will not be referenced and will be garbage-collected at some point.
    app_store = get_app_local_store()
    try:
        return app_store["engine_registry"]
    except KeyError:
        # create a new engine registry of the appropriate class
        registry_class_name = get_app_config_value(
            "PI_ENGINE_REGISTRY_CLASS", DEFAULT_REGISTRY_CLASS_NAME)
        if registry_class_name not in ENGINE_REGISTRY_CLASSES:
            log.warning(u"Unknown engine registry class: {!r}".format(
                registry_class_name))
            registry_class_name = DEFAULT_REGISTRY_CLASS_NAME
        registry = ENGINE_REGISTRY_CLASSES[registry_class_name]()
        log.info(u"Created a new engine registry: {!r}".format(registry))
        return app_store.setdefault("engine_registry", registry)
Beispiel #6
0
def get_registry():
    """
    Return the ``EngineRegistry`` object associated with the current application.
    If there is no such object yet, create one and write it to the app-local store.
    This respects the ``PI_ENGINE_REGISTRY_CLASS`` config option.
    :return: an ``EngineRegistry`` object
    """
    # This function will be called concurrently by multiple threads.
    # This is no problem when we already have an engine registry object.
    # However, if there is no registry object yet, two threads may concurrently
    # decide to create a new one. But as ``setdefault`` is atomic, only the
    # first one will be the written to ``app_store['config']``. The latter
    # one will not be referenced and will be garbage-collected at some point.
    app_store = get_app_local_store()
    try:
        return app_store["engine_registry"]
    except KeyError:
        # create a new engine registry of the appropriate class
        registry_class_name = get_app_config_value("PI_ENGINE_REGISTRY_CLASS", DEFAULT_REGISTRY_CLASS_NAME)
        if registry_class_name not in ENGINE_REGISTRY_CLASSES:
            log.warning(u"Unknown engine registry class: {!r}".format(registry_class_name))
            registry_class_name = DEFAULT_REGISTRY_CLASS_NAME
        registry = ENGINE_REGISTRY_CLASSES[registry_class_name]()
        log.info(u"Created a new engine registry: {!r}".format(registry))
        return app_store.setdefault("engine_registry", registry)
Beispiel #7
0
    def register_app(self, app):
        """
        Create an instance of a ``BaseQueue`` subclass according to the app config's
        ``PI_JOB_QUEUE_CLASS`` option and store it in the ``job_queue`` config.
        Register all collected jobs with this application.
        This instance is shared between threads!
        This function should only be called once per process.

        :param app: privacyIDEA app
        """
        with app.app_context():
            store = get_app_local_store()
        if "job_queue" in store:
            raise RuntimeError("App already has a job queue: {!r}".format(store["job_queue"]))
        try:
            package_name, class_name = app.config[JOB_QUEUE_CLASS].rsplit(".", 1)
            queue_class = get_module_class(package_name, class_name)
        except (ImportError, ValueError) as exx:
            log.warning(u"Could not import job queue class {!r}: {!r}".format(app.config[JOB_QUEUE_CLASS], exx))
            return
        # Extract configuration from app config: All options starting with PI_JOB_QUEUE_
        options = {}
        for k, v in app.config.items():
            if k.startswith(JOB_QUEUE_OPTION_PREFIX) and k != JOB_QUEUE_CLASS:
                options[k[len(JOB_QUEUE_OPTION_PREFIX):].lower()] = v
        job_queue = queue_class(options)
        log.info(u"Created a new job queue: {!r}".format(job_queue))
        store["job_queue"] = job_queue
        for name, (func, args, kwargs) in self._jobs.items():
            job_queue.register_job(name, func, *args, **kwargs)
Beispiel #8
0
def get_job_queue():
    """
    Get the job queue registered with the current app. If no job queue is configured,
    raise a ServerError.
    """
    store = get_app_local_store()
    if "job_queue" in store:
        return store["job_queue"]
    else:
        raise ServerError("privacyIDEA has no job queue configured!")
Beispiel #9
0
def get_job_queue():
    """
    Get the job queue registered with the current app. If no job queue is configured,
    raise a ServerError.
    """
    store = get_app_local_store()
    if "job_queue" in store:
        return store["job_queue"]
    else:
        raise ServerError("privacyIDEA has no job queue configured!")
Beispiel #10
0
def get_shared_config_object():
    """
    :return: the application-wide ``SharedConfigClass`` object, which is created on demand.
    """
    store = get_app_local_store()
    if 'shared_config_object' not in store:
        # It might happen that two threads create SharedConfigClass() instances in parallel.
        # However, as setting dictionary values is atomic, one of the two objects will "win",
        # and the next request handled by the second thread will use the winning config object.
        log.debug(u"Creating new shared config object")
        store['shared_config_object'] = SharedConfigClass()
    return store['shared_config_object']
    def test_03a_api_authenticate_fail(self):
        # This tests the failed to communicate to the firebase service
        self.setUp_user_realms()

        # get enrolled push token
        toks = get_tokens(tokentype="push")
        self.assertEqual(len(toks), 1)
        tokenobj = toks[0]

        # set PIN
        tokenobj.set_pin("pushpin")
        tokenobj.add_user(User("cornelius", self.realm1))

        # We mock the ServiceAccountCredentials, since we can not directly contact the Google API
        with mock.patch(
                'privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials'
        ) as mySA:
            # alternative: side_effect instead of return_value
            mySA.from_json_keyfile_name.return_value = myCredentials(
                myAccessTokenInfo("my_bearer_token"))

            # add responses, to simulate the failing communication (status 500)
            responses.add(
                responses.POST,
                'https://fcm.googleapis.com/v1/projects/test-123456/messages:send',
                body="""{}""",
                status=500,
                content_type="application/json")

            # Send the first authentication request to trigger the challenge
            with self.app.test_request_context('/validate/check',
                                               method='POST',
                                               data={
                                                   "user": "******",
                                                   "realm": self.realm1,
                                                   "pass": "******"
                                               }):
                res = self.app.full_dispatch_request()
                self.assertTrue(res.status_code == 400, res)
                jsonresp = res.json
                self.assertFalse(jsonresp.get("result").get("status"))
                self.assertEqual(
                    jsonresp.get("result").get("error").get("code"), 401)
                self.assertEqual(
                    jsonresp.get("result").get("error").get("message"),
                    "ERR401: Failed to submit "
                    "message to firebase service.")

            # Our ServiceAccountCredentials mock has been called once, because no access token has been fetched before
            self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 1)
            self.assertIn(FIREBASE_FILE,
                          get_app_local_store()["firebase_token"])
Beispiel #12
0
def init_hsm():
    """
    Initialize the HSM in the app-local store

    The config file pi.cfg may contain PI_HSM_MODULE and parameters like:
    PI_HSM_MODULE_MODULE
    PI_HSM_MODULE_SLOT_ID...

    :return: hsm object
    """
    app_store = get_app_local_store()
    if "pi_hsm" not in app_store or not isinstance(app_store["pi_hsm"], dict):
        config = get_app_config()
        HSM_config = {"obj": create_hsm_object(config)}
        app_store["pi_hsm"] = HSM_config
        log.info("Initialized HSM object {0}".format(HSM_config))
    return app_store["pi_hsm"]["obj"]
Beispiel #13
0
def init_hsm():
    """
    Initialize the HSM in the app-local store

    The config file pi.cfg may contain PI_HSM_MODULE and parameters like:
    PI_HSM_MODULE_MODULE
    PI_HSM_MODULE_SLOT_ID...

    :return: hsm object
    """
    app_store = get_app_local_store()
    if "pi_hsm" not in app_store or not isinstance(app_store["pi_hsm"], dict):
        config = get_app_config()
        HSM_config = {"obj": create_hsm_object(config)}
        app_store["pi_hsm"] = HSM_config
        log.info("Initialized HSM object {0}".format(HSM_config))
    return app_store["pi_hsm"]["obj"]
Beispiel #14
0
def get_firebase_access_token(config_file_name):
    """
    This returns the access token for a given JSON config file name

    :param config_file_name:
    :return:
    """
    fbt = "firebase_token"
    now = time.time()
    app_store = get_app_local_store()

    if fbt not in app_store or not isinstance(app_store[fbt], dict):
        # initialize the firebase_token in the app_store as dict
        app_store[fbt] = {}

    if not isinstance(app_store[fbt].get(config_file_name), AccessToken) or \
            now > app_store[fbt].get(config_file_name).expires_at:
        # If the type of the config is not class AccessToken or
        # if the token has expired
        credentials = ServiceAccountCredentials.from_json_keyfile_name(
            config_file_name, SCOPES)
        log.debug(
            "Fetching a new access_token for {!r} from firebase...".format(
                config_file_name))
        access_token_info = credentials.get_access_token()
        # Now we set the expiration date for the new access_token with a margin of 10 seconds
        At = AccessToken(access_token_info.access_token,
                         access_token_info.expires_in)
        # We do not use a lock here: The worst that could happen is that two threads
        # fetch new auth tokens concurrently. In this case, one of them wins and is written to the dictionary.
        app_store[fbt][config_file_name] = At
        readable_time = datetime.datetime.fromtimestamp(
            At.expires_at).isoformat()
        log.debug(
            u"Setting the expiration for {!r} of the new access_token to {!s}."
            .format(config_file_name, readable_time))

    return app_store[fbt][config_file_name].access_token
Beispiel #15
0
def has_job_queue():
    """
    Return a boolean describing whether the current app has an app queue configured.
    """
    return "job_queue" in get_app_local_store()
    def test_04_api_authenticate_smartphone(self):
        # Test the /validate/check endpoints and the smartphone endpoint /ttype/push
        # for authentication

        # get enrolled push token
        toks = get_tokens(tokentype="push")
        self.assertEqual(len(toks), 1)
        tokenobj = toks[0]

        # set PIN
        tokenobj.set_pin("pushpin")
        tokenobj.add_user(User("cornelius", self.realm1))

        def check_firebase_params(request):
            payload = json.loads(request.body)
            # check the signature in the payload!
            data = payload.get("message").get("data")

            sign_string = u"{nonce}|{url}|{serial}|{question}|{title}|{sslverify}".format(
                **data)
            token_obj = get_tokens(serial=data.get("serial"))[0]
            pem_pubkey = token_obj.get_tokeninfo(PUBLIC_KEY_SERVER)
            pubkey_obj = load_pem_public_key(to_bytes(pem_pubkey),
                                             backend=default_backend())
            signature = b32decode(data.get("signature"))
            # If signature does not match it will raise InvalidSignature exception
            pubkey_obj.verify(signature, sign_string.encode("utf8"),
                              padding.PKCS1v15(), hashes.SHA256())
            headers = {'request-id': '728d329e-0e86-11e4-a748-0c84dc037c13'}
            return (200, headers, json.dumps({}))

        # We mock the ServiceAccountCredentials, since we can not directly contact the Google API
        with mock.patch(
                'privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials'
        ) as mySA:
            # alternative: side_effect instead of return_value
            mySA.from_json_keyfile_name.return_value = myCredentials(
                myAccessTokenInfo("my_bearer_token"))

            # add responses, to simulate the communication to firebase
            responses.add_callback(
                responses.POST,
                'https://fcm.googleapis.com/v1/projects/test-123456/messages:send',
                callback=check_firebase_params,
                content_type="application/json")

            # Send the first authentication request to trigger the challenge
            with self.app.test_request_context('/validate/check',
                                               method='POST',
                                               data={
                                                   "user": "******",
                                                   "realm": self.realm1,
                                                   "pass": "******"
                                               }):
                res = self.app.full_dispatch_request()
                self.assertTrue(res.status_code == 200, res)
                jsonresp = res.json
                self.assertFalse(jsonresp.get("result").get("value"))
                self.assertTrue(jsonresp.get("result").get("status"))
                self.assertEqual(
                    jsonresp.get("detail").get("serial"),
                    tokenobj.token.serial)
                self.assertTrue("transaction_id" in jsonresp.get("detail"))
                transaction_id = jsonresp.get("detail").get("transaction_id")
                self.assertEqual(
                    jsonresp.get("detail").get("message"),
                    DEFAULT_CHALLENGE_TEXT)

            # Our ServiceAccountCredentials mock has not been called because we use a cached token
            self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 0)
            self.assertIn(FIREBASE_FILE,
                          get_app_local_store()["firebase_token"])

        # The challenge is sent to the smartphone via the Firebase service, so we do not know
        # the challenge from the /validate/check API.
        # So lets read the challenge from the database!

        challengeobject_list = get_challenges(serial=tokenobj.token.serial,
                                              transaction_id=transaction_id)
        challenge = challengeobject_list[0].challenge

        # Incomplete request fails with HTTP400
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": challenge
                                           }):
            res = self.app.full_dispatch_request()
            self.assertEquals(res.status_code, 400)

        # This is what the smartphone answers.
        # create the signature:
        sign_data = "{0!s}|{1!s}".format(challenge, tokenobj.token.serial)
        signature = b32encode_and_unicode(
            self.smartphone_private_key.sign(sign_data.encode("utf-8"),
                                             padding.PKCS1v15(),
                                             hashes.SHA256()))
        # Try an invalid signature first
        wrong_sign_data = "{}|{}".format(challenge, tokenobj.token.serial[1:])
        wrong_signature = b32encode_and_unicode(
            self.smartphone_private_key.sign(wrong_sign_data.encode("utf-8"),
                                             padding.PKCS1v15(),
                                             hashes.SHA256()))
        # Signed the wrong data
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": challenge,
                                               "signature": wrong_signature
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertTrue(res.json['result']['status'])
            self.assertFalse(res.json['result']['value'])

        # Correct signature, wrong challenge
        wrong_challenge = b32encode_and_unicode(geturandom())
        wrong_sign_data = "{}|{}".format(wrong_challenge,
                                         tokenobj.token.serial)
        wrong_signature = b32encode_and_unicode(
            self.smartphone_private_key.sign(wrong_sign_data.encode("utf-8"),
                                             padding.PKCS1v15(),
                                             hashes.SHA256()))
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": wrong_challenge,
                                               "signature": wrong_signature
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertTrue(res.json['result']['status'])
            self.assertFalse(res.json['result']['value'])

        # Correct signature, empty nonce
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": "",
                                               "signature": signature
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertTrue(res.json['result']['status'])
            self.assertFalse(res.json['result']['value'])

        # Correct signature, wrong private key
        wrong_key = rsa.generate_private_key(public_exponent=65537,
                                             key_size=4096,
                                             backend=default_backend())
        wrong_sign_data = "{}|{}".format(challenge, tokenobj.token.serial)
        wrong_signature = b32encode_and_unicode(
            wrong_key.sign(wrong_sign_data.encode("utf-8"), padding.PKCS1v15(),
                           hashes.SHA256()))
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": challenge,
                                               "signature": wrong_signature
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertTrue(res.json['result']['status'])
            self.assertFalse(res.json['result']['value'])

        # Result value is still false
        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={
                                               "user": "******",
                                               "realm": self.realm1,
                                               "pass": "",
                                               "state": transaction_id
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertFalse(res.json['result']['value'])

        # Now the correct request
        with self.app.test_request_context('/ttype/push',
                                           method='POST',
                                           data={
                                               "serial": tokenobj.token.serial,
                                               "nonce": challenge,
                                               "signature": signature
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            self.assertTrue(res.json['result']['status'])
            self.assertTrue(res.json['result']['value'])

        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={
                                               "user": "******",
                                               "realm": self.realm1,
                                               "pass": "",
                                               "state": transaction_id
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            jsonresp = res.json
            # Result-Value is True
            self.assertTrue(jsonresp.get("result").get("value"))
    def test_03b_api_authenticate_client(self):
        # Test the /validate/check endpoints without the smartphone endpoint /ttype/push
        self.setUp_user_realms()

        # get enrolled push token
        toks = get_tokens(tokentype="push")
        self.assertEqual(len(toks), 1)
        tokenobj = toks[0]

        # set PIN
        tokenobj.set_pin("pushpin")
        tokenobj.add_user(User("cornelius", self.realm1))

        # We mock the ServiceAccountCredentials, since we can not directly contact the Google API
        with mock.patch(
                'privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials'
        ) as mySA:
            # alternative: side_effect instead of return_value
            mySA.from_json_keyfile_name.return_value = myCredentials(
                myAccessTokenInfo("my_bearer_token"))

            # add responses, to simulate the communication to firebase
            responses.add(
                responses.POST,
                'https://fcm.googleapis.com/v1/projects/test-123456/messages:send',
                body="""{}""",
                content_type="application/json")

            # Send the first authentication request to trigger the challenge
            with self.app.test_request_context('/validate/check',
                                               method='POST',
                                               data={
                                                   "user": "******",
                                                   "realm": self.realm1,
                                                   "pass": "******"
                                               }):
                res = self.app.full_dispatch_request()
                self.assertTrue(res.status_code == 200, res)
                jsonresp = res.json
                self.assertFalse(jsonresp.get("result").get("value"))
                self.assertTrue(jsonresp.get("result").get("status"))
                self.assertEqual(
                    jsonresp.get("detail").get("serial"),
                    tokenobj.token.serial)
                self.assertTrue("transaction_id" in jsonresp.get("detail"))
                transaction_id = jsonresp.get("detail").get("transaction_id")
                self.assertEqual(
                    jsonresp.get("detail").get("message"),
                    DEFAULT_CHALLENGE_TEXT)

            # Our ServiceAccountCredentials mock has not been called because we use a cached token
            self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 0)
            self.assertIn(FIREBASE_FILE,
                          get_app_local_store()["firebase_token"])

        # The mobile device has not communicated with the backend, yet.
        # The user is not authenticated!
        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={
                                               "user": "******",
                                               "realm": self.realm1,
                                               "pass": "",
                                               "transaction_id": transaction_id
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            jsonresp = res.json
            # Result-Value is false, the user has not answered the challenge, yet
            self.assertFalse(jsonresp.get("result").get("value"))

        # As the challenge has not been answered yet, the /validate/polltransaction endpoint returns false
        with self.app.test_request_context(
                '/validate/polltransaction',
                method='GET',
                data={'transaction_id': transaction_id}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            self.assertTrue(res.json["result"]["status"])
            self.assertFalse(res.json["result"]["value"])

        # Now the smartphone communicates with the backend and the challenge in the database table
        # is marked as answered successfully.
        challengeobject_list = get_challenges(serial=tokenobj.token.serial,
                                              transaction_id=transaction_id)
        challengeobject_list[0].set_otp_status(True)

        # As the challenge has been answered, the /validate/polltransaction endpoint returns true
        with self.app.test_request_context(
                '/validate/polltransaction',
                method='GET',
                data={'transaction_id': transaction_id}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            self.assertTrue(res.json["result"]["status"])
            self.assertTrue(res.json["result"]["value"])

        with self.app.test_request_context('/validate/check',
                                           method='POST',
                                           data={
                                               "user": "******",
                                               "realm": self.realm1,
                                               "pass": "",
                                               "state": transaction_id
                                           }):
            res = self.app.full_dispatch_request()
            self.assertTrue(res.status_code == 200, res)
            jsonresp = res.json
            # Result-Value is True, since the challenge is marked resolved in the DB
        self.assertTrue(jsonresp.get("result").get("value"))

        # As the challenge does not exist anymore, the /validate/polltransaction endpoint returns false
        with self.app.test_request_context(
                '/validate/polltransaction',
                method='GET',
                data={'transaction_id': transaction_id}):
            res = self.app.full_dispatch_request()
            self.assertEqual(res.status_code, 200)
            self.assertTrue(res.json["result"]["status"])
            self.assertFalse(res.json["result"]["value"])
        self.assertEqual(get_challenges(serial=tokenobj.token.serial), [])

        # We mock the ServiceAccountCredentials, since we can not directly contact the Google API
        # Do single shot auth with waiting
        # Also mock time.time to be 4000 seconds in the future (exceeding the validity of myAccessTokenInfo),
        # so that we fetch a new auth token
        with mock.patch('privacyidea.lib.smsprovider.FirebaseProvider.time'
                        ) as mock_time:
            mock_time.time.return_value = time.time() + 4000

            with mock.patch(
                    'privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials'
            ) as mySA:
                # alternative: side_effect instead of return_value
                mySA.from_json_keyfile_name.return_value = myCredentials(
                    myAccessTokenInfo("my_new_bearer_token"))

                # add responses, to simulate the communication to firebase
                responses.add(
                    responses.POST,
                    'https://fcm.googleapis.com/v1/projects/test-123456/messages:send',
                    body="""{}""",
                    content_type="application/json")

                # In two seconds we need to run an update on the challenge table.
                Timer(2, self.mark_challenge_as_accepted).start()

                set_policy("push1",
                           scope=SCOPE.AUTH,
                           action="{0!s}=20".format(PUSH_ACTION.WAIT))
                # Send the first authentication request to trigger the challenge
                with self.app.test_request_context('/validate/check',
                                                   method='POST',
                                                   data={
                                                       "user": "******",
                                                       "realm": self.realm1,
                                                       "pass": "******"
                                                   }):
                    res = self.app.full_dispatch_request()
                    self.assertTrue(res.status_code == 200, res)
                    jsonresp = res.json
                    # We successfully authenticated! YEAH!
                    self.assertTrue(jsonresp.get("result").get("value"))
                    self.assertTrue(jsonresp.get("result").get("status"))
                    self.assertEqual(
                        jsonresp.get("detail").get("serial"),
                        tokenobj.token.serial)
                delete_policy("push1")

            # Our ServiceAccountCredentials mock has been called once because we fetched a new token
            self.assertEqual(len(mySA.from_json_keyfile_name.mock_calls), 1)
            self.assertIn(FIREBASE_FILE,
                          get_app_local_store()["firebase_token"])
            self.assertEqual(
                get_app_local_store()["firebase_token"]
                [FIREBASE_FILE].access_token, "my_new_bearer_token")

        # Authentication fails, if the push notification is not accepted within the configured time
        with mock.patch(
                'privacyidea.lib.smsprovider.FirebaseProvider.ServiceAccountCredentials'
        ) as mySA:
            # alternative: side_effect instead of return_value
            mySA.from_json_keyfile_name.return_value = myCredentials(
                myAccessTokenInfo("my_bearer_token"))

            # add responses, to simulate the communication to firebase
            responses.add(
                responses.POST,
                'https://fcm.googleapis.com/v1/projects/test-123456/messages:send',
                body="""{}""",
                content_type="application/json")

            set_policy("push1",
                       scope=SCOPE.AUTH,
                       action="{0!s}=1".format(PUSH_ACTION.WAIT))
            # Send the first authentication request to trigger the challenge
            with self.app.test_request_context('/validate/check',
                                               method='POST',
                                               data={
                                                   "user": "******",
                                                   "realm": self.realm1,
                                                   "pass": "******"
                                               }):
                res = self.app.full_dispatch_request()
                self.assertTrue(res.status_code == 200, res)
                jsonresp = res.json
                # We fail to authenticate! Oh No!
                self.assertFalse(jsonresp.get("result").get("value"))
                self.assertTrue(jsonresp.get("result").get("status"))
                self.assertEqual(
                    jsonresp.get("detail").get("serial"),
                    tokenobj.token.serial)
            delete_policy("push1")
Beispiel #18
0
def has_job_queue():
    """
    Return a boolean describing whether the current app has an app queue configured.
    """
    return "job_queue" in get_app_local_store()