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

        self._settings_facade = SettingsFacade()

        self.DEFAULT_CLIENT_ID = self._settings_facade.get(
            "oauth2_idp")["client_id"]
        self.TOKEN_VALIDITY = self._settings_facade.get(
            "access_token_validity")
Ejemplo n.º 2
0
    def test_routes_loaders_ok(self):
        '''This test case makes sure routes loaders are configured correctly for each configuration.'''

        self._settings_facade = SettingsFacade()

        self.assertTrue("fantastico.routing_engine.dummy_routeloader.DummyRouteLoader" in \
                        self._settings_facade.get("routes_loaders"))
        self.assertTrue("fantastico.mvc.controller_registrator.ControllerRouteLoader" in \
                        self._settings_facade.get("routes_loaders"))
Ejemplo n.º 3
0
    def init(self):
        '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user
        api.'''

        self._settings_facade = SettingsFacade()
        self.DEFAULT_CLIENT_ID = self._settings_facade.get(
            "oauth2_idp")["client_id"]
        self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID,
                                                    self.DEFAULT_USER_ID,
                                                    self.SCOPES)
Ejemplo n.º 4
0
    def _get_db_conn(self):
        '''This method opens a db connection and returns it to for usage.'''

        settings = SettingsFacade()
        db_config = settings.get("database_config")

        conn_manager = mvc.init_dm_db_engine(db_config)

        db_conn = conn_manager.get_connection(-50)

        return (conn_manager, -50, db_conn)
Ejemplo n.º 5
0
    def _get_db_conn(self):
        '''This method opens a db connection and returns it to for usage.'''

        settings = SettingsFacade()
        db_config = settings.get("database_config")

        conn_manager = mvc.init_dm_db_engine(db_config)

        db_conn = conn_manager.get_connection(-50)

        return (conn_manager, -50, db_conn)
Ejemplo n.º 6
0
    def test_settings_ok(self):
        '''Test case that ensures settings are functional for each environment available.'''

        self._settings_facade = SettingsFacade()

        self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware",
                          "fantastico.middleware.model_session_middleware.ModelSessionMiddleware",
                          "fantastico.middleware.routing_middleware.RoutingMiddleware",
                          "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware",
                          "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"],
                          self._settings_facade.get("installed_middleware"))

        self.assertEqual(["en_us"], self._settings_facade.get("supported_languages"))
Ejemplo n.º 7
0
class SettingsIntegration(FantasticoIntegrationTestCase):
    '''Test suite that ensures current settings for various environments remain stable.'''

    def init(self):
        self._settings_facade = None

    def test_settings_ok(self):
        '''Test case that ensures settings are functional for each environment available.'''

        self._settings_facade = SettingsFacade()

        self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware",
                          "fantastico.middleware.model_session_middleware.ModelSessionMiddleware",
                          "fantastico.middleware.routing_middleware.RoutingMiddleware",
                          "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware",
                          "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"],
                          self._settings_facade.get("installed_middleware"))

        self.assertEqual(["en_us"], self._settings_facade.get("supported_languages"))

    def test_routes_loaders_ok(self):
        '''This test case makes sure routes loaders are configured correctly for each configuration.'''

        self._settings_facade = SettingsFacade()

        self.assertTrue("fantastico.routing_engine.dummy_routeloader.DummyRouteLoader" in \
                        self._settings_facade.get("routes_loaders"))
        self.assertTrue("fantastico.mvc.controller_registrator.ControllerRouteLoader" in \
                        self._settings_facade.get("routes_loaders"))

    def test_database_config_ok(self):
        '''This test case ensures we have a database configured for fantastico framework.'''

        self._settings_facade = SettingsFacade()

        expected_config = {"drivername": "mysql+mysqlconnector",
                           "username": "******",
                           "password": "******",
                           "port": 3306,
                           "database": "fantastico"}

        config = self._settings_facade.get("database_config")

        self.assertEqual(expected_config["drivername"], config["drivername"])
        self.assertEqual(expected_config["username"], config["username"])
        self.assertEqual(expected_config["password"], config["password"])
        self.assertEqual(expected_config["port"], config["port"])
        self.assertEqual(expected_config["database"], config["database"])
        self.assertEqual("utf8", config["additional_params"]["charset"])
Ejemplo n.º 8
0
    def init(self):
        '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user
        api.'''

        self._settings_facade = SettingsFacade()
        self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"]
        self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES)
Ejemplo n.º 9
0
    def init(self):
        '''This method is invoked in order to set all common dependencies for test cases.'''

        self._settings_facade = SettingsFacade()

        self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"]
        self.TOKEN_VALIDITY = self._settings_facade.get("access_token_validity")
Ejemplo n.º 10
0
    def _build_context(self, request):
        '''Method used to build the context object starting for a request.'''

        client_langs = request.accept_language

        settings_facade = SettingsFacade(request.environ)
        supported_langs = settings_facade.get("supported_languages")

        if hasattr(client_langs, "_parsed"):
            client_langs = client_langs._parsed # pylint: disable=W0212
        else:
            client_langs = []

        context = RequestContext(settings_facade, self._get_supported_lang(supported_langs, client_langs))

        request.context = context
    def exec(self, os_lib=os):
        '''This method is executed to activate the given extension name.'''

        comp_name = self._arguments.name
        contrib_path = instantiator.get_package_abslocation(contrib)
        comp_path = "%s%s" % (contrib_path, comp_name)

        if not os_lib.path.exists(comp_path):
            raise FantasticoSdkCommandNotFoundError(
                "Extension %s does not exist." % comp_name)

        file_matcher = lambda abspath, filename: True
        root_folder = instantiator.get_component_path_data(
            SettingsFacade().get_config().__class__)[1]
        comp_root_folder = "%s%s/%s" % (root_folder, self._arguments.comp_root,
                                        comp_name)

        if not os_lib.path.exists(comp_root_folder):
            os_lib.mkdir(comp_root_folder)

        instantiator.scan_folder_by_criteria(
                            comp_path, file_matcher,
                            action=lambda abspath, filename: \
                                self._link_files(abspath, filename, contrib_path, root_folder, os_lib),
                            os_lib=os_lib)
    def _exec_command_help_scenario(self, argv, assert_action, cmd_name):
        '''This method defines a template for testing subcommands --help option. Once the subcommand is executed, the help
        string is captured and passed to assert action.

        .. code-block:: python

            class SampleSubcommandTest(CommandBaseIntegration):
                def test_sample_help(self):
                    def assert_action(help_str):
                        self.help_str.startswith("usage: %s" % SampleSubcommand.get_help())

                    self._exec_command_help_scenario(["fantastico", "sample"], "sample")'''

        if argv[-1] != "--help" or "-h":
            argv.append("--help")

        with self.assertRaises(SystemExit):
            main(argv)

        help_str = self._stdout.getvalue()

        assert_action(help_str)

        expected_doc_link = "See: %sfeatures/sdk/command_%s.html" % (
            SettingsFacade().get("doc_base"), cmd_name.replace("-", "_"))
        self.assertGreater(help_str.find(expected_doc_link), -1)
Ejemplo n.º 13
0
    def test_upload_file_ok(self):
        '''This test case ensure response is retrieved correctly from upload file.'''

        controller = RoutesForControllerTesting(SettingsFacade())
        response = controller.upload_file(Mock())

        self.assertIsNotNone(response)
        self.assertTrue(response.text.find("Hello world.") > -1)
Ejemplo n.º 14
0
    def register_resources(self, path):
        '''This method scans all files and folders from the given path, match the filenames against registered file patterns
        and import all ROA resources.'''

        instantiator.import_modules_from_folder(
            path,
            file_matcher=self._is_file_supported,
            settings_facade=SettingsFacade())
Ejemplo n.º 15
0
    def test_database_config_ok(self):
        '''This test case ensures we have a database configured for fantastico framework.'''

        self._settings_facade = SettingsFacade()

        expected_config = {"drivername": "mysql+mysqlconnector",
                           "username": "******",
                           "password": "******",
                           "port": 3306,
                           "database": "fantastico"}

        config = self._settings_facade.get("database_config")

        self.assertEqual(expected_config["drivername"], config["drivername"])
        self.assertEqual(expected_config["username"], config["username"])
        self.assertEqual(expected_config["password"], config["password"])
        self.assertEqual(expected_config["port"], config["port"])
        self.assertEqual(expected_config["database"], config["database"])
        self.assertEqual("utf8", config["additional_params"]["charset"])
Ejemplo n.º 16
0
    def setUp(self):
        self.__envs = [("fantastico.settings.BasicSettings", BasicSettings),
                       ("fantastico.settings.AwsStageSettings",
                        AwsStageSettings)]
        self.__old_middlewares_call = []

        self._save_call_methods(
            SettingsFacade().get_config().installed_middleware)

        super(FantasticoIntegrationTestCase, self).setUp()
Ejemplo n.º 17
0
    def setup_once(cls):
        '''This method is invoked once before executing any test case. For model facade we create a series of messages.'''

        cls.settings_facade = SettingsFacade()
        mvc.CONN_MANAGER = mvc.init_dm_db_engine(
            cls.settings_facade.get("database_config"), echo=True)

        cls.model_facade = ModelFacade(
            ModelFacadeMessage,
            mvc.CONN_MANAGER.get_connection(ModelFacadeIntegration.request_id))
Ejemplo n.º 18
0
class BaseModelIntegration(FantasticoIntegrationTestCase):
    '''This class provides the test cases that ensures sql alchemy configuration works as expected.'''
    def init(self):
        self._settings_facade = SettingsFacade()

    def test_connection_ok(self):
        '''This test case execute a simple sanity check against database configuration.'''

        db_config = self._settings_facade.get("database_config")

        from fantastico import mvc

        mvc.CONN_MANAGER = mvc.init_dm_db_engine(db_config)

        self.assertIsNotNone(mvc.BASEMODEL)
        self.assertIsNotNone(mvc.CONN_MANAGER)

        session = mvc.CONN_MANAGER.get_connection(uuid.uuid4())

        session.execute("SELECT 1")
Ejemplo n.º 19
0
class BaseModelIntegration(FantasticoIntegrationTestCase):
    '''This class provides the test cases that ensures sql alchemy configuration works as expected.'''
    
    def init(self):
        self._settings_facade = SettingsFacade()
    
    def test_connection_ok(self):
        '''This test case execute a simple sanity check against database configuration.'''
        
        db_config = self._settings_facade.get("database_config")
        
        from fantastico import mvc
        
        mvc.CONN_MANAGER = mvc.init_dm_db_engine(db_config)
        
        self.assertIsNotNone(mvc.BASEMODEL)
        self.assertIsNotNone(mvc.CONN_MANAGER)
        
        session = mvc.CONN_MANAGER.get_connection(uuid.uuid4())
        
        session.execute("SELECT 1")
Ejemplo n.º 20
0
    def test_context_initialization(self):
        '''This test case ensures context is correctly initialized.'''

        settings_cls = SettingsFacade().get_config().__class__

        self._middleware(self._environ, Mock())

        request = self._environ.get("fantastico.request")

        self.assertIsNotNone(request)
        self.assertIsNotNone(request.request_id)
        self.assertEqual("GET", request.method)
        self.assertEqual("application/json", request.content_type)
        self.assertEqual("123", request.headers.get("oauth_bearer"))
        self.assertEqual(1, int(request.params.get("id")))

        self.assertEqual(settings_cls().installed_middleware,
                         request.context.settings.get("installed_middleware"))
        self.assertEqual(settings_cls().supported_languages,
                         request.context.settings.get("supported_languages"))

        self.assertEqual("en_us", request.context.language.code)
Ejemplo n.º 21
0
 def init(self):
     self._settings_facade = SettingsFacade()
Ejemplo n.º 22
0
    def init(self):
        '''This method is responsible for initializing a new connection manager before each test case.'''

        mvc.CONN_MANAGER = mvc.init_dm_db_engine(
            db_config=SettingsFacade().get("database_config"), echo=True)
Ejemplo n.º 23
0
    def test_subroute_ok(self):
        '''This test case ensure method is correctly executed from SubroutesController.'''

        controller = SubroutesController(SettingsFacade())
        self.assertRaises(FantasticoTemplateNotFoundError, controller.handle_route, *[Mock()])
Ejemplo n.º 24
0
 def init(self):
     self._environ = {}
     self._settings = SettingsFacade(self._environ)
Ejemplo n.º 25
0
class TestSettingsFacadeSuite(FantasticoUnitTestsCase):
    '''Test suite for settings facade functionality.'''

    def init(self):
        self._environ = {}
        self._settings = SettingsFacade(self._environ)

    def test_get_config_noenv(self):
        '''Test case that checks a settings class can be obtained even if FANTASTICO_ACTIVE_CONFIG environment variable
        is not found.'''

        self.assertIsInstance(self._settings.get_config(), BasicSettings)

    def test_get_config_env_notfound(self):
        '''Test case that checks an exception is thrown when the specified class config can not be resolved.'''

        self._environ["FANTASTICO_ACTIVE_CONFIG"] = "not.found.class"

        self.assertRaises(FantasticoClassNotFoundError, self._settings.get_config)

    def test_get_config_env_found(self):
        '''Test case that checks that a custom configuration is instantiated correctly.'''

        self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings"

        self.assertIsInstance(self._settings.get_config(), SampleSettings)

    def test_get_setting(self):
        '''Test case that checks settings are correctly retrieved from the active configuration. In addition
        it ensures the configuration contains the minimum amount of information for installed_middleware.'''

        installed_middleware = self._settings.get("installed_middleware")

        self.assertGreaterEqual(len(installed_middleware), 3)
        self.assertEqual(["fantastico.middleware.request_middleware.RequestMiddleware",
                          "fantastico.middleware.model_session_middleware.ModelSessionMiddleware",
                          "fantastico.middleware.routing_middleware.RoutingMiddleware",
                          "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware",
                          "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"], installed_middleware)

    def test_get_setting_unavailable(self):
        '''Test case that ensures an exception is thrown whenever we try to get a setting that is not
        available.'''

        self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["setting_not_found"])

    def test_get_setting_internal_error(self):
        '''Test case that ensures properties internal server errors are handled gracefully.'''

        self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings"

        self.assertRaises(FantasticoSettingNotFoundError, self._settings.get, *["installed_middleware"])

    def test_get_root_folder(self):
        '''Test case that ensures get root folder works correctly.'''

        self._environ["FANTASTICO_ACTIVE_CONFIG"] = "fantastico.settings.BasicSettings"

        expected_root = self._get_root_folder()

        root_folder = self._settings.get_root_folder()

        self.assertEqual(expected_root, root_folder)
Ejemplo n.º 26
0
class OAuth2ControllerIntegrationTests(DevServerIntegration):
    '''This class provides the integration tests suite for ensuring oauth2 controller works as expected.'''

    DEFAULT_CLIENT_ID = None
    DEFAULT_USER_ID = 1
    DEFAULT_SCOPES = "user.profile.read user.profile.update user.profile.delete"
    TOKEN_VALIDITY = None

    _settings_facade = None

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

        self._settings_facade = SettingsFacade()

        self.DEFAULT_CLIENT_ID = self._settings_facade.get(
            "oauth2_idp")["client_id"]
        self.TOKEN_VALIDITY = self._settings_facade.get(
            "access_token_validity")

    def test_implicit_grant(self):
        '''This test case ensures implicit grant type success scenario works as expected.'''

        state = "abcd xyz&abc"
        redirect_uri = "/oauth/idp/ui/cb"
        login_token = self._get_oauth2_logintoken(self.DEFAULT_CLIENT_ID,
                                                  self.DEFAULT_USER_ID)
        endpoint = "/oauth/authorize?response_type=token&client_id=%s&login_token=%s&state=%s&scope=%s&redirect_uri=%s" % \
                    (self.DEFAULT_CLIENT_ID, login_token,
                     urllib.parse.quote(state),
                     urllib.parse.quote(self.DEFAULT_SCOPES),
                     urllib.parse.quote(redirect_uri))
        results = {}

        def trigger_implicit_flow(server):
            http_conn = http.client.HTTPConnection(host=server.hostname,
                                                   port=server.port)
            http_conn.request("GET", endpoint)

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_success(server):
            '''This method ensures the flow completed successfully by checking the location and hash part of the location.'''

            response = results["response"]

            self.assertIsNotNone(response)
            self.assertEqual(302, response.status)

            location = response.headers["Location"]

            self.assertIsNotNone(location)
            self.assertTrue(
                location.startswith("%s#access_token=" % redirect_uri))
            self.assertTrue(
                location.find("&expires_in=%s" % self.TOKEN_VALIDITY) > -1)
            self.assertTrue(
                location.find("&state=%s" % urllib.parse.quote(state)) > -1)
            self.assertTrue(
                location.find("&scope=%s" %
                              urllib.parse.quote(self.DEFAULT_SCOPES)) > -1)

            location = location.replace("%s#access_token=" % redirect_uri, "")
            access_token = location[:location.find("&")]

            self.assertGreater(len(access_token), 400)

        self._run_test_against_dev_server(trigger_implicit_flow,
                                          assert_success)

    def test_authorize_implicit_missing_param_form(self):
        '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed
        for implicit grant type.'''

        self._test_error_handling_implicit_graceful("form", "?")

    def test_authorize_implicit_missing_param_hash(self):
        '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed
        for implicit grant type (into hash section).'''

        self._test_error_handling_implicit_graceful("hash", "#")

    def _test_error_handling_implicit_graceful(self, format, delimiter):
        '''This method provides a template for ensuring error handling method types which describe error through redirect
        and query or hash strings work as expected.'''

        endpoint = "/oauth/authorize?error_format=%s&redirect_uri=/example/cb" % format

        results = {}

        def invoke_implicit(server):
            '''This method tries to retrieve login screen.'''

            http_conn = http.client.HTTPConnection(server.hostname,
                                                   server.port)

            http_conn.request("GET", endpoint)
            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_error_graceful(server):
            '''This method asserts that error was gracefully handled by OAuth2 exceptions middleware.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(302, response.status)

            location = response.headers.get("Location")
            self.assertIsNotNone(location)

            self.assertTrue(location.startswith("/example/cb%s" % delimiter))
            self.assertTrue(
                location.find("error=invalid_request") > -1,
                "%s does not contain error query param." % location)

            result = re.findall(r"error_description=(.*)&", location)
            self.assertEqual(
                1, len(result),
                "%s does not contain error_description query param." %
                location)

            result = re.findall(r"error_uri=(.*)", location)
            self.assertEqual(
                1, len(result),
                "%s does not contain error_uri query param." % location)

        self._run_test_against_dev_server(invoke_implicit,
                                          assert_error_graceful)
Ejemplo n.º 27
0
class UserResourceIntegrationTests(DevServerIntegration):
    '''This class provides the integration tests which ensures that CRUD operations work as expected on user resource. We do this
    because we also want to validate oauth2 roa validation.'''

    DEFAULT_CLIENT_ID = None
    DEFAULT_USER_ID = 1
    SCOPES = "user.profile.read user.profile.update user.profile.delete"

    _access_token = None
    _settings_facade = None

    def init(self):
        '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user
        api.'''

        self._settings_facade = SettingsFacade()
        self.DEFAULT_CLIENT_ID = self._settings_facade.get(
            "oauth2_idp")["client_id"]
        self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID,
                                                    self.DEFAULT_USER_ID,
                                                    self.SCOPES)

    def cleanup(self):
        self._invalidate_oauth2_token(self._access_token)
        self._access_token = None

    def _create_user(self, hostname, port):
        '''This method simply creates a new user using the api and returns the new user username.'''

        username = "******"

        endpoint = "/api/latest/oauth-idp-profile"

        user = {"username": username, "password": "******"}

        http_conn = http.client.HTTPConnection(hostname, port)

        http_conn.request("POST", endpoint, json.dumps(user))

        response = http_conn.getresponse()

        self.assertIsNotNone(response)
        self.assertEqual(201, response.status)

        location = response.headers.get("Location")
        self.assertIsNotNone(location)

        http_conn.close()

        return location.split("/")[-1]

    def _delete_user(self, user_id, token, hostname, port):
        '''This method removes the requested user_id.'''

        db_conn = None
        conn_manager = None
        request_id = None

        person_facade = None
        person = None

        try:
            conn_manager, request_id, db_conn = self._get_db_conn()

            person_facade = ModelFacade(Person, db_conn)
            user = ModelFacade(User,
                               db_conn).find_by_pk({User.user_id: user_id})
            person = person_facade.find_by_pk(
                {Person.person_id: user.person_id})

            endpoint = "/api/latest/oauth-idp-profile/%s?token=%s" % (user_id,
                                                                      token)

            http_conn = http.client.HTTPConnection(hostname, port)

            http_conn.request("DELETE", endpoint)

            response = http_conn.getresponse()

            http_conn.close()

            self.assertIsNotNone(response)
            self.assertEqual(204, response.status)
        finally:
            if person:
                person_facade.delete(person)

            if db_conn:
                conn_manager.close_connection(request_id)

    def test_retrieve_user_ok(self):
        '''This test case ensures default user used in tests can be retrieved correctly.'''

        results = {"user_id": None, "token": None}

        def get_new_user(server):
            '''This method triggers an http get on default user.'''

            results["user_id"] = user_id = int(
                self._create_user(server.hostname, server.port))
            results["token"] = access_token = self._get_oauth2_token(
                self.DEFAULT_CLIENT_ID, user_id, self.SCOPES)

            http_conn = http.client.HTTPConnection(host=server.hostname,
                                                   port=server.port)

            http_conn.request(
                "GET",
                "/api/latest/oauth-idp-profile/%s" % user_id,
                headers={"Authorization": "Bearer %s" % access_token})

            results["response"] = http_conn.getresponse()

            http_conn.close()

            self._delete_user(user_id, access_token, server.hostname,
                              server.port)

        def assert_user(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(200, response.status)

            body = response.read()
            self.assertIsNotNone(body)

            body = json.loads(body.decode())

            self.assertNotIn("password", body)
            self.assertEqual("*****@*****.**", body["username"])
            self.assertEqual(results.get("user_id"), body["user_id"])
            self.assertGreater(body["person_id"], 1)

        self._run_test_against_dev_server(get_new_user, assert_user)

    def test_retrieve_users_ok(self):
        '''This test case ensures users can be retrieved .'''

        results = {}

        def get_default_user(server):
            '''This method triggers an http get on default users collection endpoint.'''

            http_conn = http.client.HTTPConnection(host=server.hostname,
                                                   port=server.port)

            http_conn.request(
                "GET",
                "/api/latest/oauth-idp-profile?token=%s" % self._access_token)

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_user(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(200, response.status)

            body = response.read()
            self.assertIsNotNone(body)

            body = json.loads(body.decode())

            items = body.get("items")
            self.assertNotIn("password", items[0])
            self.assertEqual("*****@*****.**", items[0]["username"])
            self.assertEqual(1, items[0]["user_id"])
            self.assertEqual(1, items[0]["person_id"])

        self._run_test_against_dev_server(get_default_user, assert_user)

    def test_get_users_unauthorized(self):
        '''This method ensures users collection can not be accessed without an access token sent.'''

        results = {}

        def get_users(server):
            '''This method triggers an http get on default user.'''

            http_conn = http.client.HTTPConnection(host=server.hostname,
                                                   port=server.port)

            http_conn.request("GET", "/api/latest/oauth-idp-profile")

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_unauthorized(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(401, response.status)

        self._run_test_against_dev_server(get_users, assert_unauthorized)
Ejemplo n.º 28
0
class OAuth2ControllerIntegrationTests(DevServerIntegration):
    '''This class provides the integration tests suite for ensuring oauth2 controller works as expected.'''

    DEFAULT_CLIENT_ID = None
    DEFAULT_USER_ID = 1
    DEFAULT_SCOPES = "user.profile.read user.profile.update user.profile.delete"
    TOKEN_VALIDITY = None

    _settings_facade = None

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

        self._settings_facade = SettingsFacade()

        self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"]
        self.TOKEN_VALIDITY = self._settings_facade.get("access_token_validity")

    def test_implicit_grant(self):
        '''This test case ensures implicit grant type success scenario works as expected.'''

        state = "abcd xyz&abc"
        redirect_uri = "/oauth/idp/ui/cb"
        login_token = self._get_oauth2_logintoken(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID)
        endpoint = "/oauth/authorize?response_type=token&client_id=%s&login_token=%s&state=%s&scope=%s&redirect_uri=%s" % \
                    (self.DEFAULT_CLIENT_ID, login_token,
                     urllib.parse.quote(state),
                     urllib.parse.quote(self.DEFAULT_SCOPES),
                     urllib.parse.quote(redirect_uri))
        results = {}

        def trigger_implicit_flow(server):
            http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port)
            http_conn.request("GET", endpoint)

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_success(server):
            '''This method ensures the flow completed successfully by checking the location and hash part of the location.'''

            response = results["response"]

            self.assertIsNotNone(response)
            self.assertEqual(302, response.status)

            location = response.headers["Location"]

            self.assertIsNotNone(location)
            self.assertTrue(location.startswith("%s#access_token=" % redirect_uri))
            self.assertTrue(location.find("&expires_in=%s" % self.TOKEN_VALIDITY) > -1)
            self.assertTrue(location.find("&state=%s" % urllib.parse.quote(state)) > -1)
            self.assertTrue(location.find("&scope=%s" % urllib.parse.quote(self.DEFAULT_SCOPES)) > -1)

            location = location.replace("%s#access_token=" % redirect_uri, "")
            access_token = location[:location.find("&")]

            self.assertGreater(len(access_token), 400)

        self._run_test_against_dev_server(trigger_implicit_flow, assert_success)

    def test_authorize_implicit_missing_param_form(self):
        '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed
        for implicit grant type.'''

        self._test_error_handling_implicit_graceful("form", "?")

    def test_authorize_implicit_missing_param_hash(self):
        '''This test case ensures error, error_uri and error_description query parameters are appended to redirect_uri passed
        for implicit grant type (into hash section).'''

        self._test_error_handling_implicit_graceful("hash", "#")

    def _test_error_handling_implicit_graceful(self, format, delimiter):
        '''This method provides a template for ensuring error handling method types which describe error through redirect
        and query or hash strings work as expected.'''

        endpoint = "/oauth/authorize?error_format=%s&redirect_uri=/example/cb" % format

        results = {}

        def invoke_implicit(server):
            '''This method tries to retrieve login screen.'''

            http_conn = http.client.HTTPConnection(server.hostname, server.port)

            http_conn.request("GET", endpoint)
            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_error_graceful(server):
            '''This method asserts that error was gracefully handled by OAuth2 exceptions middleware.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(302, response.status)

            location = response.headers.get("Location")
            self.assertIsNotNone(location)

            self.assertTrue(location.startswith("/example/cb%s" % delimiter))
            self.assertTrue(location.find("error=invalid_request") > -1, "%s does not contain error query param." % location)

            result = re.findall(r"error_description=(.*)&", location)
            self.assertEqual(1, len(result), "%s does not contain error_description query param." % location)

            result = re.findall(r"error_uri=(.*)", location)
            self.assertEqual(1, len(result), "%s does not contain error_uri query param." % location)


        self._run_test_against_dev_server(invoke_implicit, assert_error_graceful)
Ejemplo n.º 29
0
 def init(self):
     self._settings_facade = SettingsFacade()
Ejemplo n.º 30
0
class UserResourceIntegrationTests(DevServerIntegration):
    '''This class provides the integration tests which ensures that CRUD operations work as expected on user resource. We do this
    because we also want to validate oauth2 roa validation.'''

    DEFAULT_CLIENT_ID = None
    DEFAULT_USER_ID = 1
    SCOPES = "user.profile.read user.profile.update user.profile.delete"

    _access_token = None
    _settings_facade = None

    def init(self):
        '''This method is invoked before executing each test case. It generates an access_token which is authorized to access user
        api.'''

        self._settings_facade = SettingsFacade()
        self.DEFAULT_CLIENT_ID = self._settings_facade.get("oauth2_idp")["client_id"]
        self._access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, self.DEFAULT_USER_ID, self.SCOPES)

    def cleanup(self):
        self._invalidate_oauth2_token(self._access_token)
        self._access_token = None

    def _create_user(self, hostname, port):
        '''This method simply creates a new user using the api and returns the new user username.'''

        username = "******"

        endpoint = "/api/latest/oauth-idp-profile"

        user = {"username": username, "password": "******"}

        http_conn = http.client.HTTPConnection(hostname, port)

        http_conn.request("POST", endpoint, json.dumps(user))

        response = http_conn.getresponse()

        self.assertIsNotNone(response)
        self.assertEqual(201, response.status)

        location = response.headers.get("Location")
        self.assertIsNotNone(location)

        http_conn.close()

        return location.split("/")[-1]

    def _delete_user(self, user_id, token, hostname, port):
        '''This method removes the requested user_id.'''

        db_conn = None
        conn_manager = None
        request_id = None

        person_facade = None
        person = None

        try:
            conn_manager, request_id, db_conn = self._get_db_conn()

            person_facade = ModelFacade(Person, db_conn)
            user = ModelFacade(User, db_conn).find_by_pk({User.user_id: user_id})
            person = person_facade.find_by_pk({Person.person_id: user.person_id})

            endpoint = "/api/latest/oauth-idp-profile/%s?token=%s" % (user_id, token)

            http_conn = http.client.HTTPConnection(hostname, port)

            http_conn.request("DELETE", endpoint)

            response = http_conn.getresponse()

            http_conn.close()

            self.assertIsNotNone(response)
            self.assertEqual(204, response.status)
        finally:
            if person:
                person_facade.delete(person)

            if db_conn:
                conn_manager.close_connection(request_id)

    def test_retrieve_user_ok(self):
        '''This test case ensures default user used in tests can be retrieved correctly.'''

        results = {"user_id": None, "token": None}

        def get_new_user(server):
            '''This method triggers an http get on default user.'''

            results["user_id"] = user_id = int(self._create_user(server.hostname, server.port))
            results["token"] = access_token = self._get_oauth2_token(self.DEFAULT_CLIENT_ID, user_id, self.SCOPES)

            http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port)

            http_conn.request("GET", "/api/latest/oauth-idp-profile/%s" % user_id,
                              headers={"Authorization": "Bearer %s" % access_token})

            results["response"] = http_conn.getresponse()

            http_conn.close()

            self._delete_user(user_id, access_token, server.hostname, server.port)

        def assert_user(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(200, response.status)

            body = response.read()
            self.assertIsNotNone(body)

            body = json.loads(body.decode())

            self.assertNotIn("password", body)
            self.assertEqual("*****@*****.**", body["username"])
            self.assertEqual(results.get("user_id"), body["user_id"])
            self.assertGreater(body["person_id"], 1)

        self._run_test_against_dev_server(get_new_user, assert_user)

    def test_retrieve_users_ok(self):
        '''This test case ensures users can be retrieved .'''

        results = {}

        def get_default_user(server):
            '''This method triggers an http get on default users collection endpoint.'''

            http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port)

            http_conn.request("GET", "/api/latest/oauth-idp-profile?token=%s" % self._access_token)

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_user(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(200, response.status)

            body = response.read()
            self.assertIsNotNone(body)

            body = json.loads(body.decode())

            items = body.get("items")
            self.assertNotIn("password", items[0])
            self.assertEqual("*****@*****.**", items[0]["username"])
            self.assertEqual(1, items[0]["user_id"])
            self.assertEqual(1, items[0]["person_id"])

        self._run_test_against_dev_server(get_default_user, assert_user)

    def test_get_users_unauthorized(self):
        '''This method ensures users collection can not be accessed without an access token sent.'''

        results = {}

        def get_users(server):
            '''This method triggers an http get on default user.'''

            http_conn = http.client.HTTPConnection(host=server.hostname, port=server.port)

            http_conn.request("GET", "/api/latest/oauth-idp-profile")

            results["response"] = http_conn.getresponse()

            http_conn.close()

        def assert_unauthorized(server):
            '''This method assert the user response against expected results.'''

            response = results.get("response")

            self.assertIsNotNone(response)
            self.assertEqual(401, response.status)

        self._run_test_against_dev_server(get_users, assert_unauthorized)
Ejemplo n.º 31
0
class TestSettingsFacadeSuite(FantasticoUnitTestsCase):
    '''Test suite for settings facade functionality.'''
    def init(self):
        self._environ = {}
        self._settings = SettingsFacade(self._environ)

    def test_get_config_noenv(self):
        '''Test case that checks a settings class can be obtained even if FANTASTICO_ACTIVE_CONFIG environment variable
        is not found.'''

        self.assertIsInstance(self._settings.get_config(), BasicSettings)

    def test_get_config_env_notfound(self):
        '''Test case that checks an exception is thrown when the specified class config can not be resolved.'''

        self._environ["FANTASTICO_ACTIVE_CONFIG"] = "not.found.class"

        self.assertRaises(FantasticoClassNotFoundError,
                          self._settings.get_config)

    def test_get_config_env_found(self):
        '''Test case that checks that a custom configuration is instantiated correctly.'''

        self._environ[
            "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings"

        self.assertIsInstance(self._settings.get_config(), SampleSettings)

    def test_get_setting(self):
        '''Test case that checks settings are correctly retrieved from the active configuration. In addition
        it ensures the configuration contains the minimum amount of information for installed_middleware.'''

        installed_middleware = self._settings.get("installed_middleware")

        self.assertGreaterEqual(len(installed_middleware), 3)
        self.assertEqual([
            "fantastico.middleware.request_middleware.RequestMiddleware",
            "fantastico.middleware.model_session_middleware.ModelSessionMiddleware",
            "fantastico.middleware.routing_middleware.RoutingMiddleware",
            "fantastico.oauth2.middleware.exceptions_middleware.OAuth2ExceptionsMiddleware",
            "fantastico.oauth2.middleware.tokens_middleware.OAuth2TokensMiddleware"
        ], installed_middleware)

    def test_get_setting_unavailable(self):
        '''Test case that ensures an exception is thrown whenever we try to get a setting that is not
        available.'''

        self.assertRaises(FantasticoSettingNotFoundError, self._settings.get,
                          *["setting_not_found"])

    def test_get_setting_internal_error(self):
        '''Test case that ensures properties internal server errors are handled gracefully.'''

        self._environ[
            "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.tests.test_settings.SampleSettings"

        self.assertRaises(FantasticoSettingNotFoundError, self._settings.get,
                          *["installed_middleware"])

    def test_get_root_folder(self):
        '''Test case that ensures get root folder works correctly.'''

        self._environ[
            "FANTASTICO_ACTIVE_CONFIG"] = "fantastico.settings.BasicSettings"

        expected_root = self._get_root_folder()

        root_folder = self._settings.get_root_folder()

        self.assertEqual(expected_root, root_folder)
Ejemplo n.º 32
0
 def init(self):
     self._environ = {}
     self._settings = SettingsFacade(self._environ)
Ejemplo n.º 33
0
class RoaController(BaseController):
    '''This class provides dynamic routes for ROA registered resources. All CRUD operations are supported out of the box. In
    addition error handling is automatically provided by this controller.'''

    SETTINGS_FACADE = SettingsFacade()
    ROA_API = roa_helper.normalize_absolute_roa_uri(
        SETTINGS_FACADE.get("roa_api"))
    BASE_URL = r"%s/(?P<version>\d{1,}\.\d{1,})(?P<resource_url>/[^/]*?)" % ROA_API
    BASE_LATEST_URL = "%s/latest(?P<resource_url>/[^/]*?)" % ROA_API

    OFFSET_DEFAULT = 0
    LIMIT_DEFAULT = 100

    def __init__(self,
                 settings_facade,
                 resources_registry_cls=ResourcesRegistry,
                 model_facade_cls=ModelFacade,
                 conn_manager=mvc,
                 json_serializer_cls=ResourceJsonSerializer,
                 query_parser_cls=QueryParser):
        super(RoaController, self).__init__(settings_facade)

        self._resources_registry = resources_registry_cls()
        self._model_facade_cls = model_facade_cls
        self._conn_manager = conn_manager
        self._json_serializer_cls = json_serializer_cls
        self._query_parser_cls = query_parser_cls

        doc_base = "%sfeatures/roa/errors/" % self._settings_facade.get(
            "doc_base")
        self._errors_url = doc_base + "error_%s.html"
        self._roa_api = self._settings_facade.get("roa_api")

    def _parse_filter(self, filter_expr, model):
        '''This method parse a string filter expression and builds a compatible ModelFilter.'''

        if not filter_expr:
            return None

        query_parser = self._query_parser_cls()

        return query_parser.parse_filter(filter_expr, model)

    def _parse_sort(self, sort_expr, model):
        '''This method parse a string sort expression and builds a compatible ModelSort.'''

        if not sort_expr:
            return None

        query_parser = self._query_parser_cls()

        return query_parser.parse_sort(sort_expr, model)

    def _build_error_response(self, http_code, error_code, error_description,
                              error_details):
        '''This method builds an error response compliant with :doc:`/features/roa/rest_responses` specification.'''

        error = {
            "error_code": error_code,
            "error_description": error_description,
            "error_details": error_details
        }

        response = Response(text=json.dumps(error),
                            status_code=http_code,
                            content_type="application/json")
        self._add_cors_headers(response)

        return response

    def _handle_resource_notfound(self, version, url):
        '''This method build a resource not found response which is sent to the client. You can find more information about error
        responses format on :doc:`/features/roa/rest_responses`'''

        error_code = 10000

        return self._build_error_response(
            http_code=404,
            error_code=error_code,
            error_description="Resource %s version %s does not exist." %
            (url, version),
            error_details=self._errors_url % error_code)

    def _handle_resource_item_notfound(self, version, url, resource_id):
        '''This method build a resource not found response which is sent to the client. You can find more information about error
        responses format on :doc:`/features/roa/rest_responses`. In the error description it also contains resource id.'''

        error_code = 10040

        return self._build_error_response(http_code=404,
                                          error_code=error_code,
                                          error_description="Resource %s version %s id %s does not exist." % \
                                            (url, version, resource_id),
                                          error_details=self._errors_url % error_code)

    def _handle_resource_invalid(self, version, url, ex):
        '''This method builds a resource invalid response which is sent to the client.'''

        error_code = 10010

        return self._build_error_response(http_code=ex.http_code,
                                          error_code=error_code,
                                          error_description="Resource %s version %s is invalid: %s" % \
                                                    (url, version, str(ex)),
                                          error_details=self._errors_url % error_code)

    def _handle_resource_nobody(self, version, url):
        '''This method builds a resource nobody given response which is sent to the client.'''

        error_code = 10020

        return self._build_error_response(http_code=400,
                                          error_code=error_code,
                                          error_description="Resource %s version %s can not be created: no body given." % \
                                                    (url, version),
                                          error_details=self._errors_url % error_code)

    def _handle_resource_dberror(self, version, url, dbex):
        '''This method builds a resource dberror response which is sent to the client.'''

        error_code = 10030

        return self._build_error_response(http_code=400,
                                          error_code=error_code,
                                          error_description="Resource %s version %s db exception: %s." % \
                                                    (url, version, str(dbex)),
                                          error_details=self._errors_url % error_code)

    def _get_current_connection(self, request):
        '''This method returns the current db connection for this request.'''

        return self._conn_manager.CONN_MANAGER.get_connection(
            request.request_id)

    def _trim_resource_url(self, resource_url):
        '''This method removes traling slash from resource url.'''

        if resource_url[-1] == "/":
            resource_url = resource_url[:-1]

        if resource_url[0] != "/":
            resource_url = "/%s" % resource_url

        return resource_url

    def _add_cors_headers(self, response):
        '''This method add cors headers to a given respones.'''

        response.headers["Access-Control-Allow-Origin"] = "*"
        response.headers[
            "Access-Control-Allow-Methods"] = "OPTIONS,GET,POST,PUT,DELETE"

    @Controller(url=BASE_URL + "$", method="GET")
    def get_collection(self, request, version, resource_url):
        '''This method provides the route for accessing a resource collection. :doc:`/features/roa/rest_standard` for collections
        are enabled by this method. The typical response format is presented below:

        .. code-block:: javascript

            var response = {"items": [
                                // resources represented as json objects.
                            ],
                            "totalItems": 100}

        If a resource is not found or the resource version does not exist the following response is returned:

        .. code-block:: javascript

            {"error_code": 10000,
             "error_description": "Resource %s version %s does not exist.",
             "error_details": "http://rcosnita.github.io/fantastico/html/features/roa/errors/error_10000.html"}
        '''

        if version != "latest":
            version = float(version)

        params = CollectionParams(request, RoaController.OFFSET_DEFAULT,
                                  RoaController.LIMIT_DEFAULT)

        resource = self._resources_registry.find_by_url(resource_url, version)

        if not resource:
            return self._handle_resource_notfound(version, resource_url)

        self._inject_security_context(request, resource.model)
        access_token = self.validate_security_context(request, "read")

        json_serializer = self._json_serializer_cls(resource)

        filter_expr = self._parse_filter(params.filter_expr, resource.model)

        if resource.user_dependent:
            if filter_expr:
                filter_expr = ModelFilterAnd(
                    filter_expr,
                    ModelFilter(resource.model.user_id, access_token.user_id,
                                ModelFilter.EQ))
            else:
                filter_expr = ModelFilter(resource.model.user_id,
                                          access_token.user_id, ModelFilter.EQ)

        sort_expr = self._parse_sort(params.order_expr, resource.model)

        model_facade = self._model_facade_cls(
            resource.model, self._get_current_connection(request))

        models = model_facade.get_records_paged(start_record=params.offset,
                                                end_record=params.offset +
                                                params.limit,
                                                filter_expr=filter_expr,
                                                sort_expr=sort_expr)
        items = [
            json_serializer.serialize(model, params.fields) for model in models
        ]

        if resource.validator:
            resource.validator().format_collection(items, request)

        models_count = model_facade.count_records(filter_expr=filter_expr)

        body = {"items": items, "totalItems": models_count}

        response = Response(text=json.dumps(body),
                            content_type="application/json",
                            status_code=200)

        self._add_cors_headers(response)

        return response

    @Controller(url=BASE_LATEST_URL + "$", method="GET")
    def get_collection_latest(self, request, resource_url):
        '''This method retrieves a resource collection using the latest version of the api.'''

        return self.get_collection(request, "latest",
                                   self._trim_resource_url(resource_url))

    @Controller(url=[BASE_URL + "$", BASE_URL + "/(?P<resource_id>.*?)$"],
                method="OPTIONS")  # pylint: disable=W0613
    @CorsEnabled()
    def handle_resource_options(self, request, version, resource_url,
                                **kwargs):
        '''This method enables support for http ajax CORS requests. This is mandatory if we want to host apis on different
        domains than project host.'''

        pass

    @Controller(url=[
        BASE_LATEST_URL + "$", BASE_LATEST_URL + "/(?P<resource_id>.*?)$"
    ],
                method="OPTIONS")
    def handle_resource_options_latest(self, request, resource_url, **kwargs):
        '''This method handles OPTIONS http requests for ROA api latest versions.'''

        return self.handle_resource_options(
            request, "latest", self._trim_resource_url(resource_url), **kwargs)

    def _validate_resource(self,
                           resource,
                           request,
                           request_body,
                           existing_resource_id=None):
        '''This method is used to validate the resource. If the resource validation fails an error response is sent. Otherwise
        the newly validated model is returned.'''

        if not request_body:
            return self._handle_resource_nobody(resource.version, resource.url)

        model = self._json_serializer_cls(resource).deserialize(
            request_body.decode())

        action = "create"
        if existing_resource_id:
            action = "update"

        self._inject_security_context(request, model)
        self.validate_security_context(request, action)

        if not resource.validator:
            return model

        try:
            resource.validator().validate(model, request, existing_resource_id)
        except FantasticoRoaError as ex:
            return self._handle_resource_invalid(resource.version,
                                                 resource.url, ex)

        return model

    @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="GET")
    def get_item(self, request, version, resource_url, resource_id):
        '''This method provides the API for retrieving a single item from a collection. The item is uniquely identified by
        resource_id. Below you can find a success response example:

        .. code-block:: html

            GET - /api/1.0/simple-resources/1 HTTP/1.1

            200 OK
            Content-Type: application/json
            Content-Length: ...

            {
                "id": 1,
                "name": "Test resource",
                "description": "Simple description"
            }

        Of course there are cases when exceptions might occur. Below, you can find a list of error response retrieved from
        get_item API:

            * **10000** - Whenever we try to retrieve a resource with unknown type. (Not registered to ROA).
            * **10030** - Whenever we try to retrieve a resource and an unexpected database exception occurs.
            * **10040** - Whenever we try to retrieve a resource which does not exist.
        '''

        if version != "latest":
            version = float(version)

        fields = request.params.get("fields")

        resource = self._resources_registry.find_by_url(resource_url, version)

        if not resource:
            return self._handle_resource_notfound(version, resource_url)

        self._inject_security_context(request, resource.model)
        access_token = self.validate_security_context(request, "read")

        model_facade = self._model_facade_cls(
            resource.model, self._get_current_connection(request))

        try:
            model = model_facade.find_by_pk(
                {model_facade.model_pk_cols[0]: resource_id})

            if not self._is_model_owned_by(model, access_token, resource):
                model = None
        except FantasticoDbError as dbex:
            return self._handle_resource_dberror(version, resource_url, dbex)

        if not model:
            return self._handle_resource_item_notfound(version, resource_url,
                                                       resource_id)

        json_serializer = self._json_serializer_cls(resource)

        resource_body = json_serializer.serialize(model, fields)

        if resource.validator and model:
            resource.validator().format_resource(
                DictionaryObject(resource_body, immutable=False), request)

        resource_body = json.dumps(resource_body)

        response = Response(body=resource_body.encode(),
                            content_type="application/json",
                            status_code=200)
        self._add_cors_headers(response)

        return response

    @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$", method="GET")
    def get_item_latest(self, request, resource_url, resource_id):
        '''This method provides the latest get_item route for ROA api.'''

        return self.get_item(request, "latest",
                             self._trim_resource_url(resource_url),
                             resource_id)

    @Controller(url=BASE_URL + "$", method="POST")
    def create_item(self, request, version, resource_url):
        '''This method provides the route for adding new resources into an existing collection. The API is json only and invoke
        the validator as described in ROA spec. Usually, when a resource is created successfully a similar answer is returned to
        the client:

        .. code-block:: html

            201 Created
            Content-Type: application/json
            Content-Length: 0
            Location: /api/2.0/app-settings/123

        Below you can find all error response codes which might be returned when creating a new resource:

            * **10000** - Whenever we try to create a resource with unknown type. (Not registered to ROA).
            * **10010** - Whenever we try to create a resource which fails validation.
            * **10020** - Whenever we try to create a resource without passing a valid body.
            * **10030** - Whenever we try to create a resource and an unexpected database exception occurs.

        You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.'''

        if version != "latest":
            version = float(version)

        resource = self._resources_registry.find_by_url(resource_url, version)

        if not resource:
            return self._handle_resource_notfound(version, resource_url)

        model = self._validate_resource(resource, request, request.body)
        access_token = request.context.security.access_token

        if isinstance(model, Response):
            return model

        try:
            if resource.user_dependent and access_token:
                model.user_id = access_token.user_id

            if resource.validator:
                resource.validator().on_pre_create(model, request)

            model_facade = self._model_facade_cls(
                resource.model, self._get_current_connection(request))
            model_id = model_facade.create(model)[0]

            if resource.validator:
                resource.validator().on_post_create(model, request)
        except FantasticoDbError as dbex:
            return self._handle_resource_dberror(resource.version,
                                                 resource.url, dbex)

        model_location = roa_helper.calculate_resource_url(
            self._roa_api, resource, version)
        model_location += "/%s" % model_id

        response = Response(status_code=201, content_type="application/json")
        self._add_cors_headers(response)
        response.headers["Location"] = model_location

        return response

    @Controller(url=BASE_LATEST_URL + "$", method="POST")
    def create_item_latest(self, request, resource_url):
        '''This method provides create item latest API version.'''

        return self.create_item(request, "latest",
                                self._trim_resource_url(resource_url))

    @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="PUT")
    def update_item(self, request, version, resource_url, resource_id):
        '''This method provides the route for updating existing resources from an existing collection.
        The API is json only and invokes the validator as described in ROA spec.
        Usually, when a resource is update successfully a similar answer is returned to the client:

        .. code-block:: html

            204 No Content
            Content-Type: application/json
            Content-Length: 0

        Below you can find all error response codes which might be returned when creating a new resource:

            * **10000** - Whenever we try to update a resource with unknown type. (Not registered to ROA).
            * **10010** - Whenever we try to update a resource which fails validation.
            * **10020** - Whenever we try to update a resource without passing a valid body.
            * **10030** - Whenever we try to update a resource and an unexpected database exception occurs.
            * **10040** - Whenever we try to update a resource which does not exist.

        You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.'''

        if version != "latest":
            version = float(version)

        resource = self._resources_registry.find_by_url(resource_url, version)

        if not resource:
            return self._handle_resource_notfound(version, resource_url)

        model = self._validate_resource(resource, request, request.body,
                                        resource_id)

        if isinstance(model, Response):
            return model

        access_token = request.context.security.access_token

        model_facade = self._model_facade_cls(
            resource.model, self._get_current_connection(request))
        pk_col = model_facade.model_pk_cols[0]

        try:
            existing_model = model_facade.find_by_pk({pk_col: resource_id})

            if not existing_model or not self._is_model_owned_by(
                    existing_model, access_token, resource):
                return self._handle_resource_item_notfound(
                    version, resource_url, resource_id)

            setattr(model, pk_col.name, resource_id)

            if resource.validator:
                resource.validator().on_pre_update(model, request)

            model_facade.update(model)

            if resource.validator:
                resource.validator().on_post_update(model, request)
        except FantasticoDbError as dbex:
            return self._handle_resource_dberror(resource.version,
                                                 resource.url, dbex)

        response = Response(content_type="application/json", status_code=204)
        self._add_cors_headers(response)

        return response

    @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$", method="PUT")
    def update_item_latest(self, request, resource_url, resource_id):
        '''This is the route handler for latest update existing item api.'''

        return self.update_item(request, "latest",
                                self._trim_resource_url(resource_url),
                                resource_id)

    @Controller(url=BASE_URL + "/(?P<resource_id>.*?)$", method="DELETE")
    def delete_item(self, request, version, resource_url, resource_id):
        '''This method provides the route for deleting existing resources from an existing collection.
        The API is json only. Usually, when a resource is deleted successfully a similar answer is returned to the client:

        .. code-block:: html

            204 No Content
            Content-Type: application/json
            Content-Length: 0

        Below you can find all error response codes which might be returned when creating a new resource:

            * **10000** - Whenever we try to delete a resource with unknown type. (Not registered to ROA).
            * **10030** - Whenever we try to delete a resource and an unexpected database exception occurs.
            * **10040** - Whenever we try to delete a resource which does not exist.

        You can find more information about typical REST ROA APIs response on :doc:`/features/roa/rest_responses`.'''

        if version != "latest":
            version = float(version)

        resource = self._resources_registry.find_by_url(resource_url, version)

        if not resource:
            return self._handle_resource_notfound(version, resource_url)

        self._inject_security_context(request, resource.model)
        access_token = self.validate_security_context(request, "delete")

        try:
            model_facade = self._model_facade_cls(
                resource.model, self._get_current_connection(request))
            pk_col = model_facade.model_pk_cols[0]

            existing_model = model_facade.find_by_pk({pk_col: resource_id})

            if not self._is_model_owned_by(existing_model, access_token,
                                           resource):
                existing_model = None

            if not existing_model:
                return self._handle_resource_item_notfound(
                    version, resource_url, resource_id)

            if resource.validator:
                resource.validator().on_pre_delete(existing_model, request)

            model_facade.delete(existing_model)

            if resource.validator:
                resource.validator().on_post_delete(existing_model, request)
        except FantasticoDbError as dbex:
            return self._handle_resource_dberror(version, resource_url, dbex)

        response = Response(content_type="application/json", status_code=204)
        self._add_cors_headers(response)

        return response

    @Controller(url=BASE_LATEST_URL + "/(?P<resource_id>.*?)$",
                method="DELETE")
    def delete_item_latest(self, request, resource_url, resource_id):
        '''This method provides the functionality for delete item latest version api route.'''

        return self.delete_item(request, "latest",
                                self._trim_resource_url(resource_url),
                                resource_id)

    def validate_security_context(self, request, attr_scope):
        '''This method triggers security context validation and converts unexpected exceptions to OAuth2UnauthorizedError.
        If everything is fine this method return the access_token from security context.'''

        security_ctx = request.context.security

        if not attr_scope:
            return

        try:
            if not security_ctx.validate_context(attr_scope):
                raise OAuth2UnauthorizedError(
                    "Security context insufficient scopes.")
        except OAuth2Error:
            raise
        except Exception as ex:
            raise OAuth2UnauthorizedError(
                "Security context validation failed: %s" % str(ex))

        return security_ctx.access_token

    def _inject_security_context(self, request, resource_model):
        '''This method enrich the current security context with required scopes (if necessary).'''

        if not hasattr(resource_model, "get_required_scopes"):
            return

        required_scopes_obj = resource_model.get_required_scopes()

        required_scopes_obj.inject_scopes_in_security(request)

    def _is_model_owned_by(self, model, access_token, resource):
        '''This method is used to detect if a given model can be accessed using the access token. If the resource is not user
        dependent this method always returns true.'''

        if resource.user_dependent:
            if model.user_id == access_token.user_id:
                return True

            return False

        return True