def init(self):
        '''This method is invoked automatically in order to setup common dependencies for all test cases.'''

        self._oauth2_idp = {
            "client_id": "11111111-1111-1111-1111-111111111111",
            "template": "/components/frontend/views/custom_login.html",
            "expires_in": 1209600,
            "idp_index": "/oauth/idp/ui/login"
        }

        self._client_repo = Mock()
        client_repo_cls = Mock(return_value=self._client_repo)

        self._tokens_service = Mock()
        self._tokens_service.db_conn = Mock()
        self._tokens_service.validate = Mock(return_value=None)

        self._settings_facade = Mock()

        self._settings_facade.get = self._mock_get_setting

        self._handler = ImplicitGrantHandler(self._tokens_service,
                                             self._settings_facade,
                                             client_repo_cls=client_repo_cls)

        client_repo_cls.assert_called_once_with(self._tokens_service.db_conn)
    def init(self):
        '''This method is invoked automatically in order to setup common dependencies for all test cases.'''

        self._oauth2_idp = {"client_id": "11111111-1111-1111-1111-111111111111",
                            "template": "/components/frontend/views/custom_login.html",
                            "expires_in": 1209600,
                            "idp_index": "/oauth/idp/ui/login"}

        self._client_repo = Mock()
        client_repo_cls = Mock(return_value=self._client_repo)

        self._tokens_service = Mock()
        self._tokens_service.db_conn = Mock()
        self._tokens_service.validate = Mock(return_value=None)

        self._settings_facade = Mock()

        self._settings_facade.get = self._mock_get_setting

        self._handler = ImplicitGrantHandler(self._tokens_service, self._settings_facade, client_repo_cls=client_repo_cls)

        client_repo_cls.assert_called_once_with(self._tokens_service.db_conn)
class ImplicitGrantHandlerTests(FantasticoUnitTestsCase):
    '''This class provides the tests suite for implicit grant handler.'''

    _EXPIRES_IN = 3600

    _settings_facade = None
    _tokens_service = None
    _handler = None
    _oauth2_idp = None
    _client_repo = None

    def init(self):
        '''This method is invoked automatically in order to setup common dependencies for all test cases.'''

        self._oauth2_idp = {
            "client_id": "11111111-1111-1111-1111-111111111111",
            "template": "/components/frontend/views/custom_login.html",
            "expires_in": 1209600,
            "idp_index": "/oauth/idp/ui/login"
        }

        self._client_repo = Mock()
        client_repo_cls = Mock(return_value=self._client_repo)

        self._tokens_service = Mock()
        self._tokens_service.db_conn = Mock()
        self._tokens_service.validate = Mock(return_value=None)

        self._settings_facade = Mock()

        self._settings_facade.get = self._mock_get_setting

        self._handler = ImplicitGrantHandler(self._tokens_service,
                                             self._settings_facade,
                                             client_repo_cls=client_repo_cls)

        client_repo_cls.assert_called_once_with(self._tokens_service.db_conn)

    def _mock_get_setting(self, setting_name):
        '''This method mocks settings_facade get for expected settings used in implicit grant handler.'''

        if setting_name == "access_token_validity":
            return self._EXPIRES_IN

        if setting_name == "oauth2_idp":
            return self._oauth2_idp

        raise ValueError("Setting %s must not be accessed." % setting_name)

    def test_handle_ok(self):
        '''This test case ensures implicit grant works as expected when all prerequisites are fulfilled.'''

        client_id = "sample app"
        redirect_uri = "/example/cb"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"

        self._test_handle_template(client_id, redirect_uri, scope, state,
                                   encrypted_login)

    def test_handle_noredirect_ok(self):
        '''This test case ensures implicit grant without redirect works as expected when all prerequisites 
        are fulfilled.'''

        client_id = "sample app"
        redirect_uri = "/example/cb"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"

        redirect = 0

        self._test_handle_template(client_id,
                                   redirect_uri,
                                   scope,
                                   state,
                                   encrypted_login,
                                   redirect_qparam=redirect)

    def test_handle_ok_redirectwithhash(self):
        '''This test case ensures implicit grant works as expected even when redirect uri has a fragment in it.'''

        client_id = "sample app"
        redirect_uri = "/example/cb#a=b"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"

        encrypted_access = "abcd"
        expected_redirect = "%s&access_token=%s&state=%s&token_type=access&expires_in=%s&scope=%s" % \
                                    (redirect_uri, encrypted_access,
                                     urllib.parse.quote(state), self._EXPIRES_IN,
                                     urllib.parse.quote(scope))

        self._test_handle_template(client_id,
                                   redirect_uri,
                                   scope,
                                   state,
                                   encrypted_login,
                                   expected_redirect=expected_redirect,
                                   encrypted_access=encrypted_access)

    def test_handle_missing_client(self):
        '''This test case ensures a missing query parameter exception is raised when client_id is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id=None)

        self.assertEqual("client_id", ctx.exception.param_name)

    def test_handle_missing_redirecturi(self):
        '''This test case ensures a missing query parameter exception is raised when redirect_uri is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample",
                                           redirect_uri=None)

            self.assertEqual("redirect_uri", ctx.exception.param_name)

    def test_handle_missing_state(self):
        '''This test case ensures a missing query parameter exception is raised when state is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample",
                                           redirect_uri="/example/cb",
                                           state=None)

        self.assertEqual("state", ctx.exception.param_name)

    def test_handle_missing_scope(self):
        '''This test case ensures a missing query parameter exception is raised when scope is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample",
                                           redirect_uri="/example/cb",
                                           state="xyz",
                                           scope=None)

        self.assertEqual("scope", ctx.exception.param_name)

    def test_handle_missing_login(self):
        '''This test case ensures a redirect response to idp is received if login_token query parameter is missing.'''

        request_url = "/oauth/authorize?response_type=token&state=xyz&error_format=hash&client_id=11111111-1111-1111-1111-111111111111&scope=scope1&redirect_uri=%2Fexample%2Fcb"
        expected_url = "%s?redirect_uri=%s" % (self._oauth2_idp["idp_index"],
                                               urllib.parse.quote(request_url))

        request = Request.blank(request_url)
        request.redirect = lambda url: RedirectResponse(url)

        response = self._handler.handle_grant(request)

        self.assertIsInstance(response, RedirectResponse)
        self.assertEqual(expected_url, response.headers["Location"])

    def test_handle_grant_invalidredirect(self):
        '''This test case ensures an exception is raised if the given redirect uri is not supported by the current client.'''

        client_id = "abcd"
        redirect_uri = "/abcduri"

        request = Mock()
        request.params = {
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "state": "xyz",
            "scope": "a b c"
        }

        self._client_repo.load_client_by_returnurl = Mock(return_value=None)

        response = self._handler.handle_grant(request)

        self.assertIsNotNone(response)
        self.assertEqual(401, response.status_code)
        self.assertTrue(response.body.decode().find(redirect_uri) > -1)

        self._client_repo.load_client_by_returnurl.assert_called_once_with(
            redirect_uri)

    def test_error_redirect(self):
        '''This test case ensures a redirect response is sent if the implicit handler request contains error query parameter.
        This case occurs only when authentication fails.'''

        request = Mock()
        request.params = {
            "client_id": "sample-app",
            "redirect_uri": "/sample/cb",
            "state": "xyz",
            "error": "access_denied",
            "error_description": "Simple error.",
            "error_uri": "/sample/uri/122.html"
        }

        response = self._handler.handle_grant(request)

        self.assertIsInstance(response, RedirectResponse)

        expected_redirect = "%s#error=%s&error_description=%s&error_uri=%s&state=%s" % \
                                (request.params["redirect_uri"],
                                 request.params["error"],
                                 urllib.parse.quote(request.params["error_description"]),
                                 urllib.parse.quote(request.params["error_uri"]), request.params["state"])

        self.assertEqual(expected_redirect, response.headers["Location"])

    def _test_handle_missingparam(self,
                                  client_id=None,
                                  redirect_uri=None,
                                  scope=None,
                                  state=None,
                                  encrypted_login=None):
        '''This method provides a template for testing handle_grant method from implicit provider when mandatory query
        parameters are missing.'''

        return self._test_handle_template(client_id, redirect_uri, scope,
                                          state, encrypted_login)

    def _test_handle_template(self,
                              client_id,
                              redirect_uri,
                              scope,
                              state,
                              encrypted_login,
                              expected_redirect=None,
                              encrypted_access=None,
                              redirect_qparam=None):
        '''This method provides a template for testing handle_grant method from implicit grant provider.'''

        if not encrypted_access:
            encrypted_access = "encrypted access token"

        if not expected_redirect:
            scope = scope or ""
            state = state or ""
            expected_redirect = "%s#access_token=%s&state=%s&token_type=access&expires_in=%s&scope=%s" % \
                                    (redirect_uri, encrypted_access,
                                     urllib.parse.quote(state), self._EXPIRES_IN,
                                     urllib.parse.quote(scope))

        login_token = Token({"user_id": 1})

        access_token = Token({"type": "access"})

        request = Mock()
        request.params = {
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "scope": scope,
            "state": state,
            "login_token": encrypted_login,
            "redirect": redirect_qparam
        }

        self._tokens_service.decrypt = Mock(return_value=login_token)
        self._tokens_service.generate = Mock(return_value=access_token)
        self._tokens_service.encrypt = Mock(return_value=encrypted_access)

        response = self._handler.handle_grant(request)

        if not redirect_qparam:
            self.assertIsInstance(response, RedirectResponse)
        else:
            self.assertIsInstance(response, Response)

        self.assertEqual(expected_redirect, response.headers.get("Location"))

        self._tokens_service.decrypt.assert_called_once_with(encrypted_login)
        self._tokens_service.validate.assert_called_once_with(login_token)
        self._tokens_service.generate.assert_called_once_with(
            {
                "client_id": client_id,
                "user_id": login_token.user_id,
                "scopes": scope,
                "expires_in": self._EXPIRES_IN
            }, TokenGeneratorFactory.ACCESS_TOKEN)
        self._tokens_service.encrypt.assert_called_once_with(
            access_token, client_id)
class ImplicitGrantHandlerTests(FantasticoUnitTestsCase):
    '''This class provides the tests suite for implicit grant handler.'''

    _EXPIRES_IN = 3600

    _settings_facade = None
    _tokens_service = None
    _handler = None
    _oauth2_idp = None
    _client_repo = None

    def init(self):
        '''This method is invoked automatically in order to setup common dependencies for all test cases.'''

        self._oauth2_idp = {"client_id": "11111111-1111-1111-1111-111111111111",
                            "template": "/components/frontend/views/custom_login.html",
                            "expires_in": 1209600,
                            "idp_index": "/oauth/idp/ui/login"}

        self._client_repo = Mock()
        client_repo_cls = Mock(return_value=self._client_repo)

        self._tokens_service = Mock()
        self._tokens_service.db_conn = Mock()
        self._tokens_service.validate = Mock(return_value=None)

        self._settings_facade = Mock()

        self._settings_facade.get = self._mock_get_setting

        self._handler = ImplicitGrantHandler(self._tokens_service, self._settings_facade, client_repo_cls=client_repo_cls)

        client_repo_cls.assert_called_once_with(self._tokens_service.db_conn)

    def _mock_get_setting(self, setting_name):
        '''This method mocks settings_facade get for expected settings used in implicit grant handler.'''

        if setting_name == "access_token_validity":
            return self._EXPIRES_IN

        if setting_name == "oauth2_idp":
            return self._oauth2_idp

        raise ValueError("Setting %s must not be accessed." % setting_name)

    def test_handle_ok(self):
        '''This test case ensures implicit grant works as expected when all prerequisites are fulfilled.'''

        client_id = "sample app"
        redirect_uri = "/example/cb"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"

        self._test_handle_template(client_id, redirect_uri, scope, state, encrypted_login)

    def test_handle_noredirect_ok(self):
        '''This test case ensures implicit grant without redirect works as expected when all prerequisites 
        are fulfilled.'''

        client_id = "sample app"
        redirect_uri = "/example/cb"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"
        
        redirect = 0

        self._test_handle_template(client_id, redirect_uri, scope, state, encrypted_login, 
                                   redirect_qparam=redirect)
    
    def test_handle_ok_redirectwithhash(self):
        '''This test case ensures implicit grant works as expected even when redirect uri has a fragment in it.'''

        client_id = "sample app"
        redirect_uri = "/example/cb#a=b"
        scope = "scope1 scope2"
        state = "xyz"

        encrypted_login = "******"

        encrypted_access = "abcd"
        expected_redirect = "%s&access_token=%s&state=%s&token_type=access&expires_in=%s&scope=%s" % \
                                    (redirect_uri, encrypted_access,
                                     urllib.parse.quote(state), self._EXPIRES_IN,
                                     urllib.parse.quote(scope))

        self._test_handle_template(client_id, redirect_uri, scope, state, encrypted_login,
                                   expected_redirect=expected_redirect,
                                   encrypted_access=encrypted_access)

    def test_handle_missing_client(self):
        '''This test case ensures a missing query parameter exception is raised when client_id is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id=None)

        self.assertEqual("client_id", ctx.exception.param_name)

    def test_handle_missing_redirecturi(self):
        '''This test case ensures a missing query parameter exception is raised when redirect_uri is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample", redirect_uri=None)

            self.assertEqual("redirect_uri", ctx.exception.param_name)

    def test_handle_missing_state(self):
        '''This test case ensures a missing query parameter exception is raised when state is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample", redirect_uri="/example/cb", state=None)

        self.assertEqual("state", ctx.exception.param_name)

    def test_handle_missing_scope(self):
        '''This test case ensures a missing query parameter exception is raised when scope is missing.'''

        with self.assertRaises(OAuth2MissingQueryParamError) as ctx:
            self._test_handle_missingparam(client_id="sample", redirect_uri="/example/cb", state="xyz", scope=None)

        self.assertEqual("scope", ctx.exception.param_name)

    def test_handle_missing_login(self):
        '''This test case ensures a redirect response to idp is received if login_token query parameter is missing.'''

        request_url = "/oauth/authorize?response_type=token&state=xyz&error_format=hash&client_id=11111111-1111-1111-1111-111111111111&scope=scope1&redirect_uri=%2Fexample%2Fcb"
        expected_url = "%s?redirect_uri=%s" % (self._oauth2_idp["idp_index"], urllib.parse.quote(request_url))

        request = Request.blank(request_url)
        request.redirect = lambda url: RedirectResponse(url)

        response = self._handler.handle_grant(request)

        self.assertIsInstance(response, RedirectResponse)
        self.assertEqual(expected_url, response.headers["Location"])

    def test_handle_grant_invalidredirect(self):
        '''This test case ensures an exception is raised if the given redirect uri is not supported by the current client.'''

        client_id = "abcd"
        redirect_uri = "/abcduri"

        request = Mock()
        request.params = {"client_id": client_id,
                          "redirect_uri": redirect_uri,
                          "state": "xyz",
                          "scope": "a b c"}

        self._client_repo.load_client_by_returnurl = Mock(return_value=None)

        response = self._handler.handle_grant(request)

        self.assertIsNotNone(response)
        self.assertEqual(401, response.status_code)
        self.assertTrue(response.body.decode().find(redirect_uri) > -1)

        self._client_repo.load_client_by_returnurl.assert_called_once_with(redirect_uri)

    def test_error_redirect(self):
        '''This test case ensures a redirect response is sent if the implicit handler request contains error query parameter.
        This case occurs only when authentication fails.'''

        request = Mock()
        request.params = {"client_id": "sample-app",
                          "redirect_uri": "/sample/cb",
                          "state": "xyz",
                          "error": "access_denied",
                          "error_description": "Simple error.",
                          "error_uri": "/sample/uri/122.html"}

        response = self._handler.handle_grant(request)

        self.assertIsInstance(response, RedirectResponse)

        expected_redirect = "%s#error=%s&error_description=%s&error_uri=%s&state=%s" % \
                                (request.params["redirect_uri"],
                                 request.params["error"],
                                 urllib.parse.quote(request.params["error_description"]),
                                 urllib.parse.quote(request.params["error_uri"]), request.params["state"])

        self.assertEqual(expected_redirect, response.headers["Location"])

    def _test_handle_missingparam(self, client_id=None, redirect_uri=None, scope=None, state=None, encrypted_login=None):
        '''This method provides a template for testing handle_grant method from implicit provider when mandatory query
        parameters are missing.'''

        return self._test_handle_template(client_id, redirect_uri, scope, state, encrypted_login)

    def _test_handle_template(self, client_id, redirect_uri, scope, state, encrypted_login, expected_redirect=None,
                              encrypted_access=None, redirect_qparam=None):
        '''This method provides a template for testing handle_grant method from implicit grant provider.'''

        if not encrypted_access:
            encrypted_access = "encrypted access token"

        if not expected_redirect:
            scope = scope or ""
            state = state or ""
            expected_redirect = "%s#access_token=%s&state=%s&token_type=access&expires_in=%s&scope=%s" % \
                                    (redirect_uri, encrypted_access,
                                     urllib.parse.quote(state), self._EXPIRES_IN,
                                     urllib.parse.quote(scope))

        login_token = Token({"user_id": 1})

        access_token = Token({"type": "access"})

        request = Mock()
        request.params = {"client_id": client_id,
                          "redirect_uri": redirect_uri,
                          "scope": scope,
                          "state": state,
                          "login_token": encrypted_login,
                          "redirect": redirect_qparam}

        self._tokens_service.decrypt = Mock(return_value=login_token)
        self._tokens_service.generate = Mock(return_value=access_token)
        self._tokens_service.encrypt = Mock(return_value=encrypted_access)

        response = self._handler.handle_grant(request)

        if not redirect_qparam:
            self.assertIsInstance(response, RedirectResponse)
        else:
            self.assertIsInstance(response, Response)
            
        self.assertEqual(expected_redirect, response.headers.get("Location"))

        self._tokens_service.decrypt.assert_called_once_with(encrypted_login)
        self._tokens_service.validate.assert_called_once_with(login_token)
        self._tokens_service.generate.assert_called_once_with({"client_id": client_id,
                                                               "user_id": login_token.user_id,
                                                               "scopes": scope,
                                                               "expires_in": self._EXPIRES_IN},
                                                              TokenGeneratorFactory.ACCESS_TOKEN)
        self._tokens_service.encrypt.assert_called_once_with(access_token, client_id)